1. 程式人生 > >WPF 使用滑鼠拖動一個控制元件的實現[2018.7.15]

WPF 使用滑鼠拖動一個控制元件的實現[2018.7.15]

原文: WPF 使用滑鼠拖動一個控制元件的實現[2018.7.15]

Q:已經把一個Shape和一個TextBlock組合起來放到了一個Grid中,現在想要實現用滑鼠拖動這個Grid到任意位置的功能,如何做?

<Grid Height="50" Width="50">
    <Ellipse Fill="Yellow" Stroke="Blue" Height="50" Width="50" HorizontalAlignment="Left"></Ellipse>
    <TextBlock Text="5" TextAlignment="Center" VerticalAlignment="Center" HorizontalAlignment="Center"></TextBlock>
</Grid>

A:在stackoverflow上找到解決方法。首先,為這個Grid新增三個滑鼠事件

<Grid Height="50" Width="50" MouseLeftButtonDown="grid_MouseLeftButtonDown" MouseLeftButtonUp="
    grid_MouseLeftButtonUp" MouseMove="grid_MouseMove">
    <Ellipse Fill="Yellow" Stroke="Blue" Height="50" Width="50" HorizontalAlignment="Left"></Ellipse>
    <TextBlock Text="5" TextAlignment="Center" VerticalAlignment="Center" HorizontalAlignment="Center"></TextBlock>
</Grid>

 

在cs檔案中為這三個事件新增實現:

private bool isDragging;
private Point clickPosition;

private void grid_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
    isDragging = true;
    var draggableElement = sender as UIElement;
    clickPosition = e.GetPosition(this);
    draggableElement.CaptureMouse();
}

private void grid_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
    isDragging = false;
    var draggableElement = sender as UIElement;
    draggableElement.ReleaseMouseCapture();
}

private void grid_MouseMove(object sender, MouseEventArgs e)
{
    var draggableElement = sender as UIElement;
    if (isDragging && draggableElement != null)
    {
        Point currentPosition = e.GetPosition(this.Parent as UIElement);
        var transform = draggableElement.RenderTransform as TranslateTransform;
        if (transform == null)
        {
            transform = new TranslateTransform();
            draggableElement.RenderTransform = transform;
        }
        transform.X = currentPosition.X - clickPosition.X;
        transform.Y = currentPosition.Y - clickPosition.Y;
     }
} 

 

      每個控制元件有一個RenderTransform屬性,它接收一個物件。TranslateTransform是RenderTransform的一個子類,它的例項可以賦給控制元件的RenderTransform屬性,表示以當前控制元件為原點進行的平移操作。

    【---2018.7.17新增---】 

     上面這段c#程式碼有問題。當拖動控制元件移動後,鬆開滑鼠,如果再次想要拖動該控制元件,在滑鼠剛按下時控制元件會回到原點。

      出現這個問題是因為在MouseLeftButtonDown事件發生後,立即發生MouseMove事件,currentPosition和clickPosition相等,導致transform的X和Y屬性都為0。從而grid的RenderTransform屬性指示將控制元件重新繪製在grid的起始位置。

      對於RenderTransform屬性來說,原點是多少?應該是grid控制元件物件被建立時的座標。RenderTransform指示的移動都是以這個座標軸為參考的。

       解決這個問題的方法是,在grid_MouseMove()中,每次移動的時候,要加上上一次拖拽結束時控制元件的相對座標。

       首先為這個grid新增一個名字,並使用RenderTransform屬性

<Grid Height="50" Width="50" Name="myGrid" MouseLeftButtonDown="grid_MouseLeftButtonDown" MouseLeftButtonUp="
    grid_MouseLeftButtonUp" MouseMove="grid_MouseMove">
    <Ellipse Fill="Yellow" Stroke="Blue" Height="50" Width="50" HorizontalAlignment="Left"></Ellipse>
    <TextBlock Text="5" TextAlignment="Center" VerticalAlignment="Center" HorizontalAlignment="Center"></TextBlock>
    <Grid.RenderTransform>
        <TranslateTransform x:Name="tt" />
    </RenderTransform>
</Grid>

    然後,將cs程式碼中的clickPosition取消,換一個名字,比如叫startPosition,用來記錄該控制元件在任一時刻離自己的原點的相對座標。在grid_MouseLeftButtonDown方法中,不記錄滑鼠點選的位置座標,而是記錄滑鼠被按下時這個位置離控制元件原點的距離,即剛剛定義的startPosition。

      在grid_MouseMove方法中,由於grid的RenderTransform早有定義,所以不用進行是否null的判斷。之後transform變數的值,應該為當前座標currentPosition與參考點startPosition之差。

private bool isDragging;
private Point startPosition;

private void grid_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
    isDragging = true;
    var draggableElement = sender as UIElement;
    var clickPosition = e.GetPosition(this);

    var transform = draggableElement.RenderTranform as TranslateTransform;
    startPosition.X = clickPosition.X - transform.X;    //注意減號
    startPosition.Y = clickPosition.Y - transform.Y;

    draggableElement.CaptureMouse();
}

private void grid_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
    isDragging = false;
    var draggableElement = sender as UIElement;
    draggableElement.ReleaseMouseCapture();
}

private void grid_MouseMove(object sender, MouseEventArgs e)
{
    var draggableElement = sender as UIElement;
    if (isDragging && draggableElement != null)
    {
        Point currentPosition = e.GetPosition(this.Parent as UIElement);
        var transform = draggableElement.RenderTransform as TranslateTransform;

        transform.X = currentPosition.X - startPosition.X;
        transform.Y = currentPosition.Y - startPosition.Y;
     }
}