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;
}
}
}
Best Answer
It seemed to be something to do with the mouse capture!?
The normal sequence of events during a drag goes something like this...
PreviewMouseLeftButtonDown
handler gets called andListBox.IsMouseCaptureWithin
is false.PreviewMouseMove
handler gets called. By this timeListBox.IsMouseCaptureWithin
is true.PreviewMouseMove
handlerDragDrop.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...
PreviewMouseLeftButtonDown
handler gets called andListBox.IsMouseCaptureWithin
is false.PreviewMouseMove
handler gets called. But this timeListBox.IsMouseCaptureWithin
is still false.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......with the
contextMenuCloseComplete
bool getting set in handlers for the context menu'sClosed
andOpened
events.Does that make sense? Does anyone understand where this mouse capture behaviour is coming from?