【问题标题】:WPF Databinding vs Programmatically generating controlsWPF 数据绑定与以编程方式生成控件
【发布时间】:2019-05-26 21:53:08
【问题描述】:

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

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

我的目标是创建一个用户界面,在选择对象时能够动态预览,但也能够修改对象的基础属性。

我是 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。

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

【问题讨论】:

    标签: c# wpf data-binding


    【解决方案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 资源块等中声明。

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

    <ContentPresenter />
    

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

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

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

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

    【讨论】:

    • 首先非常感谢您抽出宝贵的时间来写这么好的回复。其次,这听起来真的超级简单。我必须对其进行测试,然后用我的结果回复你:D
    • 优秀。正好符合我的需要。老实说,我开始使用以编程方式生成的 GUI 来实施我提出的解决方案。今天我用 DataTemplates 做同样的事情。我最终得到了完全相同的结果。我不确定它是否更有效。可以肯定的是,它在我将来需要的任何地方都提供了可重用的代码。我只有一个担心。我不确定如何处理从基类派生的公共属性。现在我必须将它们复制粘贴到每个数据模板上。我不确定这是否是因为基础模型类的抽象性质。
    • 这可以通过将公共元素(即那些绑定到您的model 类的元素)放入它们自己的 ControlTemplate 中来完成。然后,您只需使用 &lt;Control Template="{StaticResource YourControlTemplate}" /&gt; 将该模板插入您的 DataTemplates
    • 这正是我在网上找到的处理常见模板的解决方案,并且再次像魅力一样工作。再次感谢,案件已解决。
    猜你喜欢
    • 1970-01-01
    • 2011-10-22
    • 2016-10-24
    • 2011-05-08
    • 1970-01-01
    • 2023-04-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多