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;
}
}
}
Любая помощь приветствуется :-)