如何知道使用mvvm单击哪个树视图项

时间:2013-08-16 07:32:51

标签: wpf mvvm

我正在使用一个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>

4 个答案:

答案 0 :(得分:2)

我认为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)
{
      ...
}

答案 1 :(得分:2)

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

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

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

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

编辑&gt;&gt;&gt;

我的道歉@ Scroog1,我通常使用AttachedProperty来做到这一点。你是对的,即使使用OneWay绑定,使用此方法也会出错。不幸的是,我的AttachedProperty代码很长,但还有另一种方法可以做到这一点。

我不一定会推荐这个,因为将UI属性放入数据对象永远不是一个好主意,但如果向数据对象添加IsSelected属性,则可以将其直接绑定到数据对象TreeViewItem.IsSelected财产:

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

答案 2 :(得分:0)

除了绑定到TreeView.SelectedItem属性:

使用MVVM时,它帮助我不再考虑UI中的事件,并开始考虑UI中的状态。

您可以将ViewModel绑定到View的属性。因此,一般来说,我尝试将SelectedItem绑定到ViewModel上的属性,以便ViewModel知道所选内容。

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

答案 3 :(得分:0)

为了完整性,这里是附加属性和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);
    }
}