WPF 列表框拖拽drop 会干扰 ContextMenu?

发布于 2024-08-07 02:03:21 字数 2195 浏览 3 评论 0原文

我正在实施拖放从列表框中删除,但我在窗口其他位置的上下文菜单中看到一些奇怪的行为。如果打开上下文菜单,然后从 ListBox 开始拖动,则上下文菜单将关闭,但在执行另一次拖动之前不会再次打开。

这有道理吗?有人知道会发生什么吗?

<ListBox Grid.Row="0" ItemsSource="{Binding SourceItems}" MultiSelectListboxDragDrop:ListBoxExtension.SelectedItemsSource="{Binding SelectedItems}" SelectionMode="Multiple" PreviewMouseLeftButtonDown="HandleLeftButtonDown" PreviewMouseLeftButtonUp="HandleLeftButtonUp" PreviewMouseMove="HandleMouseMove"/>
<ListBox Grid.Row="1" ItemsSource="{Binding DestinationItems}" AllowDrop="True" Drop="DropOnToDestination" />
<Button Grid.Row="2">
    <Button.ContextMenu>
        <ContextMenu x:Name="theContextMenu">
            <MenuItem Header="context 1"/>
            <MenuItem Header="context 2"/>
            <MenuItem Header="context 3"/>
        </ContextMenu>
    </Button.ContextMenu>
    Button with context menu
</Button>

...

public partial class Window1
{
    private bool clickedOnSourceItem;

    public Window1()
    {
        InitializeComponent();

        DataContext = new WindowViewModel();
    }

    private void DropOnToDestination(object sender, DragEventArgs e)
    {
        var viewModel = (WindowViewModel)e.Data.GetData(typeof(WindowViewModel));
        viewModel.CopySelectedItems();
    }

    private void HandleLeftButtonDown(object sender, MouseButtonEventArgs e)
    {
        var sourceElement = (FrameworkElement)sender;
        var hitItem = sourceElement.InputHitTest(e.GetPosition(sourceElement)) as FrameworkElement;

        if(hitItem != null)
        {
            clickedOnSourceItem = true;
        }
    }

    private void HandleLeftButtonUp(object sender, MouseButtonEventArgs e)
    {
        clickedOnSourceItem = false;
    }

    private void HandleMouseMove(object sender, MouseEventArgs e)
    {
        if(clickedOnSourceItem)
        {
            var sourceItems = (FrameworkElement)sender;
            var viewModel = (WindowViewModel)DataContext;

            DragDrop.DoDragDrop(sourceItems, viewModel, DragDropEffects.Move);
            clickedOnSourceItem = false;
        }
    }
}

I'm implementing drag & drop from a ListBox, but I'm seeing some strange behaviour with a ContextMenu elsewhere in the window. If you open the context menu and then start a drag from the ListBox, the context menu closes but won't open again until after you perform another drag.

Does this make sense? Anybody got any ideas what might be going on?

<ListBox Grid.Row="0" ItemsSource="{Binding SourceItems}" MultiSelectListboxDragDrop:ListBoxExtension.SelectedItemsSource="{Binding SelectedItems}" SelectionMode="Multiple" PreviewMouseLeftButtonDown="HandleLeftButtonDown" PreviewMouseLeftButtonUp="HandleLeftButtonUp" PreviewMouseMove="HandleMouseMove"/>
<ListBox Grid.Row="1" ItemsSource="{Binding DestinationItems}" AllowDrop="True" Drop="DropOnToDestination" />
<Button Grid.Row="2">
    <Button.ContextMenu>
        <ContextMenu x:Name="theContextMenu">
            <MenuItem Header="context 1"/>
            <MenuItem Header="context 2"/>
            <MenuItem Header="context 3"/>
        </ContextMenu>
    </Button.ContextMenu>
    Button with context menu
</Button>

...

public partial class Window1
{
    private bool clickedOnSourceItem;

    public Window1()
    {
        InitializeComponent();

        DataContext = new WindowViewModel();
    }

    private void DropOnToDestination(object sender, DragEventArgs e)
    {
        var viewModel = (WindowViewModel)e.Data.GetData(typeof(WindowViewModel));
        viewModel.CopySelectedItems();
    }

    private void HandleLeftButtonDown(object sender, MouseButtonEventArgs e)
    {
        var sourceElement = (FrameworkElement)sender;
        var hitItem = sourceElement.InputHitTest(e.GetPosition(sourceElement)) as FrameworkElement;

        if(hitItem != null)
        {
            clickedOnSourceItem = true;
        }
    }

    private void HandleLeftButtonUp(object sender, MouseButtonEventArgs e)
    {
        clickedOnSourceItem = false;
    }

    private void HandleMouseMove(object sender, MouseEventArgs e)
    {
        if(clickedOnSourceItem)
        {
            var sourceItems = (FrameworkElement)sender;
            var viewModel = (WindowViewModel)DataContext;

            DragDrop.DoDragDrop(sourceItems, viewModel, DragDropEffects.Move);
            clickedOnSourceItem = false;
        }
    }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

榕城若虚 2024-08-14 02:03:21

好像和鼠标捕捉有关!?

拖动过程中事件的正常顺序是这样的...

  1. PreviewMouseLeftButtonDown
    处理程序被调用并且
    ListBox.IsMouseCaptureWithin
    错误的。
  2. PreviewMouseMove 处理程序
    被叫。到了这个时候
    ListBox.IsMouseCaptureWithin 为 true。
  3. PreviewMouseMove 处理程序期间
    DragDrop.DoDragDrop 被调用并且
    在此期间的某个时候,鼠标
    捕获从列表框中释放。

但是,当上下文菜单打开时开始拖动似乎发生的是......

  1. PreviewMouseLeftButtonDown
    处理程序被调用并且
    ListBox.IsMouseCaptureWithin
    错误的。
  2. PreviewMouseMove 处理程序获取
    叫。但这一次
    ListBox.IsMouseCaptureWithin
    仍然是假的。
  3. 结束后的某个时间
    PreviewMouseMove 处理程序
    ListBox然后获取鼠标捕获
    (ListBox.IsMouseCaptureWithin
    变为 true)

这样做的结果是,拖动后,列表框仍然具有鼠标捕获,因此任何单击按钮以打开上下文菜单的操作实际上都会进入列表框而不是按钮。

将以下代码添加到 PreviewMouseLeftButtonDown 处理程序的开头似乎有助于吞下关闭该上下文菜单的单击,而不是尝试从中开始拖动...

if (!contextMenuCloseComplete)
{
    sourceElement.CaptureMouse();
    return;
}

...使用 contextMenuCloseComplete bool 在上下文菜单的 ClosedOpened 事件的处理程序中设置。

这有道理吗?有谁知道这种鼠标捕获行为来自哪里?

It seemed to be something to do with the mouse capture!?

The normal sequence of events during a drag goes something like this...

  1. The PreviewMouseLeftButtonDown
    handler gets called and
    ListBox.IsMouseCaptureWithin is
    false.
  2. The PreviewMouseMove handler
    gets called. By this time
    ListBox.IsMouseCaptureWithin is true.
  3. During the PreviewMouseMove handler
    DragDrop.DoDragDrop gets called and
    sometime during this the mouse
    capture is released from the ListBox.

But, what seems to happening for a drag started when the context menu is open is...

  1. The PreviewMouseLeftButtonDown
    handler gets called and
    ListBox.IsMouseCaptureWithin is
    false.
  2. The PreviewMouseMove handler gets
    called. But this time
    ListBox.IsMouseCaptureWithin is
    still false.
  3. Sometime after the end of the
    PreviewMouseMove handler the
    ListBox then gets the mouse capture
    (ListBox.IsMouseCaptureWithin
    becomes true)

The result of this is that after the drag, the ListBox still has the mouse capture so any clicks on the button to open the context menu are actually going to the listbox not the button.

Adding the following code to the start of the PreviewMouseLeftButtonDown handler seems to help by swallowing up the click that closes that context menu rather than trying to start a drag from it...

if (!contextMenuCloseComplete)
{
    sourceElement.CaptureMouse();
    return;
}

...with the contextMenuCloseComplete bool getting set in handlers for the context menu's Closed and Opened events.

Does that make sense? Does anyone understand where this mouse capture behaviour is coming from?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文