【问题标题】:WPF - How to implement DataTemplate with complex logic?WPF - 如何用复杂的逻辑实现 DataTemplate?
【发布时间】:2019-05-24 00:03:34
【问题描述】:

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

(My WinForms treeview) 如您所见,我的 DataTemplate 的逻辑应该考虑到这些因素:

  1. 节点类型/定义将针对特定项目(节点)显示的图标和字段组合。应用程序大约有 7-8 种节点类型。类型存储在单独节点的字段中。
  2. 变量值/如果为空,我需要用文本替换,等等
  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:英语不是我的母语,对不起你的眼睛。

【问题讨论】:

    标签: wpf treeview datatemplate


    【解决方案1】:

    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;
    }
    

    在 Window 中你只需放一些类似的东西

    <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}"/>
    

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-05-13
      • 1970-01-01
      • 2011-03-24
      • 1970-01-01
      • 2021-02-28
      • 2022-09-25
      • 1970-01-01
      • 2010-12-09
      相关资源
      最近更新 更多