WPF 在按钮上拖放

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/15632317/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-13 08:19:25  来源:igfitidea点击:

WPF drag and drop on a button

c#wpfbuttondrag-and-drop

提问by Aozine

I'm writing a WPF application that has grids of buttons and I want to allow the user to drag and drop buttons between grids, possibly between different instances of the application. I've tried doing this by adding a handler to the PreviewMouseMove event on the button and then calling DoDragDrop if the left mouse button is down, but when I drag and drop the button it always ends up calling DoDragDrop twice and the drop event handler twice. Does anyone know why this happens and how to prevent it?

我正在编写一个具有按钮网格的 WPF 应用程序,我希望允许用户在网格之间拖放按钮,可能在应用程序的不同实例之间。我尝试通过向按钮上的 PreviewMouseMove 事件添加一个处理程序,然后在鼠标左键按下时调用 DoDragDrop 来执行此操作,但是当我拖放按钮时,它总是最终调用两次 DoDragDrop 和两次放置事件处理程序. 有谁知道为什么会发生这种情况以及如何预防?

Here's some example XAML which demonstrates the problem:

下面是一些演示问题的示例 XAML:

<Window x:Class="WpfTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <DockPanel>
        <Button PreviewMouseMove="PreviewMouseMove" x:Name="m_button" Width="250">
            Hello, world!
        </Button>
        <Label Drop="Drop" AllowDrop="True">
            Drop here!
        </Label>
    </DockPanel>
</Window>

and the corresponding code:

和相应的代码:

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

    private void PreviewMouseMove(object sender, MouseEventArgs e)
    {
        if (e.LeftButton == MouseButtonState.Pressed)
        {
            ++m_dragIndex;
            System.Console.WriteLine("Dragged: " + m_dragIndex.ToString());
            DragDrop.DoDragDrop(m_button, m_dragIndex, DragDropEffects.All);
            e.Handled = true;
        }
    }

    private void Drop(object sender, DragEventArgs e)
    {
        System.Console.WriteLine("Dropped: " + e.Data.GetData(typeof(Int32)).ToString());
    }

    private int m_dragIndex;
}

For a single drag, this gets written to the output:

对于单个拖动,这将写入输出:

Dragged: 1
Dragged: 2
Dropped: 2
Dropped: 1

UPDATE:I've changed the example code above to show which drop events get called when the button is dropped onto something.

更新:我已经更改了上面的示例代码,以显示当按钮被放到某物上时调用了哪些放置事件。

UPDATE:Updated the question to include dragging between containers and application instances, since this is the motivating factor for using the DragDrop system.

更新:更新了问题以包括在容器和应用程序实例之间拖动,因为这是使用 DragDrop 系统的激励因素。

回答by Aozine

I've found a workaround for this - since the first DoDragDrop calls the second PreviewMouseMove internally, I can track whether I'm already inside a PreviewMouseMove call and ignore it if I am. This seems a bit nasty though so I'm hoping there's a better solution.

我找到了一个解决方法 - 由于第一个 DoDragDrop 在内部调用第二个 PreviewMouseMove,我可以跟踪我是否已经在 PreviewMouseMove 调用中,如果是,则忽略它。这似乎有点讨厌,所以我希望有更好的解决方案。

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

    private void PreviewMouseMove(object sender, MouseEventArgs e)
    {
        if(!m_inMouseMove)
        {
            m_inMouseMove = true;
            if (e.LeftButton == MouseButtonState.Pressed)
            {
                ++m_dragIndex;
                System.Console.WriteLine("Dragged: " + m_dragIndex.ToString());
                DragDrop.DoDragDrop(m_button, m_dragIndex, DragDropEffects.All);
                e.Handled = true;
            }
            m_inMouseMove = false;
        }
    }

    private void Drop(object sender, DragEventArgs e)
    {
        System.Console.WriteLine("Dropped: " + e.Data.GetData(typeof(Int32)).ToString());
    }

    private int m_dragIndex;
    bool m_inMouseMove;
}

回答by korzenikov

I've had the same problem. I've discovered that PreviewMouseMove called twice because the first time it raised by container element (e.g. ListViewItem) and the second one - for TextBlock. So I can suggest checking the type of e.OriginalSource value as another workaround for this issue.

我遇到了同样的问题。我发现 PreviewMouseMove 调用了两次,因为它第一次由容器元素(例如 ListViewItem)引发,第二次是用于 TextBlock。所以我可以建议检查 e.OriginalSource 值的类型作为此问题的另一种解决方法。