WPF:将 PreviewMouseLeftButtonDown 事件处理程序添加到父 ListBox 后,带扩展器的 ListBoxItem 无法扩展

WPF: ListBoxItem with expander cannot expand after adding PreviewMouseLeftButtonDown event handler to parent ListBox

我有一个列表框,其中的子项是扩展器。我需要为此实现 DragDrop 事件。如果我写 XAML

<ListBox PreviewMouseLeftButtonDown="StartDragDrop">

, StartDragDrop 方法效果很好,但子扩展器无法扩展。 如果我写

<ListBox MouseLeftButtonDown="StartDragDrop">

,子扩展器工作正常,但 StartDragDrop 方法不工作。 我认为问题与气泡和隧道事件有关,但我不知道明确的解决方案。 我需要 StartDragDrop 方法和 ListBox 子扩展器 Expand 方法,它们都可以正常工作。我该怎么办?

你的假设是部分正确的,它必须与隧道效应和冒泡有关。外部控件的隧道(带预览)事件在冒泡(无预览)事件之前执行。但并不妨碍后者被处决。这仅在整个事件链中的某处 e.Handled 设置为 true 时才成立。看这个例子:

XAML:

<Border Background="Red" PreviewMouseMove="OnPreviewMouseMove"> 
  <Border Background="Blue" MouseMove="OnMouseMove" />
</Border>

C#

private void OnPreviewMouseMove(object sender, MouseEventArgs e)
{
  Debug.WriteLine("Preview outer");
  e.Handled = true;  // this prevents OnMouseMove from being executed
}

private void OnMouseMove(object sender, MouseEventArgs e)
{
  Debug.WriteLine("NoPreview inner");
}

如果删除行 "e.Handled = true;",OnMouseMove 将被命中。如果您不自己设置它,请考虑调用 base."event-name" 可能会做到这一点。

主要思想是当移动偏移量大于某种值时,在 PreviewMouseMove() 事件中调用 DoDragDrop 方法。

1) 这里是列表框:

<ListBox AllowDrop="True" Drop=" ListBox_Drop" PreviewMouseLeftButtonDown="ListBox_PreviewMouseLeftButtonDown" PreviewMouseMove="ListBox_PreviewMouseMove">

ListBoxItems 是扩展器,如果我们实现 DragAndDrop 则无法扩展。

2) 现在我们必须添加 2 个变量(我使用 VB.NET):

Private isDragging As Boolean = False 'flag: is drag operation in process?'
Private dragStartPoint As Point 'coords of dragging start.'

3) 通过鼠标单击预览记住起点坐标:

    Private Sub ListBox_PreviewMouseLeftButtonDown(sender As Object, e As MouseButtonEventArgs)
    dragStartPoint = e.GetPosition(Me)
End Sub

4) 在 PreviewMouseMove 上获取移动起点到当前移动点的偏移量。如果偏移量大于某个值,我们启动 DragAndDrop 操作并设置标志 isDragging 以记住这一点。

Private Sub ListBox_PreviewMouseMove(sender As System.Object, e As MouseEventArgs)
    If e.LeftButton = MouseButtonState.Pressed Then
        Dim diff As Vector = Point.Subtract(dragStartPoint, e.GetPosition(Me))
        If (Math.Abs(diff.X) > SystemParameters.MinimumHorizontalDragDistance) OrElse (Math.Abs(diff.Y) > SystemParameters.MinimumVerticalDragDistance) Then
            If Not isDragging Then 
                isDragging = True 'the flag is active until drop event raises.'
                Dim lstBox As ListBox = TryCast(sender, ListBox) 'get sender ListBox'
                If lstBox IsNot Nothing Then
                    Dim data As Object = GetDataFromListBox(lstBox, e.GetPosition(lstBox)) 'get data for drag-and-drop; need to be realized; there are some realizations at whosebug.com presented.'
Dim effects As DragDropEffects = DragDrop.DoDragDrop(lstBox, data, DragDropEffects.Move) 'initiate drag-and-drop.'

                    End If
                End If
            End If
        End If
    End Sub

5) 处理drop操作:

Private Sub ListBox_Drop(sender As Object, e As DragEventArgs)
        isDragging = False 'reset isDragging flag.'
        Dim lstBox As ListBox = TryCast(sender, ListBox) 'get sender ListBox.'
        If lstBox IsNot Nothing Then
            Dim myObj As MyClass = TryCast(e.Data.GetData(GetType(MyClass)), MyClass)
        '...some actions'
        End If
End Sub

我已经实现了这个想法,它的工作原理正是我所需要的:

  • MouseLeftButtonClick 带有扩展器的 ListBoxItems 是扩展和 倒塌,
  • 在按下左键的 MouseMove 上 DragAndDrop 操作是 有效,ListBoxItems 可以排序。