WPF TreeView, get TreeViewItem in PreviewMouseDown event - wpf

How can I determine TreeViewItem clicked in PreviewMouseDown event?

The following seems to work:
private void myTreeView_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
TreeViewItem item = GetTreeViewItemClicked((FrameworkElement)e.OriginalSource,
myTreeView);
...
}
private TreeViewItem GetTreeViewItemClicked(FrameworkElement sender, TreeView treeView)
{
Point p = ((sender as FrameworkElement)).TranslatePoint(new Point(0, 0), treeView);
DependencyObject obj = treeView.InputHitTest(p) as DependencyObject;
while (obj != null && !(obj is TreeViewItem))
obj = VisualTreeHelper.GetParent(obj);
return obj as TreeViewItem;
}

I originally used an extension method on TreeView that takes a UIElement--the sender of the PreviewMouseDown event--like this:
private void MyTreeView_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
var uiElement = sender as UIElement;
var treeViewItem = myTreeView.TreeViewItemFromChild(uiElement);
}
Here's the extension method (it checks the child itself in case you clicked right on a TreeViewItem directly)...
public static TreeViewItem TreeViewItemFromChild(this TreeView treeView, UIElement child)
{
UIElement proposedElement = child;
while ((proposedElement != null) && !(proposedElement is TreeViewItem))
proposedElement = VisualTreeHelper.GetParent(proposedElement) as UIElement;
return proposedElement as TreeViewItem;
}
Update:
However, I've since switched it to a more generic version that I can use anywhere.
public static TAncestor FindAncestor<TAncestor>(this UIElement uiElement)
{
while ((uiElement != null) && !(uiElement is TAncestor))
retVal = VisualTreeHelper.GetParent(uiElement) as UIElement;
return uiElement as TAncestor;
}
That either finds the type you're looking for (again, including checking itself) or returns null
You'd use it in the same PreviewMouseDown handler like so...
private void MyTreeView_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
var uiElement = sender as UIElement;
var treeViewItem = uiElement.FindAncestor<TreeViewItem>();
}
This came in very handy for when my TreeViewItem had a CheckBox in its template and I wanted to select the item when the user clicked the checkbox which normally swallows the event.
Hope this helps!

Related

How to bring into view all TreeViewItems of expanded TreeViewItem

On my Window I have TreeView. TreeView.ItemsSource is binded to my data (hierarchical data structure). When I expand some TreeViewItem that is on the bottom of the current scrollbar position, my subitems are hidden and I have to scroll to see them. Is there any way to bring all subitems of expanded item into view?
I have tried this, but didn't work:
public void TreeViewItem_Expanded(object sender, RoutedEventArgs e)
{
TreeViewItem tvi = e.OriginalSource as TreeViewItem;
if (tvi != null)
{
Debug.WriteLine("TreeNode '{0}' was expanded", tvi.Header);
tvi.BringIntoView();
}
}
Basically, select the last sub-item and bring it into view when an item is expanded. If it has no subitems, bring the item itself into view.
The tricky part is, subitems are not necessarily available when the event is executed, so I propose you check the ItemContainerGenerator.Status and if it's not yet done, subscribe to the ItemContainerGenerator.StatusChanged event.
The following code is only tested with static items, if it's making problem for dynamic data sources, I may have to re-check a few details.
void TreeViewItem_Expanded(object sender, RoutedEventArgs e)
{
TreeViewItem tvi = e.OriginalSource as TreeViewItem;
if (tvi != null)
{
if (tvi.HasItems)
{
if (tvi.ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated)
{
BringLastItemIntoView(tvi.ItemContainerGenerator);
}
else
{
tvi.ItemContainerGenerator.StatusChanged += ItemContainerGenerator_StatusChanged;
}
}
else
{
tvi.BringIntoView();
}
}
}
void ItemContainerGenerator_StatusChanged(object sender, EventArgs e)
{
var s = sender as ItemContainerGenerator;
if (s.Status == GeneratorStatus.ContainersGenerated)
{
BringLastItemIntoView(s);
s.StatusChanged -= ItemContainerGenerator_StatusChanged;
}
}
void BringLastItemIntoView(ItemContainerGenerator generator)
{
var tvi = generator.ContainerFromItem(generator.Items.LastOrDefault()) as TreeViewItem;
if (tvi != null)
{
tvi.BringIntoView();
}
}

PasswordBox validation on text change instead of Lose Focus

Framework: Silverlight 4
I have a simple ChildWindow with Username TextBox and Password PasswordBox. I have attached an event handler to the window's KeyDown event.
private void onKeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Escape)
cancelButtonClick(null, null);
if (e.Key == Key.Enter)
okButtonClick(null, null);
}
What I'm trying to achieve is that when the user presses Enter key on the keyboard, the program will behave as if the user had clicked the OK button.
The problem is the validation.
The default behavior for Silverlight's PasswordBox is to perform validation when the control loses focus. My PasswordBox is bound to some User object. When I click the Enter button, the event handler gets called, which in turn calls the okButtonClick(null, null). The problem is that the PasswordBox has not yet lost the focus at that time, so the user.Password property, which the PasswordBox is bound to, is still empty.
I've tried to place btnOK.Focus() before the okButtonClick(null, null) but to no avail.
How to set the binding so that the control will update binding on every text change instead of on the LostFocus event? What is the right way to achieve what I need?
Use PasswordChanged event for Passwordbox.
Textbox has TextboxChanged event.
I have solved the problem by stumbling upon this solution.
It basicly uses attached property to subscribe to the TextChanged/PasswordChnaged events, and then in the event it updates the binding source. After adapting the solution to my needs, here is what I got (and it works flawlessly):
public class BindingHelper
{
public static readonly DependencyProperty RefreshOnChangeProperty =
DependencyProperty.RegisterAttached("RefreshOnChange", typeof(bool), typeof(BindingHelper),
new PropertyMetadata(false, OnRefreshOnChangeChanged));
public static void SetRefreshOnChange(DependencyObject o, bool value)
{
o.SetValue(RefreshOnChangeProperty, value);
}
public static bool GetRefreshOnChange(DependencyObject o)
{
return (bool)o.GetValue(RefreshOnChangeProperty);
}
private static void OnRefreshOnChangeChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
if ((obj as TextBox) != null)
{
if ((bool)e.NewValue)
(obj as TextBox).TextChanged += textBox_TextChanged;
else
(obj as TextBox).TextChanged -= textBox_TextChanged;
}
if ((obj as PasswordBox) != null)
{
if ((bool)e.NewValue)
(obj as PasswordBox).PasswordChanged += passwordBox_PasswordChanged;
else
(obj as PasswordBox).PasswordChanged -= passwordBox_PasswordChanged;
}
}
static void passwordBox_PasswordChanged(object sender, RoutedEventArgs e)
{
PasswordBox passwordBox = sender as PasswordBox;
if (passwordBox != null)
{
BindingExpression binding = passwordBox.GetBindingExpression(PasswordBox.PasswordProperty);
if (binding != null)
binding.UpdateSource();
}
}
static void textBox_TextChanged(object sender, TextChangedEventArgs e)
{
TextBox textBox = sender as TextBox;
if (textBox != null)
{
BindingExpression binding = textBox.GetBindingExpression(TextBox.TextProperty);
if (binding != null)
binding.UpdateSource();
}
}
}

WPF MVVM design question

In my View I have a TreeView with a event "TreeView_MouseLeftButtonDown". If it fires it proofs if the mouse clicked on a TreeViewItem. If not it deselects the last TreeViewItem.
My question is, should i do this in the code-behind or call a static methode in the viewmodel-class? How would you solve this?
The Methode:
private void treeView_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (sender != null)
{
var treeView = sender as TreeView;
if (treeView != null && treeView.SelectedItem != null)
TreeViewHelper.ReturnTreeViewItem(ref treeView, (XmlNode)treeView.SelectedItem).IsSelected = false;
}
}
XAML:
<TreeView ... KeyDown="TreeView_KeyDown"
MouseLeftButtonDown="TreeView_MouseLeftButtonDown"
SelectedItemChanged="TreeView_SelectedItemChanged" />
You are trying to add a behaviour to the TreeView.
The way I would implement this would be using Attached Properties. I would create an attached property called VerifiesLeftClick or similar and implement the logic in there. This way you do not need an event in the code behind.
See here for samples.
I made for you solution using attached behaviors which were pretty well described here Introduction to Attached Behaviors in WPF by Josh Smith
My solution:
public static class TreeViewBehavior
{
public static bool GetIsResetMouseLeftButtonDown(TreeView treeView)
{
return (bool)treeView.GetValue(IsResetMouseLeftButtonDownProperty);
}
public static void SetIsResetMouseLeftButtonDown(TreeView treeViewItem, bool value)
{
treeViewItem.SetValue(IsResetMouseLeftButtonDownProperty, value);
}
public static readonly DependencyProperty IsResetMouseLeftButtonDownProperty =
DependencyProperty.RegisterAttached("PreviewMouseLeftButtonDown", typeof(bool), typeof(TreeViewBehavior),
new UIPropertyMetadata(false, OnIsMouseLeftButtonDownChanged));
static void OnIsMouseLeftButtonDownChanged(DependencyObject depObj, DependencyPropertyChangedEventArgs e)
{
TreeView item = depObj as TreeView;
if (item == null)
return;
if (e.NewValue is bool == false)
return;
if ((bool)e.NewValue)
{
item.PreviewMouseLeftButtonDown += OnMouseLeftButtonDown;
}
else
{
item.MouseLeftButtonDown -= OnMouseLeftButtonDown;
}
}
static void OnMouseLeftButtonDown(object sender, RoutedEventArgs e)
{
var tempItem = e.Source as TreeViewItem;
if (tempItem != null && tempItem.IsSelected == false)
{
tempItem.IsSelected = true;
}
else
{
var tree = e.Source as TreeView;
if (tree != null && tree.SelectedItem != null)
{
var selItem = (tree.SelectedItem as TreeViewItem);
if (selItem != null)
{
selItem.IsSelected = false;
}
}
}
}
}
and then in View you should add this:
<TreeView local:TreeViewBehavior.IsResetMouseLeftButtonDown="True">
I hope my solution do what you are trying to achieve.

What is the proper way to handle multiple datagrids in a tab control so that cells leave edit mode when the tabs are changed?

In wpf I setup a tab control that binds to a collection of objects each object has a data template with a data grid presenting the data. If I select a particular cell and put it into edit mode, leaving the grid by going to another tab this will cause the exception below to be thrown on returning the datagrid:
'DeferRefresh' is not allowed during an AddNew or EditItem transaction.
It appears that the cell never left edit mode. Is there an easy way to take the cell out of edit mode, or is something else going on here?
Update: It looks like if I do not bind the tab control to the data source, but instead explicitly define each tab and then bind each item in the data source to a content control this problem goes away. This is not really a great solution, so I would still like to know how to bind the collection directly to the tab control.
Update: So what I have actually done for my own solution is to use a ListView and a content control in place of a tab control. I use a style to make the list view look tab like. The view model exposes a set of child view models and allows the user to select one via the list view. The content control then presents the selected view model and each view model has an associated data template which contains the data grid. With this setup switching between view models while in edit mode on the grid will properly end edit mode and save the data.
Here is the xaml for setting this up:
<ListView ItemTemplate="{StaticResource MakeItemsLookLikeTabs}"
ItemsSource="{Binding ViewModels}"
SelectedItem="{Binding Selected}"
Style="{StaticResource MakeItLookLikeATabControl}"/>
<ContentControl Content="{Binding Selected}">
I'll accept Phil's answer as that should work also, but for me the solution above seems like it will be more portable between projects.
I implemented a behavior for the DataGrid based on code I found in this thread.
Usage:<DataGrid local:DataGridCommitEditBehavior.CommitOnLostFocus="True" />
Code:
using System.Collections.Generic;
using System.Reflection;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using System.Windows.Media;
/// <summary>
/// Provides an ugly hack to prevent a bug in the data grid.
/// https://connect.microsoft.com/VisualStudio/feedback/details/532494/wpf-datagrid-and-tabcontrol-deferrefresh-exception
/// </summary>
public class DataGridCommitEditBehavior
{
public static readonly DependencyProperty CommitOnLostFocusProperty =
DependencyProperty.RegisterAttached(
"CommitOnLostFocus",
typeof(bool),
typeof(DataGridCommitEditBehavior),
new UIPropertyMetadata(false, OnCommitOnLostFocusChanged));
/// <summary>
/// A hack to find the data grid in the event handler of the tab control.
/// </summary>
private static readonly Dictionary<TabPanel, DataGrid> ControlMap = new Dictionary<TabPanel, DataGrid>();
public static bool GetCommitOnLostFocus(DataGrid datagrid)
{
return (bool)datagrid.GetValue(CommitOnLostFocusProperty);
}
public static void SetCommitOnLostFocus(DataGrid datagrid, bool value)
{
datagrid.SetValue(CommitOnLostFocusProperty, value);
}
private static void CommitEdit(DataGrid dataGrid)
{
dataGrid.CommitEdit(DataGridEditingUnit.Cell, true);
dataGrid.CommitEdit(DataGridEditingUnit.Row, true);
}
private static DataGrid GetParentDatagrid(UIElement element)
{
UIElement childElement; // element from which to start the tree navigation, looking for a Datagrid parent
if (element is ComboBoxItem)
{
// Since ComboBoxItem.Parent is null, we must pass through ItemsPresenter in order to get the parent ComboBox
var parentItemsPresenter = VisualTreeFinder.FindParentControl<ItemsPresenter>(element as ComboBoxItem);
var combobox = parentItemsPresenter.TemplatedParent as ComboBox;
childElement = combobox;
}
else
{
childElement = element;
}
var parentDatagrid = VisualTreeFinder.FindParentControl<DataGrid>(childElement);
return parentDatagrid;
}
private static TabPanel GetTabPanel(TabControl tabControl)
{
return
(TabPanel)
tabControl.GetType().InvokeMember(
"ItemsHost",
BindingFlags.NonPublic | BindingFlags.GetProperty | BindingFlags.Instance,
null,
tabControl,
null);
}
private static void OnCommitOnLostFocusChanged(DependencyObject depObj, DependencyPropertyChangedEventArgs e)
{
var dataGrid = depObj as DataGrid;
if (dataGrid == null)
{
return;
}
if (e.NewValue is bool == false)
{
return;
}
var parentTabControl = VisualTreeFinder.FindParentControl<TabControl>(dataGrid);
var tabPanel = GetTabPanel(parentTabControl);
if (tabPanel != null)
{
ControlMap[tabPanel] = dataGrid;
}
if ((bool)e.NewValue)
{
// Attach event handlers
if (parentTabControl != null)
{
tabPanel.PreviewMouseLeftButtonDown += OnParentTabControlPreviewMouseLeftButtonDown;
}
dataGrid.LostKeyboardFocus += OnDataGridLostFocus;
dataGrid.DataContextChanged += OnDataGridDataContextChanged;
dataGrid.IsVisibleChanged += OnDataGridIsVisibleChanged;
}
else
{
// Detach event handlers
if (parentTabControl != null)
{
tabPanel.PreviewMouseLeftButtonDown -= OnParentTabControlPreviewMouseLeftButtonDown;
}
dataGrid.LostKeyboardFocus -= OnDataGridLostFocus;
dataGrid.DataContextChanged -= OnDataGridDataContextChanged;
dataGrid.IsVisibleChanged -= OnDataGridIsVisibleChanged;
}
}
private static void OnDataGridDataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
var dataGrid = (DataGrid)sender;
CommitEdit(dataGrid);
}
private static void OnDataGridIsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
{
var senderDatagrid = (DataGrid)sender;
if ((bool)e.NewValue == false)
{
CommitEdit(senderDatagrid);
}
}
private static void OnDataGridLostFocus(object sender, KeyboardFocusChangedEventArgs e)
{
var dataGrid = (DataGrid)sender;
var focusedElement = Keyboard.FocusedElement as UIElement;
if (focusedElement == null)
{
return;
}
var focusedDatagrid = GetParentDatagrid(focusedElement);
// Let's see if the new focused element is inside a datagrid
if (focusedDatagrid == dataGrid)
{
// If the new focused element is inside the same datagrid, then we don't need to do anything;
// this happens, for instance, when we enter in edit-mode: the DataGrid element loses keyboard-focus,
// which passes to the selected DataGridCell child
return;
}
CommitEdit(dataGrid);
}
private static void OnParentTabControlPreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
var dataGrid = ControlMap[(TabPanel)sender];
CommitEdit(dataGrid);
}
}
public static class VisualTreeFinder
{
/// <summary>
/// Find a specific parent object type in the visual tree
/// </summary>
public static T FindParentControl<T>(DependencyObject outerDepObj) where T : DependencyObject
{
var dObj = VisualTreeHelper.GetParent(outerDepObj);
if (dObj == null)
{
return null;
}
if (dObj is T)
{
return dObj as T;
}
while ((dObj = VisualTreeHelper.GetParent(dObj)) != null)
{
if (dObj is T)
{
return dObj as T;
}
}
return null;
}
}
I have managed to work around this issue by detecting when the user clicks on a TabItem and then committing edits on visible DataGrid in the TabControl. I'm assuming the user will expect their changes to still be there when they click back.
Code snippet:
// PreviewMouseDown event handler on the TabControl
private void TabControl_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
if (IsUnderTabHeader(e.OriginalSource as DependencyObject))
CommitTables(yourTabControl);
}
private bool IsUnderTabHeader(DependencyObject control)
{
if (control is TabItem)
return true;
DependencyObject parent = VisualTreeHelper.GetParent(control);
if (parent == null)
return false;
return IsUnderTabHeader(parent);
}
private void CommitTables(DependencyObject control)
{
if (control is DataGrid)
{
DataGrid grid = control as DataGrid;
grid.CommitEdit(DataGridEditingUnit.Row, true);
return;
}
int childrenCount = VisualTreeHelper.GetChildrenCount(control);
for (int childIndex = 0; childIndex < childrenCount; childIndex++)
CommitTables(VisualTreeHelper.GetChild(control, childIndex));
}
This is in the code behind.
This bug is solved in the .NET Framework 4.5. You can download it at this link.
What I think you should do is pretty close to what #myermian said.
There is an event called CellEditEnding end this event would allow you to intercept and make the decision to drop the unwanted row.
private void dataGrid1_CellEditEnding(object sender, DataGridCellEditEndingEventArgs e)
{
DataGrid grid = (DataGrid)sender;
TextBox cell = (TextBox)e.EditingElement;
if(String.IsNullOrEmpty(cell.Text) && e.EditAction == DataGridEditAction.Commit)
{
grid.CancelEdit(DataGridEditingUnit.Row);
e.Cancel = true;
}
}

How to deselect all selected items in a WPF treeview when clicking on some empty area?

I've got a rather funny problem with WPF. I have a tree-view, and selecting items works fine so far. The problem is, I want to unselect the currently selected item when the user clicks inside the blank area of the treeview. By default, the treeview keeps the current item selected, and I have added a context-menu option to deselect it, which is rather hardcore:
// Note: This is done recursivly from the start, so it
// works for child items as well
treeView.ItemContainerGenerator.ContainerFromItem(treeView.SelectedItem) as TreeViewItem).IsSelected = false;
Moreover, this is counter-intuitive, as it requires the user to right-click first, and second, after deselecting it with this way, the user cannot select it any more by clicking on the item. How is this supposed to work?
Edit: Some more information: I've added a handler to the TreeView to handle mouse click events, but the sender is always a TreeView instance, even if I click directly on a TreeViewItem. If I add a handler to my TreeView.ItemTemplate instead (i.e. the first child in the template), I never get events when I click on the empty area (which is rather logical). The code looks like this:
private void MyTreeView_MouseUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
if ((sender as TreeViewItem) == null)
{
// Always triggered
System.Diagnostics.Trace.Write("Empty area clicked");
}
}
And the XAML for this is:
<TreeView x:Name="MyTreeView" Margin="3" MouseUp="MyTreeView_MouseUp">
I found this to work much better for me. I check the originalsource which for me if it comes form a treeviewitem will be an image or a textblock. I also use a view object with a HierarchicalDataTemplate and the BasicTreeViewBase is the base class for all of my different objects. Here is the code.
private void TemplateTreeView_MouseDown(object sender, MouseButtonEventArgs e)
{
if (e.ChangedButton == MouseButton.Right && !(e.OriginalSource is Image) && !(e.OriginalSource is TextBlock))
{
BasicTreeViewBase item = TemplateTreeView.SelectedItem as BasicTreeViewBase;
if (item != null)
{
TemplateTreeView.Focus();
item.IsSelected = false;
}
}
}
The un-selectable problem can be solved with a call to Focus on the TreeView after setting TreeViewItem.IsSelected.
There can be two more problem :
The treeview is binded so the SelectedItem is an item of the binded collection.
There is many levels so ItemContainerGenerator do not contain deepest level objects
for all this reason i use this function, but the selection must not fire any events.
private void UnselectTreeViewItem(TreeView pTreeView)
{
if(pTreeView.SelectedItem == null)
return;
if(pTreeView.SelectedItem is TreeViewItem)
{
(pTreeView.SelectedItem as TreeViewItem).IsSelected = false;
}
else
{
TreeViewItem item = pTreeView.ItemContainerGenerator.ContainerFromIndex(0) as TreeViewItem;
if (item != null)
{
item.IsSelected = true;
item.IsSelected = false;
}
}
}
I implemented a general selection control once, and required this behaviour.
This is how my method looked (adapted for treeview):
protected override void OnMouseUp(MouseButtonEventArgs e)
{
base.OnMouseUp(e);
DependencyObject dpSource = e.OriginalSource as DependencyObject;
if (dpSource.FindVisualAncestor(o => typeof(TreeViewItem).IsAssignableFrom(o.GetType())) == null)
UnselectAll();
}
Basically, walk up the tree from the source. If a TreeViewItem was not found, than the user clicked empty space.
Use the extension class below
public static class TreeViewExtensions
{
public static TreeViewItem ContainerFromItem(this TreeView treeView, object item)
{
TreeViewItem containerThatMightContainItem = (TreeViewItem)treeView.ItemContainerGenerator.ContainerFromItem(item);
if (containerThatMightContainItem != null)
return containerThatMightContainItem;
else
return ContainerFromItem(treeView.ItemContainerGenerator, treeView.Items, item);
}
private static TreeViewItem ContainerFromItem(ItemContainerGenerator parentItemContainerGenerator, ItemCollection itemCollection, object item)
{
foreach (object curChildItem in itemCollection)
{
TreeViewItem parentContainer = (TreeViewItem)parentItemContainerGenerator.ContainerFromItem(curChildItem);
TreeViewItem containerThatMightContainItem = (TreeViewItem)parentContainer.ItemContainerGenerator.ContainerFromItem(item);
if (containerThatMightContainItem != null)
return containerThatMightContainItem;
TreeViewItem recursionResult = ContainerFromItem(parentContainer.ItemContainerGenerator, parentContainer.Items, item);
if (recursionResult != null)
return recursionResult;
}
return null;
}
}
Then in MouseDown event of treeview use the extension method as below:
private void trview_MouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
if ((sender as TreeViewItem) == null)
{
if (this.trview.ContainerFromItem(trview.SelectedItem) != null)
{
this.trview.ContainerFromItem(trview.SelectedItem).IsSelected = false;
}
}
this.trview.Focus();
}
Hope it works for you. I have it working in this way...
I was running into this situation myself with a custom Tree List View implementation after looking for a long time I finally found a solution that worked for me.
The full explanation can be found at http://social.msdn.microsoft.com/Forums/vstudio/en-US/36aca7f7-0b47-488b-8e16-840b86addfa3/getting-treeviewitem-for-the-selected-item-in-a-treeview
The basic idea is you capture the TreeViewItem.Selected event and save the source of the event into the Tag attribute on your TreeView. Then when you need to clear it, you can access the Tag attribute on your control and set the IsSelected value to False. This works for me with 2 levels of nested children. Hopefully it will work for you.
For persistence sake:
TreeView declaration
<TreeView Name="myTreeView" TreeViewItem.Selected="OnItemSelected"
ItemsSource="{Binding Source={StaticResource myHierarchicalData}}"/>
Event Handler
private void OnItemSelected(object sender, RoutedEventArgs e)
{
myTreeView.Tag = e.OriginalSource;
}
Clear selection logic
if (myTreeView.SelectedItem != null)
{
TreeViewItem selectedTVI = myTreeView.Tag as TreeViewItem;
// add your code here mine was selectedTVI.IsSelected = false;
}
This will deselect the currently selected TreeViewItem if none were clicked:
private void MyTreeView_PreviewMouseDown(object sender, MouseButtonEventArgs e) {
if ((sender as TreeViewItem) == null) {
TreeViewItem item = MyTreeView.SelectedItem as TreeViewItem;
if(item != null){
item.IsSelected = false;
}
}
}
Hope this is what you were looking for!
MVVM: Call this method in the eventhandler righ/left mouse click:
private void SetTreeViewSelection(object sender, MouseButtonEventArgs mouseButtonEventArgs)
{
var treeView = (TreeView)sender;
if (treeView.SelectedItem == null)
{
return;
}
IInputElement dropNode = treeView.InputHitTest(mouseButtonEventArgs.GetPosition(treeView));
if (dropNode is ScrollViewer)
{
var myBindableObject = (MyBindableObject)treeView.SelectedItem;
myBindableObject.IsSelected = false;
}
}
For a C# treeview you use treeview.SelectedNode = null; I'm not sure if this works for WPF.

Resources