相关文章推荐
完美的莴苣  ·  ASP.NET Core Blazor ...·  3 周前    · 
健壮的鸵鸟  ·  在兼容Amazon的JSON_TABLE ...·  5 月前    · 
曾深爱过的手链  ·  Quill ...·  1 年前    · 
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.

  •