如何将WPF树视图项的展开事件绑定到viewmodel

时间:2015-10-18 09:30:04

标签: c# wpf mvvm data-binding treeview

我尝试将展开的事件绑定到viewmodel(而不是* .xaml.cs文件),以便仅在展开节点后扩展树视图。

我的方法是:

<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>

我收到以下错误消息:

  

由于周期性依赖性,无法调用MarkupExtension.ProvideValue。 MarkupExtension中的属性不能引用引用MarkupExtension结果的对象。受影响的MarkupExtensions是:   System.Windows.Data.Binding

有人可以帮我解决这个错误,还是有另一种方法将事件绑定到viewmodel中的命令?

3 个答案:

答案 0 :(得分:2)

我可以理解您尝试将命令附加到TreeViewItem.Expanded事件。 我已经就这个问题进行了一项小型研究,这是我最终得到的结果。

  1. 树视图项和此事件存在一个小问题。
  2. 有几种方法可以解决这个问题:
  3. 首先是TreeViewItem风格的AttachedProperties,这里是链接: Invoke Command when TreeViewItem is Expanded

    其次是涉及行为的解决方案:

    1. XAML:

      <Grid>
      <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>
      
    2. 视图模型:

          public MainDataContext()
      {
          OnExpandedActionInViewModel = new Action<object, RoutedEventArgs>(OnExpanded);
      }
      
      public Action<object, RoutedEventArgs> OnExpandedActionInViewModel
      {
          get { return _onExpandedActionInViewModel; }
          private set
          {
              _onExpandedActionInViewModel = value;
              OnPropertyChanged();
          }
      }
      
    3. 行为命令属性:

      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); }
      }
      
    4. 行为代码:

          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();
      }
      
    5. 我希望它会对你有所帮助。 的问候,

答案 1 :(得分:1)

要创建绑定到TreeView,您必须使用RelativeSource Self,但不能使用Source with x:Reference。 例如,我将TreeView的宽度绑定到高度:

<TreeView Name="tv" Width="{Binding Height, RelativeSource={RelativeSource Self}}">

</TreeView>

或者您也可以使用ElementName,如下所示:

<TreeView Name="tv" Width="{Binding Height, ElementName=tv}">

</TreeView>

答案 2 :(得分:0)

我知道这个问题很老,但有一个不需要任何自定义代码的明显解决方案。

当使用 i:EventTrigger 时,您可以指定监听事件的对象,如下所示,我使用 RelativeSource 绑定在可视化树中向上走并找到 TreeViewItem

此外,您可以使用 ElementName 按名称绑定到元素,这不会导致循环依赖,因为当控件已经创建和初始化时绑定在调度程序上工作(不像 x:Reference,它必须在 XAML 反序列化期间解析)。

<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>