Collectives™ on Stack Overflow
Find centralized, trusted content and collaborate around the technologies you use most.
Learn more about Collectives
Teams
Q&A for work
Connect and share knowledge within a single location that is structured and easy to search.
Learn more about Teams
I'm trying to bind the expanded event to the viewmodel (not the *.xaml.cs file) to extend the treeview only after expanding a node.
My approach is:
<TreeView x:Name="TreeViewTest" ItemsSource="{Binding Items}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Children, Mode=OneTime}">
<StackPanel>
<Label Content="{Binding Title, Mode=OneTime}" />
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
<i:Interaction.Triggers>
<i:EventTrigger EventName="TreeViewItem.Expanded">
<i:InvokeCommandAction Command="{Binding DataContext.ExpandedCommand, Source={x:Reference TreeViewTest}}"> </i:InvokeCommandAction>
</i:EventTrigger>
</i:Interaction.Triggers>
</TreeView>
I get following error message:
Cannot call MarkupExtension.ProvideValue because of a cyclical dependency. Properties inside a MarkupExtension cannot reference objects that reference the result of the MarkupExtension. The affected MarkupExtensions are:
System.Windows.Data.Binding
Can someone help me how to solve this error or is there another way to bind the event to a command in the viewmodel?
As I can understand you try to attach a command to TreeViewItem.Expanded event.
I've done a small research concerning this issue and here is the result I've end up with.
There is a small problem with the tree view items and this event.
There are several approaches to solve the problem:
First is AttachedProperties in style of the TreeViewItem, here is the link:
Invoke Command when TreeViewItem is Expanded
Second is Behavior involved solution:
XAML:
<TreeView x:Name="TreeViewTest" ItemsSource="{Binding Items}" TreeViewItem.Expanded="TreeViewTest_OnExpanded">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate DataType="{x:Type soTreeViewHelpAttempt:TreeObject}"
ItemsSource="{Binding Children, Mode=OneTime}">
<HierarchicalDataTemplate.ItemTemplate>
<DataTemplate>
<StackPanel>
<Label Content="{Binding }" />
</StackPanel>
</DataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
<StackPanel>
<Label Content="{Binding Title, Mode=OneTime}" />
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
<i:Interaction.Behaviors>
<soTreeViewHelpAttempt:TreeViewItemExpandBehavior OnExpandedAction="{Binding OnExpandedActionInViewModel}"/>
</i:Interaction.Behaviors>
</TreeView></Grid>
ViewModel:
public MainDataContext()
OnExpandedActionInViewModel = new Action<object, RoutedEventArgs>(OnExpanded);
public Action<object, RoutedEventArgs> OnExpandedActionInViewModel
get { return _onExpandedActionInViewModel; }
private set
_onExpandedActionInViewModel = value;
OnPropertyChanged();
Behavior Command property:
public static readonly DependencyProperty OnExpandedActionProperty = DependencyProperty.Register(
"OnExpandedAction", typeof (Action<object,RoutedEventArgs>), typeof (TreeViewItemExpandBehavior), new PropertyMetadata(default(Action<object,RoutedEventArgs>)));
public Action<object,RoutedEventArgs> OnExpandedAction
get { return (Action<object,RoutedEventArgs>) GetValue(OnExpandedActionProperty); }
set { SetValue(OnExpandedActionProperty, value); }
Behavior code:
protected override void OnAttached()
base.OnAttached();
AssociatedObject.Loaded += AssociatedObjectOnLoaded;
private void AssociatedObjectOnLoaded(object sender, RoutedEventArgs routedEventArgs)
var treeView = sender as TreeView;
if(treeView == null) return;
treeView.ItemsSource.Cast<object>().ToList().ForEach(o =>
var treeViewItem = treeView.ItemContainerGenerator.ContainerFromItem(o) as TreeViewItem;
if(treeViewItem == null) return;
treeViewItem.Expanded += TreeViewItemOnExpanded;
_list.Add(treeViewItem);
private void TreeViewItemOnExpanded(object sender, RoutedEventArgs routedEventArgs)
if(OnExpandedAction == null)
return;
OnExpandedAction(sender, routedEventArgs);
protected override void OnDetaching()
base.OnDetaching();
AssociatedObject.Loaded -= AssociatedObjectOnLoaded;
_list.ForEach(item => item.Expanded -= TreeViewItemOnExpanded);
_list.Clear();
I hope It will help you.
Regards,
To create Binding to TreeView you must use RelativeSource Self, but not Source with x:Reference.
For example here i'm binding Width of TreeView to Height:
<TreeView Name="tv" Width="{Binding Height, RelativeSource={RelativeSource Self}}">
</TreeView>
Or also you can use ElementName, like here:
<TreeView Name="tv" Width="{Binding Height, ElementName=tv}">
</TreeView>
I know this question is old, but there's an obvious solution that does not require any custom code.
When using i:EventTrigger
, you can specify the object to listen the event from as below, where I'm using RelativeSource
binding to walk up the visual tree and find a TreeViewItem
.
Also, you can bind to an element by name using ElementName
, which doesn't cause circular dependencies because bindings work on the dispatcher when the controls are already created and initialized (unlike x:Reference
, which has to be resolved during XAML deserialization).
<i:Interaction.Triggers>
<i:EventTrigger SourceObject="{Binding RelativeSource={RelativeSource AncestorType=TreeViewItem}}" EventName="Expanded">
<i:InvokeCommandAction Command="{Binding ElementName=TreeView, Path=DataContext.ExpandedCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.