第十八章:MVVM(四)

简介:

ViewModel中的交互式属性
ViewModel的第二个例子做了一些非常基本的事情,你永远不会为此目的编写ViewModel。 SimpleMultiplierViewModel类简单地将两个数字相乘。 但它是一个很好的例子,用于演示具有多个交互属性的ViewModel的开销和机制。 (虽然你永远不会编写一个ViewModel来将两个数字相乘,但你可以编写一个ViewModel来解决二次方程或更复杂的东西。)
SimpleMultiplierViewModel类是SimpleMultiplier项目的一部分:

using System;
using System.ComponentModel;
namespace SimpleMultiplier
{
    class SimpleMultiplierViewModel : INotifyPropertyChanged
    {
        double multiplicand, multiplier, product;
        public event PropertyChangedEventHandler PropertyChanged;
        public double Multiplicand 
        {
            set
            {
                if (multiplicand != value)
                {
                    multiplicand = value;
                    OnPropertyChanged("Multiplicand");
                    UpdateProduct();
                }
            }
            get
            {
                return multiplicand;
            }
        }
        public double Multiplier
        {
            set
            {
                if (multiplier != value)
                {
                    multiplier = value;
                    OnPropertyChanged("Multiplier");
                    UpdateProduct();
                }
            }
            get
            {
                return multiplier;
            }
        }
        public double Product
        {
            protected set
            {
                if (product != value)
                {
                    product = value;
                    OnPropertyChanged("Product");
                } 
            }            
            get
            {
                return product;
            }
        }
        void UpdateProduct() 
        {
            Product = Multiplicand * Multiplier;
        }
        protected void OnPropertyChanged(string propertyName)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }
}

该类定义了double类型的三个公共属性,名为Multiplicand,Multiplier和Product。每个属性都由私有字段支持。前两个属性的set和get访问器是公共的,但Product属性的set访问器受到保护,以防止它在类外部设置,同时仍允许后代类更改它。
每个属性的set访问器首先检查属性值是否实际更改,如果是,则将支持字段设置为该值,并使用该属性名称调用名为OnPropertyChanged的方法。
INotifyPropertyChanged接口不需要OnPropertyChanged方法,但ViewModel类通常包含一个用于减少代码重复的方法。它通常被定义为受保护,以防您需要从另一个ViewModel派生一个ViewModel并在派生类中触发该事件。在本章的后面,您将看到在INotifyPropertyChanged类中减少代码重复的技术。
Multiplicand和Multiplier属性的set访问器通过调用UpdateProduct方法结束。这是执行将两个属性的值相乘并为Product属性设置新值的方法,然后触发其自己的PropertyChanged事件。
这是使用此ViewModel的XAML文件:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:SimpleMultiplier"
             x:Class="SimpleMultiplier.SimpleMultiplierPage"
             Padding="10, 0">
    <ContentPage.Resources>
        <ResourceDictionary>
            <local:SimpleMultiplierViewModel x:Key="viewModel" />
            <Style TargetType="Label">
                <Setter Property="FontSize" Value="Large" />
            </Style>
        </ResourceDictionary>
    </ContentPage.Resources>
    <StackLayout BindingContext="{StaticResource viewModel}">
 
        <StackLayout VerticalOptions="CenterAndExpand">
            <Slider Value="{Binding Multiplicand}" />
            <Slider Value="{Binding Multiplier}" />
        </StackLayout>
        <StackLayout Orientation="Horizontal"
                     Spacing="0"
                     VerticalOptions="CenterAndExpand"
                     HorizontalOptions="Center">
            <Label Text="{Binding Multiplicand, StringFormat='{0:F3}'}" />
            <Label Text="{Binding Multiplier, StringFormat=' x {0:F3}'}" />
            <Label Text="{Binding Product, StringFormat=' = {0:F3}'}" /> 
        </StackLayout>
    </StackLayout>
</ContentPage>

SimpleMultiplierViewModel在Resources字典中实例化,并使用StaticResource标记扩展设置为StackLayout的BindingContext属性。 BindingContext由StackLayout的所有子孙继承,其中包括两个Slider和三个Label元素。 BindingContext的使用允许这些绑定尽可能简单。
Slider的Value属性的默认绑定模式是TwoWay。 每个Slider的Value属性的更改会导致ViewModel的属性发生更改。
三个Label元素显示ViewModel的所有三个属性的值,其中一些格式插入时间,并且等号与数字相符:

<Label Text="{Binding Multiplicand, StringFormat='{0:F3}'}" />
<Label Text="{Binding Multiplier, StringFormat=' x {0:F3}'}" />
<Label Text="{Binding Product, StringFormat=' = {0:F3}'}" />

对于前两个,您可以将Label元素的Text属性直接绑定到相应Slider的Value属性,但这需要为每个Slider指定一个带有x的名称:Name并在Source参数中引用该名称 使用x:Reference标记扩展。 此程序中使用的方法更加清晰,并验证数据是否正在通过ViewModel从每个Slider到每个Label进行完整的旅行。
除了在构造函数中调用InitializeComponent之外,代码隐藏文件中没有任何内容。 所有业务逻辑都在ViewModel中,整个用户界面在XAML中定义:
2018_10_11_100009
如果您愿意,可以在资源字典中实例化时初始化ViewModel:

<local:SimpleMultiplierViewModel x:Key="viewModel"
                                 Multiplicand="0.5"
                                 Multiplier="0.5" />

作为双向绑定的结果,Slider元素将获得这些初始值。
当您想要稍微改变用户界面时,将用户界面与底层业务逻辑分离的好处变得很明显,可能通过将Stepper替换为Slider中的一个或两个数字:

<StackLayout VerticalOptions="CenterAndExpand">
    <Slider Value="{Binding Multiplicand}" />
    <Stepper Value="{Binding Multiplier}" />
</StackLayout>

除了两个元素的不同范围外,功能相同:
2018_10_11_100227
您也可以替换条目:

<StackLayout VerticalOptions="CenterAndExpand">
    <Slider Value="{Binding Multiplicand}" />
    <Entry Text="{Binding Multiplier}" />
</StackLayout>

Entry的Text属性的默认绑定模式也是TwoWay,因此您需要担心的是source属性double和target属性字符串之间的转换。 幸运的是,此转换由绑定基础结构自动处理:
2018_10_11_100328
如果键入一系列无法​​转换为double的字符,则绑定将保留最后一个有效值。如果您需要更复杂的验证,则必须实现自己的验证(例如使用触发器,将在第23章中讨论)。
一个有趣的实验是键入1E-1,这是可转换为双精度的科学记数法。你会看到它在条目中立即变为“0.1”。这是TwoWay绑定的效果:Multiplier属性从Entry设置为1E-1,但绑定基础结构在值返回Entry时调用的ToString方法返回文本“0.1”。因为这与现有的Entry文本,新文本已设置。为防止这种情况发生,您可以将绑定模式设置为OneWayToSource:

<StackLayout VerticalOptions="CenterAndExpand">
    <Slider Value="{Binding Multiplicand}" />
    <Entry Text="{Binding Multiplier, Mode=OneWayToSource}" />
</StackLayout>

现在,ViewModel的Multiplier属性是从Entry的Text属性设置的,而不是相反。如果您不需要从ViewModel更新这两个视图,则可以将它们都设置为OneWayToSource。但通常你会希望MVVM绑定是TwoWay。
您是否应该担心双向绑定中的无限循环?通常不会,因为PropertyChanged事件仅在属性实际更改时触发,而不是仅在它设置为相同值时触发。通常,源和目标将在反弹或反弹后停止相互更新。但是,可以编写一个“病态”值转换器,它不提供往返转换,并且确实可以在双向绑定中导致无限更新周期。

目录
相关文章
|
存储 SQL 前端开发
借一个项目谈Android应用软件架构,你还在套用MVP 或MVVM吗
借一个项目谈Android应用软件架构,你还在套用MVP 或MVVM吗
|
前端开发 Java 程序员
iOS开发 - 抛开表面看本质之iOS常用架构(MVC,MVP,MVVM)
iOS开发 - 抛开表面看本质之iOS常用架构(MVC,MVP,MVVM)
471 0
|
前端开发
[译] 实用的 MVVM 和 RxSwift
今天我们将使用 RxSwift 实现 MVVM 设计模式。对于那些刚接触 RxSwift 的人,我 在这里 专门做了一个部分来介绍。
1372 0
|
前端开发 JavaScript Android开发
|
前端开发 JavaScript Android开发
|
前端开发 JavaScript Android开发
|
前端开发 Android开发 Windows
|
前端开发 JavaScript Android开发
|
前端开发 JavaScript Android开发
|
JavaScript 前端开发 Android开发