Синхронизация TreeView с выбранным элементом в модели просмотра

У меня есть ViewModel поверх элемента управления WPF TreeView. Я хочу, чтобы ViewModel мог устанавливать и читать SelectedItem из TreeView. Однако свойство SelectedItem TreeView не может быть привязано.

Я могу установить и получить выбранный элемент в коде позади (используя ItemContainerGenerator и TreeViewItem.IsSelected = true), но это приводит к некоторому уродливому взаимодействию между кодом позади и ViewModel.

Есть ли у кого-нибудь чистое решение для этого?


person according2me    schedule 12.11.2009    source источник
comment
Вы когда-нибудь решали это? У меня именно такая проблема.   -  person BrettRobi    schedule 27.03.2010
comment
Я думаю, что этот вопрос дублирует stackoverflow.com/questions/ 1000040 /   -  person Nikita Martyanov    schedule 10.12.2012


Ответы (2)


Могу привести пример. Что я делаю, так это устанавливаю свойство IsSelected объекта TreeViewItem (а не самого TreeView) в модели представления, потому что вы можете привязаться к нему.

В моей модели представления у меня есть свойство ElementInViewModel, которое представляет собой структуру данных, которая сама формирует дерево. Я использую HierarchicalDataTemplate в своем Xaml для его отображения. Сам объект данных относится к типу YourDomainType, а его дочерние элементы (того же типа) находятся в его свойстве ChildElements.

В модели представления я установил свойства IsExpanded и IsSelected моего класса данных YourDomainType. Из-за стиля, определенного ниже, они передадут этот параметр в TreeViewItem.

Это работает для вас?

<UserControl>

    <UserControl.Resources>        
        <CollectionViewSource Source="{Binding Path=ElementInViewModel}" x:Key="Cvs">
        </CollectionViewSource>

        <HierarchicalDataTemplate DataType="{x:Type DomainModel:YourDomainType}"
                                  ItemsSource="{Binding Path=ChildElements}">
            <TextBlock Text="{Binding Path=Name}"/>            
        </HierarchicalDataTemplate>        

        <Style TargetType="{x:Type TreeViewItem}">
            <Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
            </Setter>
            <Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
            </Setter>            
        </Style>

    </UserControl.Resources>


    <DockPanel>
        <TreeView ItemsSource="{Binding Source={StaticResource Cvs}}"/>
    </DockPanel>

</UserControl>
person Martin    schedule 04.08.2015

Вы можете использовать какой-то прокси-класс для привязки свойства SelectedItem к свойству In и привязки свойства Out к вашей ViewModel:

    public class Proxy : FrameworkElement
    {
    public static readonly DependencyProperty InProperty;
    public static readonly DependencyProperty OutProperty;

    public Proxy()
    {
        Visibility = Visibility.Collapsed;
    }

    static Proxy()
    {
        var inMetadata = new FrameworkPropertyMetadata(
            delegate(DependencyObject p, DependencyPropertyChangedEventArgs args)
                {
                    if (null != BindingOperations.GetBinding(p, OutProperty))
                    {
                        var proxy = p as Proxy;
                        if (proxy != null)
                            proxy.Out = args.NewValue;
                    }
                });

        inMetadata.BindsTwoWayByDefault = false;
        inMetadata.DefaultUpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;

        InProperty = DependencyProperty.Register("In",
                                                 typeof (object),
                                                 typeof (Proxy),
                                                 inMetadata);

        var outMetadata = new FrameworkPropertyMetadata(
            delegate(DependencyObject p, DependencyPropertyChangedEventArgs args)
                {
                    ValueSource source = DependencyPropertyHelper.GetValueSource(p, args.Property);

                    if (source.BaseValueSource != BaseValueSource.Local)
                    {
                        var proxy = p as Proxy;
                        if (proxy != null)
                        {
                            var expected = proxy.In;
                            if (!ReferenceEquals(args.NewValue, expected))
                            {
                                Dispatcher.CurrentDispatcher.BeginInvoke(
                                    DispatcherPriority.DataBind, new Action(delegate
                                                                                {
                                                                                    proxy.Out = proxy.In;
                                                                                }));
                            }
                        }
                    }
                });

        outMetadata.BindsTwoWayByDefault = true;
        outMetadata.DefaultUpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;

        OutProperty = DependencyProperty.Register("Out",
                                                  typeof (object),
                                                  typeof (Proxy),
                                                  outMetadata);
    }

    public object In
    {
        get { return GetValue(InProperty); }
        set { SetValue(InProperty, value); }
    }

    public object Out
    {
        get { return GetValue(OutProperty); }
        set { SetValue(OutProperty, value); }
    }
}
<Proxy In="{Binding ElementName=Tree, Path=SelectedItem}" Out="{Binding SelectedItem, UpdateSourceTrigger=PropertyChanged}"/>
<TreeView x:Name="Tree" ItemsSource="{Binding Path=Items}"/>
person Andriy Vandych    schedule 27.12.2012