从另一个控件的HierarchicalDataTemplate设置一个控件的属性

时间:2011-02-16 18:45:37

标签: wpf datagrid treeview binding

我有一个像这样的视图模型:

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'。我如何使这个工作,我做错了什么?

2 个答案:

答案 0 :(得分:2)

无法找到dataGrid名称的原因是您尝试访问位于不同Name Scope的元素,这意味着数据模板中的元素无法“看到”元素在那个模板之外。

要解决此问题,您可以执行以下操作。对于UserControl(或Window,您需要另一个视图模型,包括TreeViewDataGrid。让我们称之为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");
        }
    }
}

将此视图模型的实例设置为包含DataContextUserControl的{​​{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)

以下是我对此类情况的常用解决方案:

  1. 首先,我总是有一个包含根节点集合的VM。我们将它命名为RootVM。此VM包含根节点和SelectedNode属性。
  2. 您的网格视图数据源应绑定到RootVM.SelectedNode.GridData
  3. 然后,您必须将树中的选定节点绑定到SelectedNode属性。这里的问题是它不能直接绑定(树视图中的SelectedItem属性是只读的),但这可以解决。我更喜欢使用附加行为,有些人更喜欢继承treeView - 这取决于你。
  4. 这似乎是如何让它发挥作用的好方法。