Reputation:
Say I have a 3-level data-bound WPF TreeView like this one:
where the selected node is bbb under cc under c. Is there a way to bind to say, cc, from some control living outside the TreeView?
UPDATE
Where I'm trying to get at here is to something similar to how you can bind to a selected item in a ListBox using this syntax:
<TextBox Text="{Binding Path=VM.Definitions/term}" />
where a ListBox ItemsSource is bound to VM.Definitions and ListBox.IsSynchronizedWithCurrentItem is set to True. I'm trying to figure out if there is a similar approach to bind to a specific level of a TreeView with scoped HierarchicalDataTemplates.
HOW I MADE IT WORK:
I accepted H.B. answers, but it was a combination of both his and Tim Murphy's answers that made me "see the light". Thing is that you can't (AFAIK) bind to levels of a TreeView like you can bind to the single level of a ListBox (actually you can, but not to a specific level).
So I realized that all I have to do is to link back to my VM whatever is selected on each level of the TreeView whenever the selection changes. For example, say you have a TreeView with three levels Customer, Order, OrderItem. In the SelectedItemChanged, you set back in your VM each level.
If the selected item is a Customer, you then have VM.SelectedCustomer set to the Customer, and VM.SelectedOrder and VM.SelectedOrderItem set to null. If the selected item is an Order, then you set VM.SelectedCustomer to the parent item of the selected item, you set VM.SelectedOrder to the selected item, and VM.SelectedOrderItem to null. And so on.
A quick example (not my actual code, just to demo the concept):
void TreeView_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
{
if (treeViewLesson.SelectedItem == null) {
VM.SelectedOrderItem = null;
VM.SelectedOrder = null;
VM.SelectedCustomer = null;
}
else if (treeViewLesson.SelectedItem is Customer) {
VM.SelectedOrderItem = null;
VM.SelectedOrder = null;
VM.SelectedCustomer = treeViewLesson.SelectedItem as Customer;
}
else if (treeViewLesson.SelectedItem is Order) {
VM.SelectedOrderItem = null;
VM.SelectedOrder = treeViewLesson.SelectedItem as Order;
VM.SelectedCustomer = VM.SelectedOrder.ParentCustomer;
}
else if (treeViewLesson.SelectedItem is OrderItem) {
VM.SelectedOrderItem = treeViewLesson.SelectedItem as OrderItem;
VM.SelectedOrder = VM.SelectedOrderItem.ParentOrder;
VM.SelectedCustomer = VM.SelectedOrder.ParentCustomer;
}
}
Upvotes: 2
Views: 866
Reputation: 185589
Edit: This kind of behavior is considerably complicated and the tools the TreeView gives you are not exactly thrilling. Sadly i cannot give you a complete answer on this but only a few pointers.
CurrentItem
property which returns the item on the path of the selection at the current level, i.e. the item which owns the subbranch which ultimately contains the selected item.Upward navigation would be quite helpful in this case i think, so having a Parent
may be a good idea as well. e.g. if the selected item is changed you can go up the tree and change the CurrentItem
property respectively, (the following may be rather bad/incorrect example code)
private void TreeView_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
{
var oldItem = e.OldValue as TreeViewModel;
// "Deselect" old branch
var parent = oldItem.Parent;
while (parent != null)
{
parent.CurrentItem = null;
parent = parent.Parent;
}
// "Select" new branch
var newItem = e.NewValue as TreeViewModel;
parent = newItem.Parent;
while (parent != null)
{
parent.CurrentItem = newItem;
newItem = parent;
parent = parent.Parent;
}
}
Bindings could then be done via a path of CurrentItems
properties:
{Binding Root.CurrentItem.CurrentItem.Value}
Well, you can bind to pretty much anything, the question normally is how do you point to it.
(Assume TreeView
to be named tv
, also assumes all items to be TreeViewItems
rather than data)
In this case if you can make a binding relative to the selection to get to that cc
:
{Binding SelectedItem.Parent, ElementName=tv}
This of course will bind to cc
with any subselection, not only bbb
.
Or you can drill down via indices, independent from any selection:
{Binding Items[2].Items[2], ElementName=tv}
You might want to be more specific about what you want to achieve.
Upvotes: 2
Reputation: 4932
If you are already using the MVVM pattern then the process is fairly easy.
Add an IsSelected property to your view model. When a TreeViewItem is selected by the user the IsSelected property of a bound object will be set.
Now within IsSelected property update the property for the control you want to change when a tree view item is selected.
C# Sample
public class TreeViewItemViewModel
{
public bool IsSelected {
get { return _isSelected; }
set {
if (value != _isSelected) {
_isSelected = value;
this.IsSelectedChanged();
}
}
}
protected virtual void IsSelectedChanged()
{
this.RaisePropertyChanged("IsSelected");
// do custom code
}
}
VB.NET Sample
Public Class TreeViewItemViewModel
Public Property IsSelected As Boolean
Get
Return _isSelected
End Get
Set(value As Boolean)
If value <> _isSelected Then
_isSelected = value
Me.IsSelectedChanged()
End If
End Set
End Property
Protected Overridable Sub IsSelectedChanged()
Me.RaisePropertyChanged("IsSelected")
' do custom code '
End Sub
End Class
Upvotes: 0