WPF 拖放与操作

WPF Drag&Drop vs manipulation

我想在具有 IsManipulationEnabled = true.

的父控件中启用子控件的拖放

启用操作后,触摸事件不会升级为鼠标事件。为了启用提升,应该在操作逻辑介入之前处理触摸事件(参见 example)。我已经尝试过了并且它有效......直到我第一次调用 DoDragDrop 。然后我不再收到鼠标事件。为什么?

这是重现问题的最少代码。为了便于阅读,删除了所有拖放操作。

XAML:

<Window x:Class="Test.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Window1" Height="300" Width="300" IsManipulationEnabled="True">
    <Grid>
        <Border Background="Red" 
                x:Name="Border"
                TouchDown="Border_OnTouchDown"
                MouseDown="Border_OnMouseDown"
                TouchUp="Border_OnTouchUp"
                Width="100" Height="50" />
    </Grid>
</Window>

C#:

public partial class Window1 : Window
{
    public Window1()
    {
        InitializeComponent();
    }

    private void Border_OnTouchDown(object sender, TouchEventArgs e)
    {
        Debug.WriteLine("Border_OnTouchDown");
        e.Handled = true;
        e.TouchDevice.Capture((FrameworkElement)sender);
    }

    private void Border_OnMouseDown(object sender, MouseButtonEventArgs e)
    {
        Debug.WriteLine("Border_OnMouseDown!");
        DragDrop.DoDragDrop((DependencyObject)sender, "", DragDropEffects.All);
    }

    protected override void OnManipulationStarted(ManipulationStartedEventArgs e)
    {
        Debug.WriteLine("OnManipulationStarted");
        base.OnManipulationStarted(e);
    }

    private void Border_OnTouchUp(object sender, TouchEventArgs e)
    {
        ((FrameworkElement)sender).ReleaseTouchCapture(e.TouchDevice);
        e.Handled = true;
    }
}

输出:

Border_OnTouchDown
Border_OnMouseDown! <- works first time
Border_OnTouchDown 
Border_OnTouchDown  <- no longer works, no matter how many times I tap
Border_OnTouchDown
Border_OnTouchDown
Border_OnTouchDown
...

如果我不在 MouseDown 中调用 DoDragDrop - 事件会得到应有的提升。

看起来这是 .NET 中的错误。我安装了 v4.5.2。现在我已经安装了 v4.6,问题不再重现。

我什至不需要将项目重新定位到 v4.6 或重新编译它:只需安装新的运行时就可以解决所有问题。

此解决方案适用于任何框架版本:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }

    private void Border_OnTouchDown(object sender, TouchEventArgs e)
    {
        Debug.WriteLine("Border_OnTouchDown");

        IsManipulationEnabled = false;
        e.TouchDevice.Capture(Border);
    }

    private void Border_OnMouseDown(object sender, MouseButtonEventArgs e)
    {
        Debug.WriteLine("Border_OnMouseDown!");
        DragDrop.DoDragDrop((DependencyObject)sender, "", DragDropEffects.All);
    }

    protected override void OnManipulationStarted(ManipulationStartedEventArgs e)
    {
        Debug.WriteLine("OnManipulationStarted");
        base.OnManipulationStarted(e);
    }

    private void Border_OnTouchUp(object sender, TouchEventArgs e)
    {
        Border.ReleaseTouchCapture(e.TouchDevice);
        IsManipulationEnabled = true;
    }
}

这里我们基本上是在用户触摸 Border 时禁用操作。由于拖放操作可能(并且可能会)在 Border 之外结束,我们还需要捕获触摸输入以确保接收到 TouchUp 事件以重新启用操作。