代码之家  ›  专栏  ›  技术社区  ›  IanR

WPF列表框拖放是否与上下文菜单冲突?

  •  3
  • IanR  · 技术社区  · 16 年前

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

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

    <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;
            }
        }
    }
    
    1 回复  |  直到 16 年前
        1
  •  1
  •   IanR    16 年前

    这似乎与抓老鼠有关!?

    拖动过程中的正常事件顺序如下…

    1. 这个 PreviewMouseLeftButtonDown 处理程序被调用并 ListBox.IsMouseCaptureWithin 是 错误的。
    2. 这个 PreviewMouseMove 处理程序 调用。到这个时候 列表框.ismousecapturewithin 是真的。
    3. 期间 预览鼠标移动 处理程序 DragDrop.DoDragDrop 调用和 在这期间的某个时候,老鼠 捕获从列表框中释放。

    但是,当上下文菜单打开时,一个拖拽开始的过程似乎是…

    1. 这个 预览鼠标松开按钮向下 处理程序被调用并 列表框.ismousecapturewithin 是 错误的。
    2. 这个 预览鼠标移动 处理程序获取 打电话。但是这次 列表框.ismousecapturewithin 是 仍然是错误的。
    3. 结束后的某个时候 预览鼠标移动 处理程序 然后列表框获取鼠标捕获 ( 列表框.ismousecapturewithin 变为真)

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

    将以下代码添加到 预览鼠标松开按钮向下 处理程序似乎有助于吞下关闭上下文菜单的单击,而不是尝试从中开始拖动…

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

    …… contextMenuCloseComplete bool在上下文菜单的处理程序中设置 Closed Opened 事件。

    这有道理吗?有人知道这种老鼠捕捉行为是从哪里来的吗?

    推荐文章