WPF拖放vs操作

本文关键字:操作 vs 拖放 WPF | 更新日期: 2023-09-27 18:10:34

我想在具有IsManipulationEnabled = true的父控件中启用子控件的拖放功能。

当操作被启用时,触摸事件不会被提升为鼠标事件。为了启用提升,应该在操作逻辑进入之前处理触摸事件(见示例)。我试过了,很管用……直到我第一次打电话给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 -事件将得到应有的提升

WPF拖放vs操作

看起来这是。net中的一个bug。我安装了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事件以重新启用操作。