Различное расширенное / выбранное состояние в нескольких TreeView с одним и тем же ViewModel - PullRequest
0 голосов
/ 06 апреля 2020

TLDR: как управлять другим состоянием IsExpanded / IsSelected одного и того же ViewModel в нескольких представлениях?

Длинная версия: у меня есть хорошее работающее приложение с огромным TreeView. В приложении много функций, которые выбирают, раскрывают и сворачивают элементы TreeView. Таким образом, ViewModel содержит свойство IsExpanded / IsSelected для каждого TreeNode (связанного с TreeViewItem стилем). Теперь у меня есть требование избегать большой прокрутки в этом Дереве. Таким образом, идея состоит в том, чтобы предоставить второй TreeView (может быть больше ..) с элементом root, выбранным из первого дерева). Проблема заключается в том, что оба TreeView зависят от одних и тех же свойств IsExpanded / IsSelected, но я хочу по-разному управлять этими состояниями для каждого дерева), и я не знаю, как обновить IsExpanded / IsSelected logi c, чтобы он работал с несколькими TreeView. .

Все функции, которые управляют этими состояниями, знают, в каком дереве они используются, потому что они зависят от родительского TreeVM ViewModel. Я думал, что имеет IsExpanded / IsSelected ObservableCollection, но не могу найти хороший способ связать свойства TreeViewItem с коллекцией (двусторонний).

App.xaml:

<Application
    x:Class="MultiTree.App"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:MultiTree"
>
    <Application.Resources />
    <Application.MainWindow>
        <local:MainWindow />
    </Application.MainWindow>
</Application>

App.xaml.cs:

using System.Windows;

namespace MultiTree
{
    /// <summary>
    /// Interaction logic for App.xaml
    /// </summary>
    public partial class App : Application
    {
        protected override void OnStartup(StartupEventArgs e)
        {
            base.OnStartup(e);
            MainWindow.Loaded += (ws, we) => {
                MainWindow.DataContext = new AppVM();
            };
            MainWindow.Show();
        }
    }
}

MainWindow.xaml:

    <Window
    x:Class="MultiTree.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:MultiTree"
    Title="MainWindow"
    Width="800"
    Height="450">
    <Window.Resources />
    <ItemsControl ItemsSource="{Binding Views}">
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <UniformGrid Rows="1" />
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <TreeView
                    Margin="5"
                    Background="Gainsboro"
                    ItemsSource="{Binding Root.Nodes}">
                    <TreeView.ItemContainerStyle>
                        <Style TargetType="TreeViewItem">
                            <Setter Property="IsSelected" Value="{Binding IsSelected, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" />
                            <Setter Property="IsExpanded" Value="{Binding IsExpanded, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" />
                            <Style.Triggers>
                                <DataTrigger Binding="{Binding IsSelected, UpdateSourceTrigger=PropertyChanged}" Value="true">
                                    <Setter Property="Background" Value="Red" />
                                </DataTrigger>
                            </Style.Triggers>
                        </Style>
                    </TreeView.ItemContainerStyle>
                    <TreeView.ItemTemplate>
                        <HierarchicalDataTemplate ItemsSource="{Binding Nodes}">
                            <TextBlock Text="{Binding Name}" />
                        </HierarchicalDataTemplate>
                    </TreeView.ItemTemplate>
                </TreeView>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>
</Window>

AppVM.cs:

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;

namespace MultiTree
{

    public class Bindable : INotifyPropertyChanged
    {
        public void OnPropertyChanged([CallerMemberName] string propertyName = "")
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }

        public event PropertyChangedEventHandler PropertyChanged;
    }

    public class TreeNode : Bindable
    {
        public TreeNode(string name, IEnumerable<TreeNode> nodes = null)
        {
            Name = name;
            if (nodes == null)
                _nodes = new ObservableCollection<TreeNode>();
            else
                _nodes = new ObservableCollection<TreeNode>(nodes);
        }

        private string _name;

        public string Name
        {
            get => _name;
            set {
                _name = value;
                OnPropertyChanged();
            }
        }

        private bool _isSelected;

        public bool IsSelected
        {
            get => _isSelected;
            set {
                _isSelected = value;
                OnPropertyChanged();
            }
        }

        private bool _isExpanded;

        public bool IsExpanded
        {
            get => _isExpanded;
            set {
                _isExpanded = value;
                OnPropertyChanged();
            }
        }

        private ObservableCollection<TreeNode> _nodes;

        public ObservableCollection<TreeNode> Nodes => _nodes;
    }

    /// <summary>
    /// "Root" for each TreeView, known by all functions that expand/collapse&select
    /// </summary>
    public class TreeVM : Bindable
    {
        private TreeNode _root;

        public TreeNode Root
        {
            get => _root;
            set {
                _root = value;
                OnPropertyChanged();
            }
        }
    }

    public class AppVM : Bindable
    {
        /// <summary>
        /// Contains all TreeNodes
        /// </summary>
        private readonly TreeNode RootNode;

        public AppVM()
        {
            RootNode = new TreeNode("ROOT", new[] {
                new TreeNode("A", new [] {
                    new TreeNode("B", new [] {
                        new TreeNode("C", new [] {
                            new TreeNode("D")
                        })
                    }),
                    new TreeNode("E", new [] {
                        new TreeNode("F", new [] {
                            new TreeNode("G", new [] {
                                new TreeNode("H"),
                                new TreeNode("I")
                            })
                        })
                    })
                })
            });

            Views.Add(new TreeVM() { Root = RootNode });
            Views.Add(new TreeVM() { Root = RootNode.Nodes.First() });
        }

        private ObservableCollection<TreeVM> _views = new ObservableCollection<TreeVM>();

        /// <summary>
        /// Each Tree can contain all or a subset of TreeNodes
        /// </summary>
        public ObservableCollection<TreeVM> Views
        {
            get => _views;
        }
    }
}

Любая помощь приветствуется :-)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...