2

以下是我真正想要做的一个简化示例,但我的问题是相同的。

假设我有两个对象,男人和女人,它们都具有相同的属性(年龄、身高和体重),但它们是两个不同的对象。我无法改变这一点。

现在假设我有一个 WPF 面板,它使用 MVVM 原理在文本框中显示某个人的年龄。为此,我使用 Text="{Binding Path=OnePerson.Age}" ,其中 OnePerson 是视图模型中定义的 Man 类型的对象。

这很好用,但我想要一个类似的页面来显示一个女人的这个信息。理想情况下,我只想使用与以前相同的视图和视图模型。但这很棘手,因为数据绑定指向 Man-object OnePerson。我可以以编程方式更改数据绑定(如WPF以编程方式绑定中所述),但我只能从视图的代码隐藏中这样做。我不允许这样做,因为我们使用的是 MVVM 模型。

我想让 OnePerson 引用一个 Man 或 Woman 对象,但我不知道这样做的好方法。它们是不同的类型,所以我不能只使用 if 语句来分配男人或女人。我可以将 OnePerson 声明为对象而不是类型,但是我不能再如此轻松地访问 Age、Height 和 Weight 属性。或者我可以制作一个完全不同的 ViewModel,其中一个将 OnePerson 声明为男人,另一个声明为女人,并为他们两个使用相同的 View。我认为这应该可行,但是对于一个视图有两个视图模型似乎有点奇怪。添加我自己的 Person 类并在它与 Man 或 Woman 之间进行转换可能只会使整个视图模型在我开始添加诸如添加新的 Man/Woman 或编辑现有的功能时变得相当复杂,

我的问题是,在这种情况下,是否有一种干净简单的方法可以使用单个 View 和 Viewmodel 来显示男人或女人的信息。还是我不应该为这些案例烦恼并制作单独的页面?

希望这足够清楚。

4

3 回答 3

3

我认为这ViewModel比 View 或绑定更成问题。ViewViewModel用于ViewModel作为View. _

WPF 的绑定系统仅在绑定无效时抛出警告,并且它不关心数据类型DataContext{Binding OnePerson.Age}无论OnePerson是 aMan还是对象,您的问题 ( ) 中显示的绑定都将正确评估Woman,并将显示该Age对象上任何属性的值。

因此,最好的解决方案是将OnePerson属性设置为可以是 aMan或 a的类型Woman。一个包含所有共享属性的接口将是理想的,因为这样代码就可以在不强制转换的情况下访问它的属性,并且您可以保留所有已有的绑定。

IPerson OnePerson { get; set; }

如果无法使用共享接口而不是可以使用 an object,但是您需要记住在代码中引用它的属性之前将OnePerson对象强制转换为 aMan或类。Woman

object OnePerson { get; set; }

...

if (((Man)OnePerson).Age < 0) ...

第二种选择是创建两个单独的属性,一个 forMan和一个 for Woman。然后,您可以从 轻松访问您正在使用的任何项目的属性ViewModel,并且您可以为 ViewModel 使用单个视图。

Man SomeMan { get; set; }
Woman SomeWoman { get; set; }

您可能需要一些标志来识别哪个是填充的对象,并使用此标志来更新对象的属性以及何时确定视图的DataContext

这是一个使用标志来确定DataContext

<Style x:Key="MyContentControlStyle">
    <Setter Property="Content" Value="{Binding SomeMan}" />
    <Style.Triggers>
        <DataTrigger Property="{Binding SelectedPersonType}" Value="Woman">
            <Setter Property="Content" Value="{Binding SomeWoman}" />
        </DataTrigger>
    </Style.Triggers>
</Style>

当然,如果您确实ViewModel为每种对象类型使用单独的模板或想要单独的模板,您可以轻松地使用诸如隐式之类DataTemplates的方法来确定如何绘制每个对象。

<DataTemplate DataType="{x:Type myModels:Man}">
    <myViews:ManUserControl />
</DataTemplate>
<DataTemplate DataType="{x:Type myModels:Woman}">
    <myViews:WomanUserControl />
</DataTemplate>

<ContentPresenter Content="{Binding SelectedPerson}" />

总的来说,我建议您尝试将OnePerson其设为两个对象共享的数据类型,或者ViewModel为两个对象创建一个单独的数据类型。听起来您的两个课程都非常相似,因此您甚至可以摆脱某种泛型ViewModel,例如PersonViewModel<T>您传入的位置ManWoman作为T

于 2013-06-27T13:41:11.243 回答
1

您是否考虑过基于接口对象制作对象。该接口基本上创建了一个合同,说明从它派生的任何东西都必须至少具有它所声明的任何东西......派生控件可以有更多,但至少是你想要的东西。例如。

public interface IPersonProperties
{
   string PersonName { get; set; }
   int Age { get; set; }

   // if you want a function that is common between them too
   bool SomeCommonFunction(string whateverParms);

   etc...
}

public class Man : IPersonProperties
{
   // these required as to support the IPersonProperties
   public string PersonName { get; set; }
   public int Age { get; set; }

   public bool SomeCommonFunction(string whateverParms)
   {  doSomething;
      return true;
   }


   // you can still have other stuff specific to man class definition
   public string OtherManBasedProperty { get; set;}

   public void SomeManFunction()
   {  // do something specific for man here }
}

public class Woman : IPersonProperties
{
   // these required as to support the IPersonProperties
   public string PersonName { get; set; }
   public int Age { get; set; }

   public bool SomeCommonFunction(string whateverParms)
   {  doSomething;
      return false;
   }

   // you can still have other stuff specific to WOMAN class definition
   public string OtherWOMANBasedProperty { get; set;}

   public void SomeWomanFunction()
   {  // do something specific for man here }

}

然后,在您的 MVVM 中,您可以公开的“对象”是 IPersonProperties 的对象,例如

public class YourMVVM
{
   public IPersonProperties BindToMe{ get; set }

   public YourMVVM()
   {
      BindToMe = new Man();
      // OR... BindToMe = new Woman();
   }
}

然后,但是/在您创建对象的 MVVM 对象中的任何位置,请执行此操作。您的绑定将是相同的,但可以是任何一种性别。如果它们之间通过接口有共同点,您可以简单地通过

if( BindToMe.SomeCommonFunction( "testing"))
   blah blah.

但是,如果在某些方法中您需要根据特定性别做某事,您可以这样做...

if( BindToMe is Man )
   ((Man)BindToMe).SomeManFunction();
else
   ((Woman)BindToMe).SomeWOMANFunction();

希望这为您在实施选择上打开了一些大门。

于 2013-06-27T12:38:52.867 回答
0

如果您只想为您的年龄使用一个 xaml 控件,您可以这样做

年龄用户控制

  <TextBlock Text="{Binding Path=Age}" />

个人观点

   <local:AgeUserControl DataContext="{Binding Path=MyObjectTypePersonProperty} />
于 2013-06-27T12:57:09.803 回答