【问题标题】:how to know which treeview item is clicked using mvvm如何知道使用 mvvm 单击了哪个树视图项
【发布时间】:2013-08-18 13:15:33
【问题描述】:

我有一个 WPF MVVM 应用程序,它有一个 TreeView,所有静态项目都保存在 XAML 页面中。我如何知道在我的视图模型中单击了哪个 MenuItem,以便我可以相应地显示相应的页面。

    <TreeView Height="Auto" HorizontalAlignment="Stretch" Margin="0" Name="MyTreeViewMenu" 
                      VerticalAlignment="Stretch" Width="Auto" Opacity="1" 
                     BorderThickness="1" BorderBrush="Black" Grid.Row="2">

        <TreeViewItem Header="Country" Width="Auto" HorizontalAlignment="Stretch" 
                      ></TreeViewItem>

        <TreeViewItem Header="View Details" Width="Auto" HorizontalAlignment="Stretch" IsEnabled="False">
                <TreeViewItem Header="User" />
                <TreeViewItem Header="Group" />
                <TreeViewItem Header="User Group" />
            </TreeViewItem>
    </TreeView>

【问题讨论】:

    标签: wpf mvvm


    【解决方案1】:

    我想Selected 事件在您的情况下与click 具有相同的效果。要确定选择了哪一个TreeViewItem,您应该添加事件Trigger

    <TreeView Height="Auto" HorizontalAlignment="Stretch" Margin="0" Name="MyTreeViewMenu" 
                          VerticalAlignment="Stretch" Width="Auto" Opacity="1" 
                         BorderThickness="1" BorderBrush="Black" Grid.Row="2">
    
            <TreeViewItem Header="Country" Width="Auto" HorizontalAlignment="Stretch"></TreeViewItem>    
            <TreeViewItem Header="View Details" Width="Auto" HorizontalAlignment="Stretch" IsEnabled="False">
                    <TreeViewItem Header="User" />
                    <TreeViewItem Header="Group" />
                    <TreeViewItem Header="User Group" />
                </TreeViewItem>
                   <i:Interaction.Triggers>
                      <i:EventTrigger EventName="SelectedItemChanged">
                          <i:InvokeCommandAction 
                             Command="{Binding selectItemCommand}"
                             CommandParameter="{Binding SelectedItem, ElementName=MyTreeViewMenu}"/>
                      </i:EventTrigger>
                  </i:Interaction.Triggers>
    </TreeView>
    

    因此,您可以使用并确定通过传递给Command 的参数选择了哪个项目。

    ViewModel 应该如下所示:

    private ICommand _selectItemCommand;
    public ICommand selectItemCommand
    {
        get
        {
            return _selectItemCommand ?? (_selectItemCommand = new RelayCommand(param => this.LoadPage(param)));
        }
    }
    
    private void LoadPage(object selectedMenuItem)
    {
          ...
    }
    

    【讨论】:

    • TreeViewItem.Selected 是一个事件,不是吗?如何使用StaticResourceCommand 与事件匹配?此外,该请求是针对 MVVM 解决方案的。
    • 在我的视图模型中添加了命令和属性。尽管在菜单项选择上调用了命令,但如何获取 selectedmenuItem ?见下文 cmets。
    • 私有字符串_SelectedItem;公共字符串 SelectedItem { 获取 { 返回 _SelectedItem; } 设置 { _SelectedItem = 值; FirePropertyChanged("SelectedItem"); } } 私有 ICommand _selectItemCommand;公共 ICommand selectItemCommand { get { return _selectItemCommand ?? (_selectItemCommand = new DelegateCommand(() => { LoadPage(); })); } }
    • @user2519971 在我的示例中,有一个CommandParameter 代表SelectedItem,它将传递给您的方法。使用无参数方法,这将不起作用。您必须修改命令以具有相应的参数。
    【解决方案2】:

    查看 MSDN 上的 TreeView.SelectedItem Property 页面。

    您可以直接绑定到TreeView.SelectedItem 属性:

    <TreeView ItemsSource="{Binding Items}" SelectedItem="{Binding Item, Mode=OneWay}" />
    

    注意TreeView.SelectedItem 属性是只读的,所以您必须使用OneWay 绑定...这意味着您不能从视图模型中设置选定项。为此,您需要使用Attached Property 创建自己的双向选定项属性。

    编辑>>>

    抱歉@Scroog1,我通常使用AttachedProperty 来执行此操作。您是对的,即使使用OneWay 绑定,使用此方法也会出错。不幸的是,我的AttachedProperty 代码很长,但还有另一种方法。

    我不一定推荐这样做,因为将 UI 属性放入数据对象从来都不是一个好主意,但如果您将 IsSelected 属性添加到数据对象,则可以将其直接绑定到 @987654333 @属性:

    <TreeView ItemsSource="Items" HorizontalAlignment="Stretch" ... Name="MyTreeViewMenu">
        <TreeView.ItemContainerStyle>
            <Style TargetType="{x:Type TreeViewItem}">
                <Setter Property="IsSelected" Value="{Binding IsSelected}" />
            </Style>
        </TreeView.ItemContainerStyle>
    </TreeView>
    

    我刚刚在 StackOverflow 上的 WPF MVVM TreeView SelectedItem 帖子中搜索并为您找到了一个“更全面”的答案。

    另外,还有另一种方法...您也可以使用TreeView.SelectedValueTreeView.SelectedValuePath 属性。基本思想是将TreeView.SelectedValuePath 属性设置为数据对象上的属性名称。当一个项目被选中时,TreeView.SelectedValue 属性将被设置为所选数据项的该属性的值。您可以从 MSDN 的 How to: Use SelectedValue, SelectedValuePath, and SelectedItem 页面了解有关此方法的更多信息。如果您具有唯一可识别的属性(例如某种标识符),这通常最有效。此代码示例来自 MSDN:

    <TreeView ItemsSource="{Binding Source={StaticResource myEmployeeData}, 
    XPath=EmployeeInfo}" Name="myTreeView" SelectedValuePath="EmployeeNumber" />
    
    <TextBlock Margin="10">SelectedValuePath: </TextBlock>
    <TextBlock Margin="10,0,0,0" Text="{Binding ElementName=myTreeView, 
    Path=SelectedValuePath}" Foreground="Blue"/>
    
    <TextBlock Margin="10">SelectedValue: </TextBlock>
    <TextBlock Margin="10,0,0,0" Text="{Binding ElementName=myTreeView, 
    Path=SelectedValue}" Foreground="Blue"/>
    

    【讨论】:

    • 我在尝试此操作时收到错误 'SelectedItem' property is read-only and cannot be set from markup.
    • 请正确阅读我的回答以解决您的问题...请注意“您必须使用 OneWay 绑定”。
    • 我使用的是单向绑定。
    【解决方案3】:

    除了绑定到 TreeView.SelectedItem 属性:

    在使用 MVVM 时,它帮助我停止思考 UI 中的事件并开始思考 UI 中的状态。

    您可以将 ViewModel 绑定到 View 的属性。所以通常我会尝试将 SelectedItem 绑定到 ViewModel 上的属性,以便 ViewModel 知道选择了什么。

    以同样的方式,您可以向正在显示的 ViewModel 项目添加一个名为 Selected 的属性,并将该属性绑定到视图中的复选框。这样您就可以在 ViewModel 中轻松启用多项选择并访问选定的项目。

    【讨论】:

      【解决方案4】:

      为了完整起见,这里是附加属性和 TreeView 子类选项:

      附加属性选项

      public static class TreeViewSelectedItemHelper
      {
          public static readonly DependencyProperty BindableSelectedItemProperty
              = DependencyProperty.RegisterAttached(
                  "BindableSelectedItem",
                  typeof (object),
                  typeof (TreeViewSelectedItemHelper),
                  new FrameworkPropertyMetadata(false,
                                                OnSelectedItemPropertyChanged)
                      {
                          BindsTwoWayByDefault = true
                      });
      
          public static object GetBindableSelectedItem(TreeView treeView)
          {
              return treeView.GetValue(BindableSelectedItemProperty);
          }
      
          public static void SetBindableSelectedItem(
              TreeView treeView, 
              object selectedItem)
          {
              treeView.SetValue(BindableSelectedItemProperty, selectedItem);
          }
      
          private static void OnSelectedItemPropertyChanged(
              DependencyObject sender,
              DependencyPropertyChangedEventArgs args)
          {
              var treeView = sender as TreeView;
              if (treeView == null) return;
              SetBindableSelectedItem(treeView, args.NewValue);
              treeView.SelectedItemChanged -= HandleSelectedItemChanged;
              treeView.SelectedItemChanged += HandleSelectedItemChanged;
              if (args.OldValue != args.NewValue)
                  SetSelected(treeView, args.NewValue);
          }
      
          private static void SetSelected(ItemsControl treeViewItem,
                                          object itemToSelect)
          {
              foreach (var item in treeViewItem.Items)
              {
                  var generator = treeViewItem.ItemContainerGenerator;
                  var child = (TreeViewItem) generator.ContainerFromItem(item);
                  if (child == null) continue;
                  child.IsSelected = (item == itemToSelect);
                  if (child.HasItems) SetSelected(child, itemToSelect);
              }
          }
      
          private static void HandleSelectedItemChanged(
              object sender,
              RoutedPropertyChangedEventArgs<object> args)
          {
              if (args.NewValue is TreeViewItem) return;
              var treeView = sender as TreeView;
              if (treeView == null) return;
              var binding = BindingOperations.GetBindingExpression(treeView,
                  BindableSelectedItemProperty);
              if (binding == null) return;
              var propertyName = binding.ParentBinding.Path.Path;
              var property = binding.DataItem.GetType().GetProperty(propertyName);
              if (property != null)
                  property.SetValue(binding.DataItem, treeView.SelectedItem, null);
          }
      }
      

      子类选项

      public class BindableTreeView : TreeView
      {
          public BindableTreeView()
          {
              SelectedItemChanged += HandleSelectedItemChanged;
          }
      
          public static readonly DependencyProperty BindableSelectedItemProperty =
              DependencyProperty.Register(
                  "BindableSelectedItem",
                  typeof (object),
                  typeof (BindableTreeView),
                  new FrameworkPropertyMetadata(
                      default(object),
                      OnBindableSelectedItemChanged) {BindsTwoWayByDefault = true});
      
          public object BindableSelectedItem
          {
              get { return GetValue(BindableSelectedItemProperty); }
              set { SetValue(BindableSelectedItemProperty, value); }
          }
      
          private static void OnBindableSelectedItemChanged(
              DependencyObject d,
              DependencyPropertyChangedEventArgs e)
          {
              var treeView = d as TreeView;
              if (treeView != null) SetSelected(treeView, e.NewValue);
          }
      
          private static void SetSelected(ItemsControl treeViewItem,
                                          object itemToSelect)
          {
              foreach (var item in treeViewItem.Items)
              {
                  var generator = treeViewItem.ItemContainerGenerator;
                  var child = (TreeViewItem) generator.ContainerFromItem(item);
                  if (child == null) continue;
                  child.IsSelected = (item == itemToSelect);
                  if (child.HasItems) SetSelected(child, itemToSelect);
              }
          }
      
          private void HandleSelectedItemChanged(
              object sender,
              RoutedPropertyChangedEventArgs<object> e)
          {
              SetValue(BindableSelectedItemProperty, SelectedItem);
          }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-03-18
        • 2013-10-26
        相关资源
        最近更新 更多