MVVM 中的数据模板是否已过时?

发布于 2024-11-02 09:28:39 字数 4055 浏览 1 评论 0原文

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

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

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

感谢您的所有澄清!

更新:

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

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 的数据模板当然也发生了变化:

<!-- 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>

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

I have created the following model (the code is simplified to illustrate the situation):

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

Next I have created a view model:

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

And finally, the view:

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

So, when the model that is loaded is of type Person the personTemplate is shown; vice versa, when the model is Company the companyTemplate is shown.

My questions are:

  1. Does this approach make sense at all? Would it be smarter to delete the Model
    property in the ViewModel class and to introduce an enum or just a simple bool
    which would show person if true, or company if `false?
  2. While defining the data templates, I specified DataTypes to Person and Company
    types (it was natural to me to do it this way). Do I need it at all because in the very
    next line I am setting a new data context to be the one from the UserControl?
  3. Should the DataTypes of the data templates be different view models, something like
    PersonViewModel and CompanyViewModel? Does it make sense to create them?
  4. How can I, and can I at all, make data template inherit the data context from the
    ContentControl automatically?

I know that all this is a matter of a personal choice in the end, but since I am learning MVVM (I am using MVVM Light), I am wondering which approach would be the most recommendable one? I still do not fully understand when should the classes from models be used as data types for data templates and when should view models be used for that purpose. Should the assembly that represents the model even be referenced in the view assembly (assuming that view, model and view model all reside in separate assemblies)?

Thanks for all the clarifications!

UPDATE:

This update should explain the problem of having classes of the model as DataTypes in the data templates when the property of the model class is not directly binded to just one control in the view.

There is an enum and a new property in the Person, so now it looks like this:

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

And in the view, the data template of the person is changed as well of course:

<!-- 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>

If the Content of the ContentControl is set to Model property of the ViewModel, how would I resolve the gender/radio buttons situation; because, now they do not match in the way one control/one property?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

财迷小姐 2024-11-09 09:28:39

我会将其更改为:

<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 中的绑定使用 此处

I would change it to this:

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

like this you define implicit styles for your classes and you don't have to use a templateselector. Also then you don't need all your string properties in the ViewModel class:

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

Disclaimer, the binding in the RadioButtons uses a Converter from here.

贪了杯 2024-11-09 09:28:39

绝对不是(它们已经过时了吗)。

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

Absolutely positively not (are they obsolete).

  1. Yes, it makes perfect sense, however your bindings, not so much. Those issues can be handled in a number of different ways. For instance, the Account could have a Parent property which exposes the ViewModel it is contained in (I didn't say it was the best approach).
  2. This is an issue with your ViewModel design, where you must bind not against the Account but the ViewModel. It might be possible to change the design so that you don't need to do this; hard to tell with the snapshot you have provided.
  3. I don't think that will help you at this point. I'd see if there was a way to keep your ViewModel related UI out of the DataTemplate.
  4. Here's one good solution: Create a DataTemplateSelector that chooses the template based on the Account property. That way, you can bind the ItemsSource directly to the DataContext, and it will be available within the DataTemplate.
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文