我有一个只有一个Window
的无边界透明Button
。
My fancy button looks like this
预期的行为是:
起初,我尝试在PreviewMouseDown事件上调用DragMove(),但这会阻止click事件。现在我的想法是:我将鼠标按下后设置100ms的延迟。如果时间过去了,那么按钮将被拖动,否则仅是一次单击。
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();
}
基本上可以,但是有一些错误:
Button
和Window
控件之外的任何位置,都会引发PreviewMouseDown和PreviewMouseUp事件。我不知道为什么?有人有更好的解决方案吗?
第一个问题:当您按下按钮但不移动时,您必须处理这种情况。我认为一种更好的方法(而不是100ms的延迟)将是指定最小移动阈值,在此阈值以上将开始拖动。您可以这样做:
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();
}