【问题标题】:Are data templates obsolete in MVVM?MVVM 中的数据模板是否已过时?
【发布时间】:2011-04-18 17:22:29
【问题描述】:

我创建了以下模型(代码被简化以说明情况):

public abstract class Account
{
    public string Name { get; set; }
}

public class Person : Account
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

public class Company : Account
{
    public string Owner { get; set; }
}

接下来我创建了一个视图模型:

public class ViewModel
{
    public Account Model { ... }
    public string Name { ... }
    public string FirstName { ... }
    public string LastName { ... }
    public string Owner { ... }   
    ...
}

最后是视图:

<UserControl>
  <UserControl.Resources>

    <!-- Person data template -->
    <DataTemplate x:Key="personTemplate" DataType="{x:Type model:Person}">
      <Grid DataContext="{Binding ElementName=rootLayout, Path=DataContext}">
        <TextBlock Text="{Binding Path=Name}" />
        <TextBlock Text="{Binding Path=FirstName}" />
        <TextBlock Text="{Binding Path=LastName}" />
      </Grid>
    </DataTemplate>

    <!-- Company data template -->
    <DataTemplate x:Key="companyTemplate" DataType="{x:Type model:Company}">
      <Grid DataContext="{Binding ElementName=rootLayout, Path=DataContext}">
        <TextBlock Text="{Binding Path=Name}" />
        <TextBlock Text="{Binding Path=Owner}" />
      </Grid>
    </DataTemplate>

    <!-- Data template selector for different account types -->
    <local:AccountTemplateSelector x:Key="templateSelector" 
        PersonTemplate="{StaticResource personTemplate}" 
        CompanyTemplate="{StaticResource companyTemplate}" />

  </UserControl.Resources>

  <StackPanel Name="rootLayout" DataContext="{Binding Path=viewModel}">
    <ContentControl Content="{Binding Path=Model}" 
        ContentTemplateSelector="{StaticResource templateSelector}"/>
    <Button Content="Save" />
    <Button Content="Close" />
  </StackPanel>

</UserControl>

所以,当加载的模型是Person 类型时,会显示personTemplate;反之亦然,当模型为Company 时,将显示companyTemplate

我的问题是:

  1. 这种方法有意义吗?删除Model会不会更聪明 ViewModel 类中的属性并引入一个枚举或只是一个简单的bool 如果true 会显示人,如果`false 会显示公司?
  2. 在定义数据模板时,我将DataTypes 指定为PersonCompany 类型(这样做对我来说很自然)。我是否需要它,因为在非常 下一行我将一个新的数据上下文设置为来自UserControl?
  3. 数据模板的DataTypes 是否应该是不同的视图模型,例如 PersonViewModelCompanyViewModel?创建它们有意义吗?
  4. 我怎样才能让数据模板从 ContentControl 自动?

我知道这最终都是个人选择的问题,但是由于我正在学习 MVVM(我正在使用 MVVM Light),我想知道哪种方法最值得推荐?我仍然不完全理解何时应将模型中的类用作数据模板的数据类型,以及何时应将视图模型用于此目的。表示模型的组件是否应该在视图组件中被引用(假设视图、模型和视图模型都位于单独的组件中)?

感谢所有的澄清!

更新:

此更新应解释当模型类的属性未直接绑定到视图中的一个控件时,数据模板中模型的类为DataTypes 的问题。

Person 中有一个枚举和一个新属性,所以现在看起来像这样:

public class Person : Account
{
    public enum GenderType { Female, Male, NotSpecified }

    public string FirstName { get; set; }
    public string LastName { get; set; }
    public GenderType Gender {get; set; }
}

在视图中,人的数据模板当然也发生了变化:

<!-- Person data template -->
<DataTemplate x:Key="personTemplate" DataType="{x:Type model:Person}">
  <Grid DataContext="{Binding ElementName=rootLayout, Path=DataContext}">
    <TextBlock Text="{Binding Path=Name}" />
    <TextBlock Text="{Binding Path=FirstName}" />
    <TextBlock Text="{Binding Path=LastName}" />
    <RadioButton Name="Female" />
    <RadioButton Name="Male" />
    <RadioButton Name="NotSpecified" />
  </Grid>
</DataTemplate>

如果ContentControlContent 设置为ViewModelModel 属性,我将如何解决性别/单选按钮的情况;因为,现在它们在一个控件/一个属性的方式上不匹配?

【问题讨论】:

  • @vorrtex:好点子!实际上,下面提供的解决方案也隐含地打乱了这个问题。感谢您的加入。
  • 1) 除了 Model 属性,我会删除 4 个其他属性。 3) 一个视图模型可以有多个数据模板。 4)他们自动继承它。更新问题:使用 ListBox 并将 ItemContainerStyle 属性更改为看起来像 RadioButton。
  • 投票支持看起来像带有单选按钮想法的面板的列表框。
  • @Boris 我偶尔按下 Enter 按钮。无论如何,我想说的几乎和 Markus 说的一样:删除不必要的属性并绑定到 rootLayout。
  • @Boris 如果你喜欢这个想法,我可以分享我在我的应用程序中使用的这个模板的代码:pastebin.com/5nrJQNj2 像这样设置它:ItemContainerStyle="{StaticResource RadioButtonItemContainerStyle}"。但它来自 Silverlight,所以我不知道它在 WPF 中如何工作。

标签: wpf mvvm mvvm-light


【解决方案1】:

我会改成这样:

<UserControl>
  <UserControl.Resources>
    <!-- Person data template -->
    <DataTemplate DataType="{x:Type model:Person}">
      <Grid>
        <TextBlock Text="{Binding Path=Name}" />
        <TextBlock Text="{Binding Path=FirstName}" />
        <TextBlock Text="{Binding Path=LastName}" />
        <RadioButton Name="Female"       IsChecked="{Binding Gender , Converter={StaticResource enumBooleanConverter}, ConverterParameter=Female}" />
        <RadioButton Name="Male"         IsChecked="{Binding Gender , Converter={StaticResource enumBooleanConverter}, ConverterParameter=Male}" />
        <RadioButton Name="NotSpecified" IsChecked="{Binding Gender , Converter={StaticResource enumBooleanConverter}, ConverterParameter=NotSpecified }" />
      </Grid>
    </DataTemplate>

    <!-- Company data template -->
    <DataTemplate DataType="{x:Type model:Company}">
      <Grid>
        <TextBlock Text="{Binding Path=Name}" />
        <TextBlock Text="{Binding Path=Owner}" />
      </Grid>
    </DataTemplate>
  </UserControl.Resources>

  <StackPanel DataContext="{Binding viewModel}">
    <ContentControl Content="{Binding Model}" />
    <Button Content="Save" />
    <Button Content="Save" />
    <Button Content="Close" />
  </StackPanel>

</UserControl>

这样,您可以为类定义隐式样式,而不必使用模板选择器。此外,您不需要 ViewModel 类中的所有字符串属性:

public class ViewModel
{
    public Account Model { ... } 
    ...
}

免责声明,RadioButtons 中的绑定使用来自here 的转换器。

【讨论】:

  • @Markus:谢谢你的回答。如果我在Person 类中有一个Birthday 属性并且我希望它显示如下:“Born on ”-在视图模型中为它​​创建一个属性并格式化/转换会更好吗它在那里(这会给“干净”的数据模板带来问题),或者为它创建一个转换器?谢谢。哦,让我们假设在这里使用 StringFormat 是不可能的,原因很长,具体要在这里解释。
  • @Boris 您可以在视图模型上定义属性,例如:DateTime Birthday。然后我会使用 StringFormat 或 Converter 来显示它。
  • @Markus:再次感谢。看来Birthday 的例子并不能很好地说明我的担忧。这就是为什么我在我的问题中添加了一些更新来解释我面临的问题。我非常喜欢您将Model 提供给ContentControlContent 属性的想法!唯一的问题是,我不确定如何处理性别属性。如果你能帮我解决这个问题,那就太完美了!
  • @Boris 以适应您的更新我宁愿建议使用组合框来选择女性/男性的东西,如 here
  • @Boris,如果您真的想使用 RadioButtons,请查看this question的答案
【解决方案2】:

绝对不会(它们是否已过时)。

  1. 是的,这很有意义,但是你的绑定,不是那么多。这些问题可以通过多种不同的方式来处理。例如,Account 可以有一个 Parent 属性,该属性公开它包含的 ViewModel(我没有说这是最好的方法)。
  2. 这是您的 ViewModel 设计的一个问题,您必须绑定的不是 Account,而是 ViewModel。可能可以更改设计,以便您不需要这样做;您提供的快照很难判断。
  3. 我认为目前这对您没有帮助。我想看看是否有办法让您的 ViewModel 相关的 UI 脱离 DataTemplate。
  4. 这是一个很好的解决方案: 创建一个DataTemplateSelector,它根据Account 属性选择模板。这样,您可以将 ItemsSource 直接绑定到 DataContext,并且它将在 DataTemplate 中可用。

【讨论】:

  • 感谢您的回答。我对我的问题添加了一些更新。所以现在,如果我绑定Account,我将如何处理Gender 属性,因为我不能只将 Gender 绑定到 3 个单选按钮。或者如果我可能会,你能告诉我怎么做吗?谢谢!
猜你喜欢
  • 1970-01-01
  • 2011-09-27
  • 1970-01-01
  • 1970-01-01
  • 2011-05-09
  • 2017-06-04
  • 1970-01-01
  • 2021-07-30
  • 2022-01-10
相关资源
最近更新 更多