WPF-如何使用复杂的逻辑实现DataTemplate?

时间:2018-12-25 06:40:30

标签: wpf treeview datatemplate

我当前正在将我的应用程序从 WinForms 转移到 WPF 。 由于我是WPF的新手,因此我坚持为自己的treeView项创建DataTemplates。屏幕截图显示了在 WinForms版本中我的树状视图的外观,我需要在WPF中获得关闭结果

(My WinForms treeview) 如您所见,我的DataTemplate的逻辑应考虑以下因素:

  1. 节点类型/定义将为特定项目(节点)显示的图标和字段组合。应用程式大约有7-8种节点类型。类型存储在单独的节点的字段中。
  2. 变量值/如果为null等,我需要替换为文本
  3. 数值变量值/例如:如果为零,则设置灰色,等等。
  4. 其他属性/例如:根据布尔字段添加文本块。
  5. 依此类推...

所有这些因素导致产生大量的可能的商品参数组合

我还使用 DevComponents WPF DotNetBar AdvTree 将项目属性划分为列。我想我应该为不同的字段集创建“子模板”,并从它们组成每一列的整个DataTemplate。 我已经了解了触发器,并且不得不说,使用触发器实现我的逻辑无论如何都会使我的子模板变得很大。

(Current state of my WPF treeview) 所以这是我的问题:

  1. 是否可以使用C#代码动态组成复杂的模板(无需创建原始XAML并在运行时加载它)?
  2. 也许我应该使用完全不同的方式(而不是使用DataTemplate)?在Winforms中,我只使用OwnerDraw模式,因此任务比在WPF中更容易:(
  3. 以及如何在模板内显示嵌套属性?例如: Item.Prop.Subprop1.Subprop2。 Targetprop

PS:英语不是我的母语,对不起。

1 个答案:

答案 0 :(得分:0)

1)答案是肯定的。 例如,如果您想在窗口中为简单字符串定义模板

public MainWindow()
{
    InitializeComponent();

    DataTemplate template = new DataTemplate(typeof(string));
    FrameworkElementFactory borderFactory = new FrameworkElementFactory(typeof(Border));
    borderFactory.SetValue(Border.PaddingProperty, new Thickness(1));
    borderFactory.SetValue(Border.BorderThicknessProperty, new Thickness(1));
    borderFactory.SetValue(Border.BorderBrushProperty, Brushes.Red);

    FrameworkElementFactory textFactory = new FrameworkElementFactory(typeof(TextBlock));
    textFactory.SetBinding(TextBlock.TextProperty, new Binding
    {
        Mode = BindingMode.OneWay
    });
    borderFactory.AppendChild(textFactory);
    template.VisualTree = borderFactory;

    myControl.ContentTemplate = template;
}

在窗口中,您只需要输入

<ContentControl x:Name="myControl" Content="Test text" Margin="10"/>

您的内容控件将呈现带红色边框的字符串。 但是正如您所见,以这种方式定义模板确实很复杂。 我能想象出这种方法的唯一场景是某种程序生成的模板。

另一种方法是为模板生成一个字符串,然后使用XamlReader加载它:

string xaml = "<Ellipse Name=\"EllipseAdded\" Width=\"300.5\" Height=\"200\" 
Fill=\"Red\" xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"/>";
object ellipse = XamlReader.Load(xaml);

2)我真的没有看到在后面的代码中生成模板的需要。例如这种数据结构:

public class User
{
    public string Name { get; set; }
    public User Friend { get; set; }
}
public class RootNode
{
    public string Title { get; set; }
    public User User { get; set; }
    public List<Node> Nodes { get; set; }
}
public class Node
{
    public string Title { get; set; }
    public List<SubNode> SubNodes { get; set; }
}
public class SubNode
{
    public string Title { get; set; }
}

您可以定义这种类型的模板:

<Window 
        ...
        Title="MainWindow" Height="350" Width="525" >

    <Window.Resources>
        <HierarchicalDataTemplate DataType="{x:Type local:RootNode}" ItemsSource="{Binding Nodes}">
            <StackPanel x:Name="spContainer" Orientation="Horizontal">
                <TextBlock Text="{Binding Title}"/>
                <TextBlock Text="{Binding User.Friend.Friend.Name}"/>
            </StackPanel>
            <HierarchicalDataTemplate.Triggers>
                <DataTrigger Binding="{Binding User}" Value="{x:Null}">
                    <Setter TargetName="spContainer" Property="Background" Value="Yellow"/>
                </DataTrigger>
            </HierarchicalDataTemplate.Triggers>
        </HierarchicalDataTemplate>

        <HierarchicalDataTemplate DataType="{x:Type local:Node}" ItemsSource="{Binding SubNodes}">
            <TextBlock Text="{Binding Title}"/>
        </HierarchicalDataTemplate>

        <HierarchicalDataTemplate DataType="{x:Type local:SubNode}" ItemsSource="{Binding Nodes}">
            <TextBlock Text="{Binding Title}"/>
        </HierarchicalDataTemplate>

    </Window.Resources>

    <Grid>

        <TreeView ItemsSource="{Binding RootNodes}"/>

    </Grid>
</Window>

如您所见,您可以按数据类型定义模板,还可以使用触发器在特定情况下修改行为,还可以使用som绑定转换器...

3)您可以像普通属性一样绑定到嵌套属性:

<TextBlock Text="{Binding User.Friend.Friend.Name}"/>

但是在某些情况下,两个以上级别的绑定可能会失败(属性更改时无法解析或无法更新,...)