嵌套有限深度的视觉元素
本文关键字:视觉 元素 深度 嵌套 | 更新日期: 2023-09-27 18:30:09
我有一个视图模型列表,我想在ItemControl 中显示它
ItemsControl的ItemPanel设置为<Canvas>
,元素是不同类型的ViewModel,它们都继承自具有Left
、Top
、Width
、Height
和Elements
属性的一个ViewModel(ElementViewModel)。元素的位置由样式设置:
<ItemsControl ItemsSource="{Binding Elements}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style TargetType="ContentPresenter">
<Setter Property="Canvas.Left" Value="{Binding Left}"/>
<Setter Property="Canvas.Top" Value="{Binding Top}"/>
<Setter Property="Width" Value="{Binding Width}"/>
<Setter Property="Height" Value="{Binding Height}"/>
</Style>
</ItemsControl.ItemContainerStyle>
</ItemsControl>
项目属于不同类型,视图由DataTemplates 检索
<DataTemplate DataType="{x:Type local:FirstTypeElementViewModel}">
<local:firstElementType />
</DataTemplate>
<DataTemplate DataType="{x:Type local:SecondTypeElementViewModel}">
<local:secondElementType />
</DataTemplate>
现在的问题是,我想在有限的深度内显示这些元素的子元素。我试图将ItemsControl移到一个自定义控件中,然后将其嵌入到我的每个元素视图中,但这种情况下的可重复性并不受任何限制,这会导致显示带有所有子元素的所有元素和较差的性能。
我该如何实现我的目标?
我们有一个树数据结构,希望显示它,但不超过特定级别。正确的
首先,我们需要以某种方式获得元素的深度——它与根部的距离。让我们的元素具有额外的属性int Depth
。
public class ElementViewModel : INotifyPropertyChanged
{
public int Depth {get;set;}
public IEnumerable<ElementViewModel> Elements {get; set;}
...
}
请注意,当VM未实现INotifyPropertyChanged
或未从实现相应DependecnyProperty
的DependencyObject
继承时,可能会发生内存泄漏。
那么,接下来是什么呢?多重绑定。与简单的绑定不同,MultiBinding允许您在IMultiValueConverter的帮助下将多个值转换为一个值。
我们为什么需要它?这个想法很简单,当达到所需的深度级别时,我们希望返回嵌套元素的空集合。
public class ElementsConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
var elements = values[0] as IEnumerable<ElementViewModel>;
var depth = (int)values[1];
if (depth <= 9) // depthLimit can be passed through parameter (MultiBinding.ConverterParameter property) or via AmbientContext. Actually many ways exist.
{
return elements;
}
else
{
return new ElementViewModel[0];
}
}
public object[] ConverBack(bject value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
<ItemsControl>
<ItemsControl.ItemsSource>
<MultiBinding Mode="OneWay">
<MultiBinding.Converter>
<localNamespace:ElementsConverter/>
</MultiBinding.Converter>
<Binding Path="Elements"/>
<Binding Path="Depth"/>
</MultiBinding>
</ItemsControl.ItemsSource>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style TargetType="ContentPresenter">
<Setter Property="Canvas.Left" Value="{Binding Left}"/>
<Setter Property="Canvas.Top" Value="{Binding Top}"/>
<Setter Property="Width" Value="{Binding Width}"/>
<Setter Property="Height" Value="{Binding Height}"/>
</Style>
</ItemsControl.ItemContainerStyle>
</ItemsControl>