First things first: I have a WPF project which uses C# and MVVM (MVVM Light), in Visual Studio 2010.
I have a Canvas control inside which is a ListBox - each ListBoxItem can be moved around with mouse drag (each has a DataTemplate contains a Thumb control to allow each item to be dragged).
That looks as follows:
<DataTemplate>
<Grid Background="Transparent">
<Thumb Name="myThumb" Template="{StaticResource NodeVisualTemplateRegular}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="DragDelta">
<cmd:EventToCommand Command="{Binding ChatNodeListViewModel.DragDeltaCommand, Source={StaticResource Locator}}" PassEventArgsToCommand="True"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Thumb>
</Grid> </DataTemplate>
As you can see, there's a command which handles the dragging part (DeltaDragCommand).
None of this is a problem, but then I wanted to add the ability to pan around the Canvas control with a click and drag motion. This is where things come into conflict with the above stated dragging of ListBoxItems.
The Canvas pan stuff is handled in the code behind and the subscriptions for the events look like this:
NodeDragScrollViewer.PreviewMouseLeftButtonDown += OnMouseLeftButtonDown;
NodeDragScrollViewer.PreviewMouseLeftButtonUp += OnMouseLeftButtonUp;
NodeDragScrollViewer.MouseMove += OnMouseMove;
NodeDragScrollViewer.PreviewMouseWheel += OnPreviewMouseWheel;
NodeDragScrollViewer.ScrollChanged += OnScrollViewerScrollChanged;
The events we see here are run on the ScrollViewer (a control in which the Canvas control sits). The problem is that these events sit 'on top of' the ListBoxItems - the click for the pan of the canvas will activate before the click for the drag of the ListBoxItem.
Now a solution I've seen is to use the VisualTreeHelper HitTest method to find out what was clicked on. If I can find out if a ListBoxItem was clicked on, then I can ignore the Canvas pan actions and let things progress as before.
The problem I'm facing is that I just can't get this to work. I have seem people explain how this should work and have borrowed code for this action. I present that code here:
Point pt = new Point();
VisualTreeHelper.HitTest(NodeDragCanvas, null,
new HitTestResultCallback(MyHitTestResult),
new PointHitTestParameters(pt));
private HitTestResultBehavior MyHitTestResult(HitTestResult result)
{
var p = FindParent<ListBoxItem>(result.VisualHit);
//Set the behavior to return visuals at all z-order levels.
return HitTestResultBehavior.Continue;
}
public static T FindParent<T>(DependencyObject child) where T : DependencyObject
{
//get parent item
DependencyObject parentObject = VisualTreeHelper.GetParent(child);
//we've reached the end of the tree
if (parentObject == null) return null;
//check if the parent matches the type we're looking for
T parent = parentObject as T;
if (parent != null)
return parent;
else
return FindParent<T>(parentObject);
}
If you look at the contents of HitTestResultBehaviour, this is where I check for the control that I've clicked on. I'm hoping it will be a ListBoxItem... but that always returns 'null'. I am aware that the method can fire multiple times, but I've never seen the var p (in this case) be anything other than a null value. If, however, I attempt to use FindParent<ListBox>, I will get returned the ListBox. It has the right amount of items in it and seems to be as I would expect. But this doesn't work for me since my ListBox is the size of the Canvas.
It seems this is the key point, but I don't know what else to try and all avenues of research seem to lead back to this method.
Can anyone offer any guidance? Thanks