【问题标题】:How to bind expanded event of WPF treeview item to the viewmodel如何将 WPF 树视图项的扩展事件绑定到视图模型
【发布时间】:2015-10-18 09:30:04
【问题描述】:

我正在尝试将扩展事件绑定到视图模型(不是 *.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

有人可以帮我解决这个错误吗?或者有其他方法可以将事件绑定到视图模型中的命令吗?

【问题讨论】:

    标签: c# wpf mvvm data-binding treeview


    【解决方案1】:

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

    1. 树视图项和此事件存在一个小问题。
    2. 有几种方法可以解决这个问题:

    首先是 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();
      }
      

    希望对你有所帮助。 问候,

    【讨论】:

      【解决方案2】:

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

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

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

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

      【讨论】:

        【解决方案3】:

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

        当使用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>
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2012-09-29
          • 1970-01-01
          • 1970-01-01
          • 2023-03-06
          • 1970-01-01
          • 1970-01-01
          • 2017-05-22
          • 1970-01-01
          相关资源
          最近更新 更多