如何使ViewModel - View界面更清晰?

时间:2017-11-24 16:49:52

标签: c# .net wpf mvvm

想象一下SomeViewModel使用的视图模型SomeView

public class SomeViewModel : BindableBase
{
    public NumberEditorViewModel VoltageEditor { get; private set; }

    public SomeViewModel()
    {
        VoltageEditor = new NumberEditorViewModel(...) { ... }
    }
}

使用可重复使用的子视图模型来编辑所有数字,例如:

public class NumberEditorViewModel: BindableBase
{
    public string Title { get; private set; }
    public string Value { get; set; }
    public string Unit { get; private set; }

    ...
}

因此视图可以像这样使用它:

<StackPanel Orientation="Horizontal">
    <TextBlock Text={Binding VoltageEditor.Title} />
    <TextBox Text={Binding VoltageEditor.Value} />
    <TextBlock Text={Binding VoltageEditor.Unit} />
</StackPanel>

这些属性 - TitleValueUnit是视图将要访问的内容。

但实际上NumberEditorViewModel是一个复杂的类,提供单位转换(公制/英制),格式化值,因此它们具有适当的精度(格式字符串)等。因此它有一大堆属性指定它使用的单位,需要什么样的精度等,这样它就可以正确地格式化和解析值。它们将设置在SomeViewModel中,如:

VoltageEditor = new NumberEditorViewModel()
{
    Title = "My Voltage:",
    UnitType = UnitType.Voltage,
    PrecisionType = PrecisionType.SomePrecision,
    ...
}

这会使NumberEditorViewModel模型变得混乱,因为它具有很多属性,并且没有人知道视图将使用哪些属性,并且该对象的创建者将使用哪些属性来控制其行为

如何改进这一点以使视图和视图模型之间以及此视图模型与其创建者之间的界面更加清晰?

我尝试使用接口仅向视图提供属性的子集,但WPF似乎忽略了它并使用对象上可用的任何东西进行绑定。这也会杀死视图模型第一种方法。

1 个答案:

答案 0 :(得分:1)

这是一个想法,您可以使用子类作为适配器,而不是使用接口来提供属性的子集。

每个子类都可以由NumberEditorViewModel的getter给出。

这样的事情:

public class NumberEditorViewModel
{
    // quick & dirty
    public SubTypeA ExampleA { get { return new SubTypeA ( this ); } }
    public SubTypeB ExampleB { get { return new SubTypeB ( this ); } }
}

然后在你看来:

<StackPanel Orientation="Horizontal">
    <TextBlock Text={Binding VoltageEditor.ExampleA.Title} />
    <TextBox Text={Binding VoltageEditor.ExampleA.Value} />
    <TextBlock Text={Binding VoltageEditor.ExampleA.Unit} />
</StackPanel>
  • 编辑 -

(1)我们倾向于使用嵌套类来做这种事情,但这只是一个偏好问题。

(2)这是一个approch,它将引发子类型之间的propertychanged事件:

您可以利用PropertyChanged事件在视图模型之间进行通信,因为它们彼此密切相关。

public class NumberEditorViewModel : BindableBase
{
    // subtypes ///////////////////////////////

    public class SubTypeA
    {
        public string PrecisionType 
        {
            get { return _precisionType; } 
            set { _precisionType = value; OnPropertyChanged ( ); }
        }
        private string _precisionType;
    }

    public class SubTypeB
    {
        public string INPC 
        {
            get { return _inpc; } 
            set { _inpc = value; OnPropertyChanged ( ); }
        }
        private string _inpc;
    }

    // ctor ///////////////////////////////

    public NumberEditorViewModel ( )
    {
        ExampleA = new SubTypeA ( );
        ExampleB = new SubTypeB ( );

        ExempleA.PropertyChanged += ( sender, e ) =>
        {
            if ( e.PropertyName == nameof ( SubTypeA.PrecisionType ) )
            {
                var exempleA = sender as ExempleA;

                ExampleB.INPC = ... /* newValue calculated */
            }
        }
    }

    public SubTypeA ExampleA { get; private set; }
    public SubTypeB ExampleB { get; private set; }
}