首页 文章

WPF:使用MVVM时更改数据绑定

提问于
浏览
2

以下是我真正想要做的简化示例,但我对它的问题是一样的 .

假设我有两个对象,Man和Woman,它们都具有相同的属性(Age,Height和Weight),但它们是两个不同的对象 . 我无法改变这一点 .

现在假设我有一个WPF面板,使用MVVM原理,在文本框中显示某个人的年龄 . 我使用Text =“”,其中OnePerson是视图模型中定义的Man类型的对象 .

这工作正常,但我想要一个类似的页面来显示一个女人的这个信息 . 理想情况下,我想像以前一样使用相同的视图和视图模型 . 但这很棘手,因为数据绑定指向Man-object OnePerson . 我可以通过编程方式更改数据绑定(如WPF Binding Programatically中所述),但我只能从视图's codebehind. I' m这样做,因为我们正在使用MVVM模型 .

我想让OnePerson引用一个Man或Woman对象,但我不知道这样做的好方法 . 它们是不同的类型,因此我不能仅使用if语句指定Man或Woman . 我可以将OnePerson声明为对象而不是类型,但是我不能再轻松访问Age,Height和Weight属性了 . 或者我可以创建一个完全不同的ViewModel,其中一个将OnePerson声明为Man而另一个声明为Woman,并对它们使用相同的View . 我认为这应该可行,但是对于单个视图有两个视图模型似乎有点奇怪 . 添加我自己的Person类并在它与男人或女人之间进行翻译可能只会让整个视图模型变得更复杂,当我开始添加添加新的Man / Woman或编辑现有的功能时,我可能会这样做好好复制粘贴Man视图和viewmodel,只将OnePerson对象更改为Woman .

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

希望这很清楚 .

3 回答

  • 3

    我认为 ViewModel 上的问题比View或绑定更多 . View 仅仅是 ViewModel 的直观表示,听起来你的问题需要在 ViewModel 中修复,而不是 View .

    WPF 's binding system only throws a warning if a binding is invalid, and it doesn' t关心数据类型 DataContext 是 . 无论 OnePersonMan 还是 Woman 对象,您的问题( {Binding OnePerson.Age} )中显示的绑定都将正确评估,并将在该对象上显示任何 Age 属性的值 .

    因此,最好的解决方案是使 OnePerson 属性成为 ManWoman 的类型 . 包含所有共享属性的接口将是理想的,因为它可以通过代码访问它的属性而无需强制转换,并且您可以保留已有的所有绑定 .

    IPerson OnePerson { get; set; }
    

    如果无法使用共享接口而不能使用 object ,则需要记住在引用代码中的属性之前将 OnePerson 对象强制转换为 ManWoman 类 .

    object OnePerson { get; set; }
    
    ...
    
    if (((Man)OnePerson).Age < 0) ...
    

    第二种方法是创建两个单独的属性,一个用于 Man ,另一个用于 Woman . 然后,您可以从 ViewModel 轻松访问您正在使用的任何项目的属性,并且可以使用ViewModel的单个视图 .

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

    您可能需要一些标志来识别哪个是已填充的对象,并使用此标志来更新对象's properties and when you want to determine the View' s 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

  • 0

    您是否考虑过基于Interfaced对象制作对象?界面基本上创建一个 Contract ,声明从它派生的任何东西必须具有它已经声明的任何东西......派生控件可以有更多,但至少你想要的东西 . 例如 .

    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();
    

    希望这为您提供实施选择的大门 .

  • 1

    如果你只是想为你的年龄使用一个xaml控件,你可以做到

    ageusercontrol

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

    personview

    <local:AgeUserControl DataContext="{Binding Path=MyObjectTypePersonProperty} />
    

相关问题