WPF 可拖动和可点击按钮
WPF draggable and clickable button
我有一个无边框透明Window
只有一个Button
。
My fancy button looks like this
预期的行为是:
- 当我点击并拖动按钮时,按钮必须跟随光标。
- 当我只点击 Button 时,MouseDown、PreviewMouseDown 事件或 Command 绑定应该触发。
起初我尝试在 PreviewMouseDown 事件上调用 DragMove(),但这阻止了点击事件。现在我的想法是:我在鼠标按下后设置了 100 毫秒的延迟。如果时间过去了,按钮将被拖动,否则只是点击。
代码
private bool _dragging;
private Point startpos;
CancellationTokenSource cancellation;
private void Button_PreviewMouseMove(object sender, MouseEventArgs e)
{
if (_dragging && e.LeftButton == MouseButtonState.Pressed)
{
var currentpos = e.GetPosition(this);
Left += currentpos.X - startpos.X;
Top += currentpos.Y - startpos.Y;
}
}
private async void Button_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
if (e.ChangedButton != MouseButton.Left)
return;
_dragging = false;
startpos = e.GetPosition(this);
cancellation?.Cancel();
cancellation = new CancellationTokenSource();
await Task.Delay(100, cancellation.Token).ContinueWith(task =>
{
_dragging = !task.IsCanceled;
});
}
private void Button_PreviewMouseUp(object sender, MouseButtonEventArgs e)
{
if (_dragging)
{
_dragging = false;
e.Handled = true;
}
cancellation?.Cancel();
}
基本上可以用,但有一些错误:
- 当我按住鼠标按钮较长时间并释放时,点击将不起作用,因为 100 毫秒后拖动将被激活。
- 在我拖动按钮并单击
Button
和 Window
控件之外的任意位置后,将引发 PreviewMouseDown 和 PreviewMouseUp 事件。不知道为什么??
有人有更好的解决方案吗?
对于你的第一个问题:
当您按下按钮但未移动时,您必须处理这种情况。我认为更好的方法(而不是 100 毫秒延迟)是指定一个最小移动阈值,超过该阈值将开始拖动。
你可以这样做:
private const double _dragThreshold = 1.0;
private bool _dragging;
private Point startpos;
CancellationTokenSource cancellation;
private void Button_PreviewMouseMove(object sender, MouseEventArgs e)
{
var currentpos = e.GetPosition(this);
var delta = currentpos - startpos;
if ((delta.Length > _dragThreshold || _dragging) && e.LeftButton == MouseButtonState.Pressed)
{
_dragging = true;
Left += currentpos.X - startpos.X;
Top += currentpos.Y - startpos.Y;
}
}
private async void Button_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
if (e.ChangedButton != MouseButton.Left)
return;
_dragging = false;
startpos = e.GetPosition(this);
cancellation?.Cancel();
cancellation = new CancellationTokenSource();
}
对于你的第二个问题:按钮将捕获鼠标按下事件。
完成拖动后,您需要使用 ReleaseMouseCapture
方法释放捕获的鼠标。
private void Button_PreviewMouseUp(object sender, MouseButtonEventArgs e)
{
if (_dragging)
{
_dragging = false;
e.Handled = true;
var button = sender as Button;
button.ReleaseMouseCapture();
}
cancellation?.Cancel();
}
我有一个无边框透明Window
只有一个Button
。
My fancy button looks like this
预期的行为是:
- 当我点击并拖动按钮时,按钮必须跟随光标。
- 当我只点击 Button 时,MouseDown、PreviewMouseDown 事件或 Command 绑定应该触发。
起初我尝试在 PreviewMouseDown 事件上调用 DragMove(),但这阻止了点击事件。现在我的想法是:我在鼠标按下后设置了 100 毫秒的延迟。如果时间过去了,按钮将被拖动,否则只是点击。
代码
private bool _dragging;
private Point startpos;
CancellationTokenSource cancellation;
private void Button_PreviewMouseMove(object sender, MouseEventArgs e)
{
if (_dragging && e.LeftButton == MouseButtonState.Pressed)
{
var currentpos = e.GetPosition(this);
Left += currentpos.X - startpos.X;
Top += currentpos.Y - startpos.Y;
}
}
private async void Button_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
if (e.ChangedButton != MouseButton.Left)
return;
_dragging = false;
startpos = e.GetPosition(this);
cancellation?.Cancel();
cancellation = new CancellationTokenSource();
await Task.Delay(100, cancellation.Token).ContinueWith(task =>
{
_dragging = !task.IsCanceled;
});
}
private void Button_PreviewMouseUp(object sender, MouseButtonEventArgs e)
{
if (_dragging)
{
_dragging = false;
e.Handled = true;
}
cancellation?.Cancel();
}
基本上可以用,但有一些错误:
- 当我按住鼠标按钮较长时间并释放时,点击将不起作用,因为 100 毫秒后拖动将被激活。
- 在我拖动按钮并单击
Button
和Window
控件之外的任意位置后,将引发 PreviewMouseDown 和 PreviewMouseUp 事件。不知道为什么??
有人有更好的解决方案吗?
对于你的第一个问题:
当您按下按钮但未移动时,您必须处理这种情况。我认为更好的方法(而不是 100 毫秒延迟)是指定一个最小移动阈值,超过该阈值将开始拖动。
你可以这样做:
private const double _dragThreshold = 1.0;
private bool _dragging;
private Point startpos;
CancellationTokenSource cancellation;
private void Button_PreviewMouseMove(object sender, MouseEventArgs e)
{
var currentpos = e.GetPosition(this);
var delta = currentpos - startpos;
if ((delta.Length > _dragThreshold || _dragging) && e.LeftButton == MouseButtonState.Pressed)
{
_dragging = true;
Left += currentpos.X - startpos.X;
Top += currentpos.Y - startpos.Y;
}
}
private async void Button_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
if (e.ChangedButton != MouseButton.Left)
return;
_dragging = false;
startpos = e.GetPosition(this);
cancellation?.Cancel();
cancellation = new CancellationTokenSource();
}
对于你的第二个问题:按钮将捕获鼠标按下事件。
完成拖动后,您需要使用 ReleaseMouseCapture
方法释放捕获的鼠标。
private void Button_PreviewMouseUp(object sender, MouseButtonEventArgs e)
{
if (_dragging)
{
_dragging = false;
e.Handled = true;
var button = sender as Button;
button.ReleaseMouseCapture();
}
cancellation?.Cancel();
}