以下是我真正想要做的事情的简化示例,但我对它的问题是一样的。
假设我有两个物体,男人和女人,两者都具有相同的属性(年龄,身高和体重),但它们是两个不同的对象。我不能改变它。
现在假设我有一个WPF面板,使用MVVM原理,在文本框中显示某个人的年龄。我使用Text =" {Binding Path = OnePerson.Age}"为此,OnePerson是viewmodel中定义的Man类型的对象。
这很好用,但我想要一个类似的页面来显示一个女人的这个信息。理想情况下,我想像以前一样使用相同的视图和视图模型。但这很棘手,因为数据绑定指向Man-object OnePerson。我可以通过编程方式更改数据绑定(如WPF Binding Programatically中所述),但我只能从视图的代码隐藏中进行更改。我不允许这样做,因为我们正在使用MVVM模型。
我想让OnePerson提到男人或女人的对象,但我不知道这样做的好方法。它们是不同的类型,所以我不能使用if语句分配一个男人或女人。我可以将OnePerson声明为对象而不是类型,但是我无法再轻松访问Age,Height和Weight属性了。或者我可以创建一个完全不同的ViewModel,其中一个将OnePerson声明为Man而另一个声明为Woman,并对它们使用相同的View。我认为这应该可行,但是对于单个视图有两个视图模型似乎有点奇怪。添加我自己的Person类并在它与男人或女人之间进行翻译可能只会让整个视图模型变得更复杂,当我开始添加添加新的Man / Woman或编辑现有的功能时,我可能会这样做好好复制粘贴Man视图和viewmodel,只将OnePerson对象更改为Woman。
我的问题是,在这种情况下,是否有一种干净简单的方法可以使用单个View和Viewmodel来显示男人或女人的信息。或者,我是否应该为这些案件打扰和制作单独的页面?
希望这很清楚。
答案 0 :(得分:3)
我认为ViewModel
上的问题比View或绑定更多。 View
仅用于ViewModel
的直观表示,听起来您的问题需要在ViewModel
中修复,而不是View
。
WPF的绑定系统仅在绑定无效时才会发出警告,并且它不关心DataContext
的数据类型。无论{Binding OnePerson.Age}
是OnePerson
还是Man
对象,问题(Woman
)中显示的绑定都会正确评估,并会显示任意{{1}的值该对象的属性。
因此,最佳解决方案是将Age
属性设为可以是OnePerson
或Man
的类型。包含所有共享属性的接口将是理想的,因为它的属性可以通过代码访问而无需强制转换,并且您可以保留已有的所有绑定。
Woman
如果无法使用共享界面而不能使用IPerson OnePerson { get; set; }
,那么您需要记住将object
对象强制转换为OnePerson
或{{1在引用代码中的属性之前的类。
Man
第二种方法是创建两个单独的属性,一个用于Woman
,另一个用于object OnePerson { get; set; }
...
if (((Man)OnePerson).Age < 0) ...
。然后,您可以从Man
轻松访问您正在使用的任何项目的属性,并且可以使用ViewModel的单个视图。
Woman
您可能需要一些标志来识别哪个是填充的对象,并使用此标志来更新对象的属性以及何时想要确定视图的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>
之类的内容轻松地执行此操作,以确定如何绘制每个对象。 / p>
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
或{{ 1}}为PersonViewModel<T>
答案 1 :(得分:1)
您是否考虑过基于Interfaced对象制作对象?该接口基本上创建了一个契约,声明从它派生的任何东西必须具有它至少声明的任何东西......派生控件可以有更多,但至少你想要的东西。例如。
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();
希望这为您提供实施选择的大门。
答案 2 :(得分:0)
如果你只想在你的年龄使用一个xaml控件,你可以
ageusercontrol
<TextBlock Text="{Binding Path=Age}" />
personview
<local:AgeUserControl DataContext="{Binding Path=MyObjectTypePersonProperty} />