我有一个像这样的视图模型:
public abstract class ViewModelBase : INotifyPropertyChanged {
public abstract string Header { get; }
public abstract IEnumerable<ViewModelBase> Nodes { get; }
public abstract IEnumerable GridData { get; }
// following are hooked up to PropertyChanged event in the standard way
public bool IsSelected { get; set; }
public bool IsExpanded { get; set; }
}
这表示TreeView中的节点。现在,每当选择项更改时,我希望将所选项的GridData
属性复制到单独的DataGrid的ItemSource属性。
我有一个应该做我想做的DataTrigger Setter:
<DataGrid Name="dataGrid" />
<TreeView Name="treeView" ItemsSource="{Binding}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate DataType="{x:Type ViewModel:ViewModelBase}" ItemsSource="{Binding Nodes}">
<TextBlock Text="{Binding Header}" />
<HierarchicalDataTemplate.Triggers>
<DataTrigger Binding="{Binding Path=IsSelected}" Value="True">
<Setter TargetName="dataGrid" Property="DataContext" Value="{Binding GridData}" />
</DataTrigger>
</HierarchicalDataTemplate.Triggers>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
</Style>
</TreeView.ItemContainerStyle>
</TreeView>
然而,这是一个错误'无法找到触发器目标'dataGrid'。我如何使这个工作,我做错了什么?
答案 0 :(得分:2)
无法找到dataGrid
名称的原因是您尝试访问位于不同Name Scope的元素,这意味着数据模板中的元素无法“看到”元素在那个模板之外。
要解决此问题,您可以执行以下操作。对于UserControl
(或Window
,您需要另一个视图模型,包括TreeView
和DataGrid
。让我们称之为MyTreeViewModel
:
public class MyTreeViewModel : INotifyPropertyChanged // Better to implement this interface in some base class for all your view models
{
private TreeItemViewModelBase _selectedNode;
public IEnumerable<TreeItemViewModelBase> TopLevelNodes
{
get { yield return new TopLevelTreeNodeViewModel(); // Subclass of your base class for tree nodes (ViewModelBase) }
}
public TreeItemViewModelBase SelectedNode
{
get { return _selectedNode; }
set
{
_selectedNode = value;
RaisePropertyChanged("SelectedNode");
}
}
}
将此视图模型的实例设置为包含DataContext
和UserControl
的{{1}}的{{1}}。然后,您的XAML将如下所示:
TreeView
然后,您需要修改ViewModelBase(我建议将其重命名为DataGrid
以避免混淆):
<DataGrid Name="dataGrid" ItemsSource="{Binding SelectedNode.GridData}" />
<TreeView Name="treeView" ItemsSource="{Binding TopLevelNodes}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate DataType="{x:Type ViewModel:ViewModelBase}" ItemsSource="{Binding Nodes}">
<TextBlock Text="{Binding Header}" />
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
</Style>
</TreeView.ItemContainerStyle>
</TreeView>
如您所见,它维护对父视图模型的引用,并且只要TreeItemViewModelBase
设置为public abstract class TreeItemViewModelBase : INotifyPropertyChanged
{
private bool _isSelected;
public TreeItemViewModelBase(MyTreeViewModel parentViewModel)
{
ParentViewModel = parentViewModel;
}
public abstract string Header { get; }
public abstract IEnumerable<TreeItemViewModelBase> Nodes { get; }
public abstract IEnumerable GridData { get; }
public MyTreeViewModel ParentViewModel { get; private set; }
// following are hooked up to PropertyChanged event in the standard way
public bool IsSelected
{
get { return _isSelected; }
set
{
_isSelected = value;
RaisePropertyChanged("IsSelected");
if (_isSelected) {
ParentViewModel.SelectedNode = this;
}
}
}
public bool IsExpanded { get; set; }
}
,它就会在父视图模型上将其自身设置为IsSelected
,从而触发数据绑定在true
SelectedNode
上刷新。
希望这有帮助。
答案 1 :(得分:0)
以下是我对此类情况的常用解决方案:
RootVM
。此VM包含根节点和SelectedNode
属性。 RootVM.SelectedNode.GridData
SelectedNode
属性。这里的问题是它不能直接绑定(树视图中的SelectedItem
属性是只读的),但这可以解决。我更喜欢使用附加行为,有些人更喜欢继承treeView - 这取决于你。这似乎是如何让它发挥作用的好方法。