Установить свойство одного элемента управления из HierarchicalDataTemplate другого - PullRequest
2 голосов
/ 16 февраля 2011

У меня есть видовая модель так:

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 выбранного элемента было скопировано в отдельное свойство ItemSrid DataGrid.

У меня есть 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 ]

2 голосов
/ 16 февраля 2011

Причина, по которой имя dataGrid не может быть найдено, заключается в том, что вы пытаетесь получить доступ к элементу, расположенному в другой области имен , что означает, что элементы в шаблоне данных не могут "видеть" элементы которые находятся за пределами этого шаблона.

Чтобы исправить это, вы можете сделать следующее. Вам понадобится другая модель представления для вашего 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, который содержит вас TreeView и DataGrid. Тогда ваш XAML будет выглядеть так:

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

Затем вам нужно изменить ViewModelBase (я предлагаю переименовать его в 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 в модели родительского представления, что вызывает привязку данных к DataGrid s ItemsSource чтобы обновить.

Надеюсь, это поможет.

0 голосов
/ 16 февраля 2011

Вот мое обычное решение для такой ситуации:

  1. Прежде всего, у меня всегда есть виртуальная машина, которая содержит коллекцию корневых узлов. Давайте назовем это RootVM. Эта виртуальная машина содержит корневые узлы и свойство SelectedNode.
  2. Ваш источник данных вида сетки должен быть привязан к RootVM.SelectedNode.GridData
  3. Затем необходимо связать выбранный узел в дереве со свойством SelectedNode. Проблема в том, что его нельзя связать напрямую (свойство SelectedItem в виде дерева доступно только для чтения), но это можно решить. Я предпочитаю использовать прикрепленное поведение для этого, некоторые предпочитают наследовать treeView - решать только вам.

Кажется, это хороший способ заставить его работать.

...