Introduction
I had a need to set some items in a TreeView as not selectable as they were top items of different types.
Since the items were not of type TreeViewItem
I had some limitations on how to do it.
Using the code
Since I like MVVM, this will be done using behaviors. We need to take over the items before they are presented to the user. Behaviors add to the control some sort
of "behavior" and by that we are able to control how it responds to actions in our application.
We need to take over the loaded event as in this point
the treeview finished loading and the items are already in the Items
list of the treeview. In the loaded event we will go over all items in the list and find
the ones we are looking for. The trick in having the items not selectable is simple: not giving them focus.
So first let's run over the items:
private void TreeViewLoaded(object sender, RoutedEventArgs routedEventArgs)
{
foreach (var item in AssociatedObject.Items)
{
if (item is ContainerViewModel)
{
SetNotFocusable(AssociatedObject, item);
}
}
}
The items in the list are of the type I've bound them to (different view models) so I can't
really set focusable
to false
. I need a helper method that will do it for me.
The helper method will need to run over the TreeViewItem
s in the list and find a requested item, and set its
focusable
property to false
.
I got from the web a general method that gets a parent class for items named
ItemsControl
.
The first thing the method does is try and get the requested item from the
ItemsControl
children. As it can only get level 1 items, the method is recursive.
static private bool SetNotFocusable(ItemsControl parent, object child)
{
if (parent == null || child == null)
return false;
TreeViewItem childNode = parent.ItemContainerGenerator
.ContainerFromItem(child) as TreeViewItem;
if (childNode != null)
{
return childNode.Focusable = false;
}
}
We are using above the method ContainerFromItem
in order to get the
TreeViewItem
itself from the object we need, which the TreeViewItem
wraps.
Once found, we set its Focusable
property to false
.
If not found then we need to go over the children, and if one of them is a container itself then go inside.
if (parent.Items.Count > 0)
{
foreach (object childItem in parent.Items)
{
ItemsControl childControl = parent
.ItemContainerGenerator
.ContainerFromItem(childItem)
as ItemsControl;
if (SetNotFocusable(childControl, child))
{
return true;
}
}
}
If trying to find the object fails then return false
.
Here is the complete method:
static private bool SetNotFocusable(ItemsControl parent, object child)
{
if (parent == null || child == null)
return false;
TreeViewItem childNode = parent.ItemContainerGenerator
.ContainerFromItem(child) as TreeViewItem;
if (childNode != null)
{
childNode.Focus();
return childNode.Focusable = false;
}
if (parent.Items.Count > 0)
{
foreach (object childItem in parent.Items)
{
ItemsControl childControl = parent
.ItemContainerGenerator
.ContainerFromItem(childItem)
as ItemsControl;
if (SetNotFocusable(childControl, child))
{
return true;
}
}
}
return false;
}
Hope this will help you as it helped me.