Reputation: 1307
I have a treeview Item as such in a treeview that will have a list bound to it:
<TreeViewItem Name="tviOffline" Foreground="Red" FontWeight="Bold"
Header="Offline">
<TreeViewItem.ItemTemplate>
<DataTemplate DataType="{x:Type local:Buddy}">
<StackPanel>
<TextBlock Text="{Binding Nick}" FontSize="10" Foreground="#8CFFD528" />
</StackPanel>
</DataTemplate>
</TreeViewItem.ItemTemplate>
</TreeViewItem>
I cannot figure out how to get each of its childs to have a double click event.
any help is appreciated. thanks much.
Upvotes: 14
Views: 25475
Reputation: 13296
The WPF TreeView
is a really tricky control.
You need to set the event handler with a style:
<TreeView>
<TreeView.Resources>
<Style TargetType="{x:Type TreeViewItem}" BasedOn="{StaticResource {x:Type TreeViewItem}}">
<EventSetter Event="MouseDoubleClick" Handler="TreeViewItem_OnMouseDoubleClick"/>
</Style>
</TreeView.Resources>
</TreeView>
By putting the style in the resources of the TreeView
, the style will be applied to all the items (and not only to the first level like when you use ItemContainerStyle
).
If the user clicks on a child, the event will also be raised on all the parents because the child is inside its parent (it's not bubbling, the double click event is handled by the TreeViewItem
so it does not go up in the hierarchy ; setting Handled
in your event handler does not change anything here). So you have to filter the event in your handler to only do work when the element which has been clicked (OriginalSource
) is in the TreeViewItem
which has raised the event.
void TreeViewItem_OnMouseDoubleClick(object sender, MouseButtonEventArgs e)
{
var senderTreeViewItem = (TreeViewItem)sender;
TreeViewItem treeViewItemWhichHasBeenClicked = ((DependencyObject)e.OriginalSource!).GetVisualParentOrDefault<TreeViewItem>()!;
if (senderTreeViewItem == treeViewItemWhichHasBeenClicked)
{
Debug.WriteLine("Do stuff");
}
}
static class DependencyObjectExtensions
{
public static T? GetVisualParentOrDefault<T>(this DependencyObject depObj) where T : DependencyObject
{
while (true)
{
ArgumentNullException.ThrowIfNull(depObj);
DependencyObject? parent = VisualTreeHelper.GetParent(depObj);
switch (parent)
{
case null:
return null;
case T result:
return result;
default:
depObj = parent;
break;
}
}
}
}
Upvotes: 0
Reputation: 1783
This is the only way I managed to get it to work for all the cases:
void MyTreeView_PreviewMouseDoubleClick(object sender, MouseButtonEventArgs e)
{
var clickedItem = TryGetClickedItem(myTreeView, e);
if (clickedItem == null)
return;
e.Handled = true; // to cancel expanded/collapsed toggle
DoStuff(clickedItem);
}
TreeViewItem TryGetClickedItem(TreeView treeView, MouseButtonEventArgs e)
{
var hit = e.OriginalSource as DependencyObject;
while (hit != null && !(hit is TreeViewItem))
hit = VisualTreeHelper.GetParent(hit);
return hit as TreeViewItem;
}
Upvotes: 5
Reputation: 7817
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<EventSetter Event="MouseDoubleClick" Handler="OnItemMouseDoubleClick" />
...
And THEN, the handler has to be written as follows in order to prevent the double-click from firing on successive parent TreeViewItems:
private void OnItemMouseDoubleClick(object sender, MouseButtonEventArgs args)
{
if (sender is TreeViewItem)
{
if (!((TreeViewItem)sender).IsSelected)
{
return;
}
}
.... do stuff.
}
Thanks to Aurelien Ribon for getting 90% of the way there. The double-click problem seems to be well-known in other postings on Stack Exchange. Just consolidating both solutions into one answer.
Upvotes: 20
Reputation: 7634
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<EventSetter Event="MouseDoubleClick" Handler="OnItemMouseDoubleClick" />
...
Upvotes: 30