我有一个像这样的DataModel:
public class Node
{
public List<Node> Children { get; private set; }
public string Name { get; private set; }
public Node(string _name, params Node[] _children)
{
Name = _name;
Children = new List<Node>(_children);
}
}
我现在想要为这个模型定义一个视图(我没有使用TreeView,原因超出了这个问题的范围),这允许人们以两种方式之一使用它。
示例1:默认布局,内容应自动展开
<NodeView DataContext="{Binding Root}"/>
上面应该以与树视图相同的方式扩展节点树,即递归地沿着节点及其子节点创建每个节点的新视图。
示例2:允许其他人手动设置内容
<NodeView DataContext="{Binding Root}">
<StackPanel>
<TextBlock Text="{Binding Children[0].Name"/>
<TextBlock Text="{Binding Children[1].Name"/>
<TextBlock Text="{Binding Children[2].Name"/>
</StackPanel>
</NodeView>
上面的内容现在不会扩展,但只显示前三个子节点。
我以为我可以使用以下自定义控件执行此操作,但是我收到了stackoverflow异常,我做错了什么?
<Style TargetType="{x:Type l:NodeView}">
<Setter Property="Content">
<Setter.Value>
<GroupBox>
<ItemsControl ItemsSource="{Binding Children}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<l:NodeView />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</GroupBox>
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type l:NodeView}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="18" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock Text="{Binding Name}"
Grid.Column="1" />
<ContentPresenter Grid.Row="1"
Grid.Column="1" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
如果有人发现更容易使用,这是项目的链接 https://www.dropbox.com/s/j32mm7gave17v7j/NodeView.zip
答案 0 :(得分:0)
问题主要来自你的风格。您设置内容。但是,您应该指定Template和ItemTemplate。
第一个将描述NodeView的视觉效果:带有子列表的文本块。 第二个将描述您的子节点是如何可视化的:NodeView控件。
<Style TargetType="{x:Type l:NodeView}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type l:NodeView}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="18" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock Text="{Binding Name}"
Grid.Column="1" />
<ItemsControl Grid.Row="1" Grid.Column="1"
ItemsSource="{Binding Children}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<l:NodeView Content="{Binding}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
你会注意到没有ContentPresenter它看起来有点奇怪。但这是NodeView不合适的基类的结果。内容控件通常用于单个内容,但在这里您有一个包含子项的内容。
顺便说一下,这不是你写它的唯一方法。可以保留模板的默认模板,并将整个内容放入NodeView的ContentTemplate中。
<Style TargetType="{x:Type l:NodeView}">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate TargetType="{x:Type l:NodeView}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="18" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock Text="{Binding Name}"
Grid.Column="1" />
<ItemsControl Grid.Row="1" Grid.Column="1"
ItemsSource="{Binding Children}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<l:NodeView Content="{Binding}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
在主窗口中,您应该使用Content属性而不是DataContext。
PS:我在没有测试的情况下编写了代码,因此不确定它的开箱即用。
答案 1 :(得分:0)
问题似乎是,你以控件本身的风格引用你正在模板化的控件。 所以当WPF尝试创建样式时,它会尝试初始化它,因此它必须创建datatemplate,因此它必须初始化样式......并且你有循环。
我发现,解决此问题的唯一方法是按代码设置默认内容,如下所示:
public NodeView()
{
var dt = FindResource("DefaultNodeContent") as DataTemplate;
var lb = new ItemsControl();
lb.ItemTemplate = dt;
var binding = new Binding("Children");
lb.SetBinding(ItemsControl.ItemsSourceProperty, binding);
var gb = new GroupBox();
gb.Content = lb;
this.Content = gb;
}
为此,您必须向您的应用添加资源字典
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:l="clr-namespace:WpfApplication5">
<DataTemplate x:Key="DefaultNodeContent">
<l:NodeView DataContext="{Binding}" />
</DataTemplate>
</ResourceDictionary>
然后你的风格将改为:
<Style TargetType="{x:Type l:NodeView}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type l:NodeView}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="18" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock Text="{Binding Name}"
Grid.Column="1" />
<ContentPresenter Grid.Row="1"
Grid.Column="1" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
希望这有帮助。