WPF数据绑定与以编程方式生成控件

时间:2019-05-26 21:53:08

标签: c# wpf data-binding

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

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,该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。这些在执行过程中完全没有问题,但是我发现没有办法捕获它们并返回默认值或其他值。

我想解决这个问题的唯一方法是在抽象类中添加所有所需的属性,以便它们都存在于所有带有某些null值或某些内容的派生类中,但这并不适合全部。

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

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

我什至不确定这种解决方案是否可行,但是无论如何,我主要需要有关是否可以解决数据绑定问题或是否应该以编程方式进行更新的建议和建议。用户界面(如果可能)。

1 个答案:

答案 0 :(得分:1)

对于一个菜鸟来说,到目前为止您做得不错:)实际上,这两个问题都有一个非常简单的解决方案:DataTemplates。您可以阅读有关on the microsoft site的所有信息。

对于您的情况,您想为要显示的每种类型声明一个单独的模板:

 <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资源块等中声明该代码,但通常在显示此代码的任何窗口/用户控件的Resources块中完成。

任何能够以某种方式绑定到数据的WPF控件都使用

DataTemplates。以Button为例。...按钮的整体外观(包括边框及其在鼠标悬停时的行为方式)由其ControlTemplate决定(当然,您也可以覆盖自己)。但是在XAML的某个时刻,它必须呈现分配给它的“ Content”属性的实际数据,如果您查看Button的ControlTemplate,您会发现它内藏着这样的东西:

<ContentPresenter />

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

许多控件使用DataTemplate,包括ListBox等,其中可以根据列表的类型为列表中的每个元素提供不同的图形表示。回到您自己的特定问题,如果您只想呈现数据本身而周围没有任何花哨的东西,则只需使用ContentControl

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

因此,MyModel应该是视图模型层(或您将DataContext设置为其他类型)中类型为model的属性。将该属性分配为A,B或C类型的实例,将使ContentControl填充您在其DataTemplate中声明的任何实例。