WPF перетаскиваемая и нажимаемая кнопка - PullRequest
0 голосов
/ 13 февраля 2020

У меня есть прозрачный Window без полей и только один Button.

Моя необычная кнопка выглядит следующим образом

Ожидаемое поведение:

  • Когда я нажимаю и перетаскиваю кнопку, кнопка должна следовать за курсором.
  • Когда я нажимаю только кнопку, события MouseDown, PreviewMouseDown или привязка команд должны вызываться.

Сначала я попытался вызвать DragMove () для события PreviewMouseDown, но это блокирует события щелчка. Теперь моя идея такова: я установил задержку в 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. Я не знаю почему ??

У кого-нибудь есть лучшее решение?

1 Ответ

0 голосов
/ 13 февраля 2020

Для вашей первой проблемы:
Вы должны обрабатывать случай, когда ваша кнопка нажата, но не перемещена. Я думаю, что лучшим способом сделать это (вместо задержки в 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();
}
...