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;
}
}