我对这个设计持怀疑态度。为什么您认为将单个对象及其属性值呈现为该对象具有某种层次结构对用户有用?
如果你只是想强加一些
结构,无需使用
TreeView
class TableItem
{
public string Property1 { get; set; }
public string Property2 { get; set; }
public string Property3 { get; set; }
public TableItem() { }
public TableItem(string property1, string property2, string property3)
{
Property1 = property1;
Property2 = property2;
Property3 = property3;
}
}
class ViewModel
{
public ObservableCollection<TableItem> TableItems { get; } = new ObservableCollection<TableItem>();
}
<Window x:Class="TestSO46300831HiearchicalObservable.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:l="clr-namespace:TestSO46300831HiearchicalObservable"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<l:ViewModel>
<l:ViewModel.TableItems>
<l:TableItem Property1="Item #1, property #1"
Property2="Item #1, property #2"
Property3="Item #1, property #3"/>
<l:TableItem Property1="Item #2, property #1"
Property2="Item #2, property #2"
Property3="Item #2, property #3"/>
<l:TableItem Property1="Item #3, property #1"
Property2="Item #3, property #2"
Property3="Item #3, property #3"/>
</l:ViewModel.TableItems>
</l:ViewModel>
</Window.DataContext>
<Window.Resources>
<DataTemplate DataType="{x:Type l:TableItem}">
<StackPanel>
<TextBlock Text="{Binding Property1}"/>
<TextBlock Text="{Binding Property1}" Margin="10,0,0,0"/>
<TextBlock Text="{Binding Property1}" Margin="20,0,0,0"/>
</StackPanel>
</DataTemplate>
</Window.Resources>
<StackPanel>
<ListBox ItemsSource="{Binding TableItems}"/>
</StackPanel>
</Window>
也就是说,如果你
使用
树状视图
INotifyCollectionChanged
ObservableCollection<T>
并追踪原始收藏。需要中间集合,以便可以将项目从原始的单个对象项目转换为可与
班例如:
class HierarchicalTableItem
{
public string Text { get; }
public IReadOnlyList<HierarchicalTableItem> Items { get; }
public HierarchicalTableItem(string text, HierarchicalTableItem child = null)
{
Text = text;
Items = child != null ? new[] { child } : null;
}
}
class ViewModel
{
public ICommand AddCommand { get; }
public ICommand InsertCommand { get; }
public ICommand RemoveCommand { get; }
public int Index { get; set; }
public ObservableCollection<TableItem> TableItems { get; } = new ObservableCollection<TableItem>();
public ViewModel()
{
AddCommand = new DelegateCommand(() => TableItems.Add(_CreateTableItem()));
InsertCommand = new DelegateCommand(() => TableItems.Insert(Index, _CreateTableItem()));
RemoveCommand = new DelegateCommand(() => TableItems.RemoveAt(Index));
}
private int _itemNumber;
private TableItem _CreateTableItem()
{
_itemNumber = (_itemNumber < TableItems.Count ? TableItems.Count : _itemNumber) + 1;
return new TableItem(
$"Item #{_itemNumber}, property #1",
$"Item #{_itemNumber}, property #2",
$"Item #{_itemNumber}, property #3");
}
}
class ConvertingObservableCollection<T> : ObservableCollection<object>
{
private readonly IValueConverter _converter;
private readonly ObservableCollection<T> _collection;
public ConvertingObservableCollection(IValueConverter converter, ObservableCollection<T> collection)
{
_converter = converter;
_collection = collection;
_ResetItems();
_collection.CollectionChanged += _OnCollectionChanged;
}
private void _OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
_AddItems(e);
break;
case NotifyCollectionChangedAction.Move:
_RemoveItems(e);
_AddItems(e);
break;
case NotifyCollectionChangedAction.Remove:
_RemoveItems(e);
break;
case NotifyCollectionChangedAction.Replace:
_ReplaceItems(e);
break;
case NotifyCollectionChangedAction.Reset:
_ResetItems();
break;
}
}
private void _ReplaceItems(NotifyCollectionChangedEventArgs e)
{
for (int i = 0; i < e.NewItems.Count; i++)
{
this[i] = _Convert(e.NewItems[i]);
}
}
private void _AddItems(NotifyCollectionChangedEventArgs e)
{
for (int i = 0; i < e.NewItems.Count; i++)
{
Insert(i + e.NewStartingIndex, _Convert(e.NewItems[i]));
}
}
private void _RemoveItems(NotifyCollectionChangedEventArgs e)
{
for (int i = e.OldItems.Count - 1; i >= 0; i--)
{
RemoveAt(i + e.OldStartingIndex);
}
}
private void _ResetItems()
{
Clear();
foreach (T t in _collection)
{
Add(_Convert(t));
}
}
private object _Convert(object value)
{
return _converter.Convert(value, typeof(T), null, null);
}
}
class TableItemHierarchicalConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
TableItem tableItem = value as TableItem;
if (tableItem == null)
{
return Binding.DoNothing;
}
return new HierarchicalTableItem(tableItem.Property1,
new HierarchicalTableItem(tableItem.Property2,
new HierarchicalTableItem(tableItem.Property3)));
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
class ConvertingCollectionConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
IValueConverter converter = parameter as IValueConverter;
if (converter == null || value == null ||
value.GetType().GetGenericTypeDefinition() != typeof(ObservableCollection<>))
{
return Binding.DoNothing;
}
Type resultType = typeof(ConvertingObservableCollection<>).MakeGenericType(value.GetType().GenericTypeArguments);
return Activator.CreateInstance(resultType, converter, value);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
<Window x:Class="TestSO46300831HiearchicalObservable.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:l="clr-namespace:TestSO46300831HiearchicalObservable"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<l:ViewModel>
<l:ViewModel.TableItems>
<l:TableItem Property1="Item #1, property #1"
Property2="Item #1, property #2"
Property3="Item #1, property #3"/>
<l:TableItem Property1="Item #2, property #1"
Property2="Item #2, property #2"
Property3="Item #2, property #3"/>
<l:TableItem Property1="Item #3, property #1"
Property2="Item #3, property #2"
Property3="Item #3, property #3"/>
</l:ViewModel.TableItems>
</l:ViewModel>
</Window.DataContext>
<Window.Resources>
<l:ConvertingCollectionConverter x:Key="convertingCollectionConverter1"/>
<l:TableItemHierarchicalConverter x:Key="tableItemConverter1"/>
</Window.Resources>
<ScrollViewer>
<StackPanel>
<UniformGrid Columns="4">
<Button Content="Add" Command="{Binding AddCommand}"/>
<Button Content="Insert" Command="{Binding InsertCommand}"/>
<Button Content="Remove" Command="{Binding RemoveCommand}"/>
<TextBox Text="{Binding Index}"/>
</UniformGrid>
<TreeView ItemsSource="{Binding TableItems,
Converter={StaticResource convertingCollectionConverter1},
ConverterParameter={StaticResource tableItemConverter1}}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Items}">
<TextBlock Text="{Binding Text}"/>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
</StackPanel>
</ScrollViewer>
</Window>
-
ConvertingObservableCollection<T>
-这可以查看原始收藏并根据原始收藏的当前状态显示转换的项目。
-
ConvertingCollectionConverter
-这会将原始集合转换为
ConvertingObservableCollection<T>
对象以绑定到
树状视图
-
TableItemHierarchicalConverter
树状视图
当然,还有一个简单的容器类
HierarchicalTableItem
用于表示每个表项的层次结构。
归根结底,要记住的关键是
树状视图
你
必须
HierarchicalDataTemplate
元素可用于定义如何显示
每个级别
在树上。这意味着单个数据项必须具有可用于
ItemsSource
模板本身是相同类型数据项的某种类型的集合。