wpf 如何使用 mvvm 知道单击了哪个树视图项
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/18268103/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
how to know which treeview item is clicked using mvvm
提问by user2519971
I am having a WPF MVVM application which has a TreeViewwith all the static items maintained in XAML page. How do I know in my view-model which MenuItem is clicked so that I can show that respective page accordingly.
我有一个 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>
回答by Anatolii Gabuza
I suppose that Selectedevent will have same effect as a clickin your case. To determine which one TreeViewItemwas selected you should add event Trigger:
我想该Selected事件与click您的情况具有相同的效果。要确定TreeViewItem选择了哪一个,您应该添加 event 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>
As a result you can use and determine which item was selected by a parameter passed to Command.
因此,您可以使用并确定传递给 的参数选择了哪个项目Command。
ViewModelshould look something like this:
ViewModel应该是这样的:
private ICommand _selectItemCommand;
public ICommand selectItemCommand
{
get
{
return _selectItemCommand ?? (_selectItemCommand = new RelayCommand(param => this.LoadPage(param)));
}
}
private void LoadPage(object selectedMenuItem)
{
...
}
回答by Sheridan
Take a look at the TreeView.SelectedItem Propertypage at MSDN.
查看MSDN上的TreeView.SelectedItem 属性页。
You can bind directly to the TreeView.SelectedItemproperty:
您可以直接绑定到该TreeView.SelectedItem属性:
<TreeView ItemsSource="{Binding Items}" SelectedItem="{Binding Item, Mode=OneWay}" />
Note that the TreeView.SelectedItemproperty is only read only, so you mustuse a OneWaybinding... this means that you cannot set the selected item from your view model. To do that, you will need to create your own two way selected item property using an Attached Property.
请注意,该TreeView.SelectedItem属性是只读的,因此您必须使用OneWay绑定...这意味着您无法从视图模型中设置所选项目。为此,您需要使用Attached Property.
EDIT >>>
编辑 >>>
My apologies @Scroog1, I normally use an AttachedPropertyto do this. You are right that even with a OneWaybinding, there is an error using this method. Unfortuately, my AttachedPropertycode is long, but there is another way to do this.
我很抱歉@Scroog1,我通常使用 anAttachedProperty来执行此操作。您是对的,即使使用OneWay绑定,使用此方法也会出错。不幸的是,我的AttachedProperty代码很长,但还有另一种方法可以做到这一点。
I wouldn't necessarily recommend this as it's never really a good idea to put UI properties into your data objects, but if you add an IsSelectedproperty to your data object, then you can bind it directly to the TreeViewItem.IsSelectedproperty:
我不一定推荐这样做,因为将 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>
I just searched and found a 'fuller' answer for you in the WPF MVVM TreeView SelectedItempost here on StackOverflow.
我刚刚在 StackOverflow 上的WPF MVVM TreeView SelectedItem帖子中搜索并找到了一个“更完整”的答案。
Alternatively, there is another way... you could also use the TreeView.SelectedValueand TreeView.SelectedValuePathproperties. The basic idea is to set the TreeView.SelectedValuePathproperty to the name of a property on your data object. When an item is selected, the TreeView.SelectedValueproperty will then be set to the value of that property of the selected data item. You can find out more about this method from the How to: Use SelectedValue, SelectedValuePath, and SelectedItempage at MSDN. This generally works best if you have a uniquely identifiable property like an identifier of some kind. This code example is from MSDN:
或者,还有另一种方法……您也可以使用TreeView.SelectedValue和TreeView.SelectedValuePath属性。基本思想是将TreeView.SelectedValuePath属性设置为数据对象上的属性名称。当一个项目被选中时,该TreeView.SelectedValue属性将被设置为所选数据项的该属性的值。您可以从MSDN 上的如何:使用 SelectedValue、SelectedValuePath 和 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"/>
回答by Erno
In addition to binding to the TreeView.SelectedItem property:
除了绑定到 TreeView.SelectedItem 属性:
When using MVVM it helped me to stop thinking about events in the UI and start thinking about state in the UI.
使用 MVVM 时,它帮助我停止考虑 UI 中的事件,而开始考虑 UI 中的状态。
You can bind the ViewModel to properties of the View. So in general I try to bind a SelectedItem to a property on the ViewModel so the ViewModel knows what is selected.
您可以将 ViewModel 绑定到 View 的属性。所以一般来说,我尝试将 SelectedItem 绑定到 ViewModel 上的一个属性,以便 ViewModel 知道选择了什么。
In the same way you could add a property to the ViewModel items being shown called Selected and bind this property to a checkbox in the View. That way you can enable multiple selection and access the selected items easily within the ViewModel.
以同样的方式,您可以向显示的 ViewModel 项添加一个名为 Selected 的属性,并将此属性绑定到视图中的复选框。这样您就可以在 ViewModel 中启用多项选择并轻松访问所选项目。
回答by Scroog1
For completeness, here are the attached property and TreeView subclass options:
为了完整起见,这里是附加的属性和 TreeView 子类选项:
Attached property option
附加属性选项
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);
}
}
Subclass option
子类选项
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);
}
}

