首页 > 解决方案 > WPF 数据绑定与以编程方式生成控件

问题描述

我的底层数据结构由建立在通用抽象类上的对象层次结构组成:

public abstract class model {
   public string name {get;};
   public string type {get;};
}

public class A:model {
   public int val1 {get; set;}; 
}

public class B:model {
   public int val2 {get; set;}; 
}

public class C:B {
   public Dictionary<string, int> extra_props; 
}

我的目标是创建一个在对象选择时能够动态预览但也能够修改对象的底层属性的 UI。

我是 WPF 的绝对菜鸟,所以我不知道它的全部功能是什么。现在关于单值属性,我已经找到了使用数据绑定将属性绑定到 UI 元素的方法。它就像显示和修改值的魅力一样。

<GroupBox x:Name="InfoBox" Header="Object Information">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="41*"/>
            <ColumnDefinition Width="200*"/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="20"/>
            <RowDefinition Height="20"/>
            <RowDefinition Height="20"/>
            <RowDefinition Height="50*"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <DockPanel Grid.Row="0" Grid.ColumnSpan="2" >
            <TextBlock>Name:</TextBlock>
            <TextBlock Text="{Binding name}" HorizontalAlignment="Right"></TextBlock>
        </DockPanel>
        <DockPanel Grid.Row="1" Grid.ColumnSpan="2" >
            <TextBlock>Type:</TextBlock>
            <TextBlock Text="{Binding type}" HorizontalAlignment="Right"></TextBlock>
        </DockPanel>
        <DockPanel Grid.Row="2" Grid.ColumnSpan="2" >
            <TextBlock>Material Name:</TextBlock>
            <TextBlock Text="{Binding val1}" HorizontalAlignment="Right"></TextBlock>
        </DockPanel>
    </Grid>
</GroupBox>

问题 1:B绑定类型的对象时,属性val1不存在,并且我在调试输出中收到一些 BindingErrors。这些在执行过程中根本没有问题,但我发现没有办法捕捉它们并返回默认值或其他东西。

我想到的解决此问题的唯一方法是在抽象类中添加所有所需的属性,以便它们都存在于具有一些空值或其他东西的所有派生类上,但这根本感觉不对。

问题 2: 对于更复杂的类,例如C,我想根据类的属性集/列表动态生成 UI。现在我完全不知道如何使用数据绑定来完成它,除了在 XML 中一一添加它们并解决问题 1 的问题。

我想到的最可行的解决方案是以编程方式生成一个控件并将其添加到主窗口,其中包含我需要的类属性的文本框和输入,并且再次以编程方式希望能够将对象绑定到控件以便值被适当地读取/设置。显然,这种方法也可以解决问题 1。

我什至不确定这个解决方案是否可行,但无论如何我需要建议和建议,主要是关于是否有办法解决我的数据绑定问题,或者我是否应该以编程方式更新 UI(如果可能的话)。

标签: c#wpfdata-binding

解决方案


好吧,对于一个菜鸟来说,到目前为止你做得很好:) 实际上有一个非常简单的解决方案可以解决这两个问题:DataTemplates。您可以在 microsoft 网站上阅读所有关于它们的信息。

在您的情况下,您想为您尝试显示的每种类型声明一个单独的模板:

 <DataTemplate DataType="{x:Type local:A}">
     <TextBlock Text="This object is type A">
 </DataTemplate>

 <DataTemplate DataType="{x:Type local:B}">
     <TextBlock Text="This object is type B">
 </DataTemplate>

...等等。这通常在此代码出现的任何窗口/用户控件的资源块中完成,尽管它也可以在 App.xaml 资源块等中声明。

任何能够以某种方式绑定到数据的 WPF 控件都使用 DataTemplates。举Button个例子……按钮的整体外观,包括边框以及它在鼠标悬停时的行为等是由它的 ControlTemplate 决定的(当然你也可以自己覆盖它)。但是在 XAML 中的某个时刻,它必须呈现您分配给其“内容”属性的实际数据,如果您要查看 Button 的 ControlTemplate,您会发现其中隐藏着类似这样的内容:

<ContentPresenter />

这实际上是说“好的,这是我的实际数据应该呈现的地方,但不是在这里专门声明它,我希望您改为引用对象的相应 DataTemplate”。通过这种方式,您可以使用单个 ControlTemplate 创建顶级按钮样式,但随后您可以为要在其中呈现的每种类型的事物指定多个 DataTemplate。

许多控件使用 DataTemplate,包括 ListBox 等,其中列表中的每个元素都可以根据其类型赋予不同的图形表示。回到您自己的具体问题,如果您只想渲染数据本身而没有任何花里胡哨等,那么只需使用ContentControl

 <ContentControl Content="{Binding MyModel}" />

所以MyModel应该是model您的视图模型层中的类型属性(或您将 DataContext 设置为的任何其他内容)。将该属性指定为 A、B 或 C 类型的实例将导致 ContentControl 填充您在其 DataTemplate 中声明的任何内容的实例。


推荐阅读