热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

【xamarin+MvvmCross从零开始】四、MvvmCross详解(2)

前言上篇我们说明了几个在MvvmCross中常用的对象,这次我们讲一下MvvmCross的数据绑定。什么是数据绑定数据绑定是MvvmCross的至关重要的特性,通过代码或XA
前言

上篇我们说明了几个在MvvmCross中常用的对象,这次我们讲一下MvvmCross的数据绑定。

什么是数据绑定

数据绑定是MvvmCross的至关重要的特性,通过代码或XAML方式建立View与ViewModel之间的关联,以达到数据呈现、交互的目的。

那什么是数据绑定呢?

  • 建立View和ViewModel中属性之间的关联
  • 可以指定BindingMode来控制数据的流向
  • 可以指定ValueConvert实现数据的双向转换,在使用ValueConvert时可以通过参数实现定制转换
  • 可以指定FallbackValue,当数据绑定失败时作为默认结果使用
ViewModel定义

我们来看一个典型的ViewModel是如何定义的

using System.Windows.Input;
using MvvmCross.Core.ViewModels;

namespace XamarinSample.ViewModels
{
    public class UserViewModel: MvxViewModel
    {
        private string _name;
        private int _age;
        private string _password;

        /// 
        /// 用户名
        /// 
        public string Name
        {
            get { return _name; }
            set
            {
                _name = value;
                // 触发属性 Name 变更
                RaisePropertyChanged(() => Name);
            }
        }

        /// 
        /// 年龄
        /// 
        public int Age
        {
            get { return _age; }
            set
            {
                _age = value;
                RaisePropertyChanged(() => Age);
            }
        }

        /// 
        /// 密码
        /// 
        public string Password
        {
            get { return _password; }
            set
            {
                _password = value;
                RaisePropertyChanged(() => Password);
            }
        }

        private ICommand _saveCommand;

        /// 
        /// 保存用户信息Command,供绑定保存操作使用
        /// 
        public ICommand SaveCommand => _saveCommand ?? (_saveCommand = new MvxCommand(Save));

        private void Save()
        {
            // 保存User数据
        }
    }
}

在MvvmCross中所有的ViewModel需要实现IMvxViewModel接口,MvvmCross也实现了基本的ViewModel,所以只需要从MvxViewModel继承就可以。

所有可以绑定数据必须是属性,并且如果要实现数据变更通知,需要在数据变化时调用RaisePropertyChanged 方法。当给Name赋值时,将触发INotityPropertyChange接口中的PropertyChanged事件,View层通过对PropertyChanged事件的接收而实现View层的响应。

Mvvm 不支持直接对方法进行绑定,若要实现对方法的绑定,需要将方法转换为ICommand类型的属性,再进行绑定。这里我们将Save方法封装为SaveCommand后就可以将SaveCommand方法绑定到View 中。

绑定模式

One-Way

  • 单向绑定,由ViewModel向View单向的数据绑定。
  • 当ViewModel的数据发生变时,View显示的数据也会同时发生变化。
  • 一般应用于数据展示,比如天气预报数据显示,ViewModel由服务器更新数据后,呈现界面自动更新。
  • 单向绑定在Xaml绑定为默认的绑定模式。

One-Way-To-Source

  • 由界面流向数据源的单向绑定。
  • 比如用户填表,只需要采集用户数据,不需要向用户进行呈现。
  • 这种绑定的模式不经常使用。

Two-Way

  • 双向数据绑定,当ViewModel发生变化时,View也相应发生变化;当用户在界面更新数据或都输入时,ViewModel数据也发生变化。
  • 一般应用于数据编辑等,如用户信息修改,用户在View修改信息时,ViewModel也相应的发生变化。

 

One-Time

  • 一次性绑定。数据的方向为由ViewModel向View绑定。
  • 一般只在界面第一次初始化绑定时获取数据,以后不管ViewModel如何变化,View的呈现都不发生变化。
  • 这种模式不经常使用。一般在显示固定内容时使用,如固定的标签、不会发生变化的背景图片等。

 

数据转换(ValueConverter)

数据转换是一个实现了IValueConverter接口的一个对象。我们来看一下接口的定义:

image

接口主要包括两个方法:

Convert:实现将ViewModel中的数据转换为支持或方便View呈现的数据。如数据的格式化、布尔型数据转换为是/否等可方便识别的内容等。

ConvertBack:与Convert相反,将View中的数据转换为方便持久化的数据。

通过接口中可以看到,转换支持一个object类型的参数对象。表示在使用ValueConvert时,可以传入一个自定义的参数,极大的提高了转换的灵活性。如在将float转换为字符串时,可以传入一个整形的参数,用来标识转换结果需要保存的小数位数。

接口可以根据需要进行实现,如一个One-Way绑定使用的转换,就没有必要实现ConvertBack部分。

数据转换在MvvmCross中一个通用的特性。这就表示只用在PCL程序集中实现一次数据绑定接口,就可以在Android、iOS、WP等多个平台上使用。

备选值(Fallback Values)

有时ViewModel中的值不是一个可用的值,那这时就需要使用备选值。如显示用户所属的部门数据时,正常情况下需要将关联的部门显示为部门的名称,但当用户并没有设置关联的部门信息时,显示为空显示不太友好,这时可以使用备选值,友好的显示为[未设置部门信息]。

数据绑定(Data Binding)

数据绑定是MvvmCross的核心内容。随着MvvmCross的不断更新,绑定的方式也有很多种。

JSON 绑定

这种绑定方式做为较早的绑定方式,目前已经不推荐使用,这里我们就不做说明了。

Swiss 绑定

Swiss绑定是Json绑定的替代方式,就绑定的语法而言,较JSON绑定方式有极大的简化。

Swiss绑定的语法:

$Target$ $SourcePath$
Target必须是View某个对象的直接属性,比如:
  • Text,控件的Text属性
  • IsChecked,可选控件的IsChecked属性
  • Value,控件的值
  • ……

SourcePath为ViewModel中某个属性或都子对象的某个属性,也就是说可以根据需要指定多级关联属性,比如:

  • UserId
  • RememberMe
  • Password
  • Customer.FirstName
  • Customer.Address.City
  • Customer.Orders[0].Date
  • Customer.Orders[0].Total
  • Customer.Cards["Primary"].Expiry
  • Customer.Cards["Primary"].Number

SourcePath除了以上语法以外,还有一些特殊的用法

  • 如果直接使用 . (点)的话,则是说明绑定的对象为当前的整个ViewModel
  • 如果需要对绑定的ViewModel属性进行转换,可以添加一个Converter,语法如下:

         , COnverter=$ConverterName$

  • 在使用Converter时,可根据需要使用ConverterParameter:

         , COnverterParameter=$ParameterValue$

        ConverterName值可以是以下的一种:

      • 使用单引号或双引号限定,表示字符串
      • null代表C#中的null值
      • 整型值,MvvmCross将解释为 长整型long
      • 浮点数,MvvmCross将解释为 双精度 数值 double
  • 可以根据需要添加FallbackValue:

          , FallbackValue=$FallbackValue$

         FallbackValue的赋值语法基本和ParameterValue一样,再加上枚举值的ToString()方法。

  • 如果需要改变BindMode,可以添加BindMode:

          ,Mode=$BindMode$

      • OneWay
      • OneWayToSource
      • TwoWay
      • OneTime
      • Default
  • 如果需要在同一个对象上绑定多个值,需要要每个不同的绑定值之间用分号(;)进行分隔。
  • 如果绑定的值是一个ICommand类型的属性,可根据需要添加CommandParameter:

          , CommandParameter=$CPValue$

          在这里CPValue的取值与ParameterValue一样

 

下面我们看几个Swiss绑定的例子:

Text Customer.FirstName
将View中某个对象的Text属性绑定到ViewModel中的Customer.FirstName属性
 
Text Title, COnverter=Length
将View中某个对象的Text属性绑定以ViewModel中的Title属性,并使用转换器 Length 进行数据转换
 
Text Order.Amount, COnverter=Trim, COnverterParameter='£'
将View中某个对象的Text属性绑定到ViewModel中的Order.Amount属性,并使用转换器 Trim,转换器参数为字符串'£'
 
Value Count, Mode=TwoWay
将View中某个对象的Value属性绑定到ViewModel中的Count属性,设置绑定的模式为双向绑定
 
Click DayCommand, CommandParameter='Thursday'
将View中的某个对象的Click事件绑定到ViewModel中的dayCommand 属性,并使用命令参数 'Thursday'
 

 

Fluent 绑定

Fluent绑定做为一种新的绑定方式,可以通过C#代码进行数据的绑定。由于在iOS或MacOS等平台上不方便使用Xaml方式的绑定,数据的绑定就使用Fluent绑定。

Fluent绑定方式在编码时可以充分利用VS的智能提示功能,并且能写出干净整洁的代码,方便阅读。

 

Fluent绑定通常情况下是通过 CreateBindingSet 创建视图与数据源的绑定关系。

绑定的语法包括:

  • Binding($BindObject$)

       指定绑定对象,$BindObject$是指View中的某个对象。

  • For(v => v.$ViewProperty$)
    $ViewProperty$指定绑定对象的属性,这里只能是关联对象的直接属性。部分对象可以不指定For语句,则$ViewProperty$将使用默认属性。如UIButton的默认属性是Text属性。
  • To(vm => vm.$ViewModelPath$)

    $ViewModelPath$是ViewModel中的属性路径,可以是多级属性,如User.FirstName

  • 绑定模式
      OneWay()
      TwoWay()
      OneWayToSource()
      OneTime()
 
  • WithConversion($name$, $parameter$)

       指定数据转换器。$name$是指定转换器的名称,$parameter$是指当前转换器的参数

  • 使用Fluent绑定,在绑定结束后必须调用Apply()方法,否则绑定不会起作用。

 

我们看几个例子:

 var set = this.CreateBindingSet();
 set.Bind(nameLabel)
    .For(v => v.Text)
    .To(vm => vm.Customer.FirstName);
 set.Bind(creditLabel)
    .For(v => v.Text)
    .To(vm => vm.Customer.Total)
    .WithConversion("CurrencyFormat", "$");
 set.Bind(cardLabel)
    .For(v => v.Text)
    .To(vm => vm.Customer.Cards["Primary"].Number)
    .WithConversion("LastFour")
    .OneWay()
    .FallbackValue("N/A");
 set.Bind(warningView)
    .For(v => v.Hidden)
    .To(vm => vm.Customer.Alert)
    .WithConversion("Not")
    .FallbackValue(true);
 set.Apply();

 

Tibet 绑定

Tibet绑定与Swiss绑定完全兼容,并且在Swiss绑定上进行了扩展,主要扩展以下几个部分:

  • multi-binding
  • value combiners
  • literal-binding
  • binding macros
  • functional syntax for ValueConverters and ValueCombiners
  • nested value conversion

 

多值绑定 (multi-binding)

在Swiss绑定中,每次只能绑定一个ViewModel属性,当需要绑定多个属性时,就只能通过在ViewModel中添加属性来实现,这样很不友好。

private string _firstName;
public string FirstName
{
   get { return _firstName; }
   set
   {
      _firstName = value;
      RaisePropertyChanged(() => FirstName);
      RaisePropertyChanged(() => FullName);
   }
}

private string _lastName;
public string LastName
{
   get { return _lastName; }
   set
   {
      _lastName = value;
      RaisePropertyChanged(() => LastName);
      RaisePropertyChanged(() => FullName);
   }
}

public string FullName
{
   get { return _firstName + "" + _lastName; }
}

在示例代码中,如果我们想要在View上显示FullName,就需要在ViewModel中添加一个属性FullName,这样才能绑定到View中,在Tibet绑定中,我们可以这样写:

Text FirstName + ' ' + LastName
这样,MvvmCross实际将这个绑定解析为三个绑定值 FirstName、 ' ' 、LastName, 当FirstName 或 LastName 发生变化时,相应的绑定对象的值都会发生变化。

值连接 (value combiners)

类似的值连接Tibet还实现了一些:
  • If(test, if_true, if_false) - 接受三个输入值,类似于三元运算
    • 一个bool型的表达式
    • 当表达式为true时所返回的值
    • 当表达式为false时所返回的值
  • Format(format, args…) - 字符串格式化,同string.Format用法一致 
    • 字符串格式化模板
    • 0或多个字符串格式化值
  • Add(one,two) -  接受两个参数。当两个参数类型为字符串时,返回两个字符串的连接值;当两个值为数值时,返回两个数值相加的结果。
  • GreaterThan(one, two) - 接受两个参数进行大于运算,参数类型可以字符串、浮点型或是整型数据。

由于绑定字符串不需要编译,而在支行时通过MvvmCross进行解释执行,所以某些绑定不是支持所有平台。而且某些复杂的表达式在解释时可能与所期望的结果不同。

语义绑定 (literal-binding)

绑定对很多简单的表达式是可以执行的

  • Value 100 * Ratio

       绑定常量值或是运算表达式

  • Value ‘True’

       bool 型的属性可以通 True 或 False 绑定常量值

  • Value  Format('Hello {1} - today is {0:ddd MMM yyyy}', TheDate, Name)

    绑定的Format表达式

Rio 绑定及其它

关于数据绑定,MvvmCross还会逐步增强,包括语法的简化和功能增强,后期会增加宏绑定等内容。

 

小结

本篇内容主要讲解了MvvmCross中数据绑定的方式。在实际的开发过程中主要使用Swiss和Fluent绑定。习惯通过XAML绑定的同学可以使用Swiss绑定,在不能使用Swiss或是习惯通过c#代码进行数据绑定的同学可以使用Fluent绑定。

下篇我们讲下ViewModel对象的生命周期以及如何通过ViewModel进行导航。


推荐阅读
  • 阿,里,云,物,联网,net,core,客户端,czgl,aliiotclient, ... [详细]
  • YOLOv7基于自己的数据集从零构建模型完整训练、推理计算超详细教程
    本文介绍了关于人工智能、神经网络和深度学习的知识点,并提供了YOLOv7基于自己的数据集从零构建模型完整训练、推理计算的详细教程。文章还提到了郑州最低生活保障的话题。对于从事目标检测任务的人来说,YOLO是一个熟悉的模型。文章还提到了yolov4和yolov6的相关内容,以及选择模型的优化思路。 ... [详细]
  • Java在运行已编译完成的类时,是通过java虚拟机来装载和执行的,java虚拟机通过操作系统命令JAVA_HOMEbinjava–option来启 ... [详细]
  • 本文介绍了在开发Android新闻App时,搭建本地服务器的步骤。通过使用XAMPP软件,可以一键式搭建起开发环境,包括Apache、MySQL、PHP、PERL。在本地服务器上新建数据库和表,并设置相应的属性。最后,给出了创建new表的SQL语句。这个教程适合初学者参考。 ... [详细]
  • 本文主要解析了Open judge C16H问题中涉及到的Magical Balls的快速幂和逆元算法,并给出了问题的解析和解决方法。详细介绍了问题的背景和规则,并给出了相应的算法解析和实现步骤。通过本文的解析,读者可以更好地理解和解决Open judge C16H问题中的Magical Balls部分。 ... [详细]
  • http:my.oschina.netleejun2005blog136820刚看到群里又有同学在说HTTP协议下的Get请求参数长度是有大小限制的,最大不能超过XX ... [详细]
  • Android JSON基础,音视频开发进阶指南目录
    Array里面的对象数据是有序的,json字符串最外层是方括号的,方括号:[]解析jsonArray代码try{json字符串最外层是 ... [详细]
  • 本文详细介绍了Spring的JdbcTemplate的使用方法,包括执行存储过程、存储函数的call()方法,执行任何SQL语句的execute()方法,单个更新和批量更新的update()和batchUpdate()方法,以及单查和列表查询的query()和queryForXXX()方法。提供了经过测试的API供使用。 ... [详细]
  • 前景:当UI一个查询条件为多项选择,或录入多个条件的时候,比如查询所有名称里面包含以下动态条件,需要模糊查询里面每一项时比如是这样一个数组条件:newstring[]{兴业银行, ... [详细]
  • 本文介绍了如何使用C#制作Java+Mysql+Tomcat环境安装程序,实现一键式安装。通过将JDK、Mysql、Tomcat三者制作成一个安装包,解决了客户在安装软件时的复杂配置和繁琐问题,便于管理软件版本和系统集成。具体步骤包括配置JDK环境变量和安装Mysql服务,其中使用了MySQL Server 5.5社区版和my.ini文件。安装方法为通过命令行将目录转到mysql的bin目录下,执行mysqld --install MySQL5命令。 ... [详细]
  • 本文讨论了Kotlin中扩展函数的一些惯用用法以及其合理性。作者认为在某些情况下,定义扩展函数没有意义,但官方的编码约定支持这种方式。文章还介绍了在类之外定义扩展函数的具体用法,并讨论了避免使用扩展函数的边缘情况。作者提出了对于扩展函数的合理性的质疑,并给出了自己的反驳。最后,文章强调了在编写Kotlin代码时可以自由地使用扩展函数的重要性。 ... [详细]
  • 本文讨论了在openwrt-17.01版本中,mt7628设备上初始化启动时eth0的mac地址总是随机生成的问题。每次随机生成的eth0的mac地址都会写到/sys/class/net/eth0/address目录下,而openwrt-17.01原版的SDK会根据随机生成的eth0的mac地址再生成eth0.1、eth0.2等,生成后的mac地址会保存在/etc/config/network下。 ... [详细]
  • 本文详细介绍了如何使用MySQL来显示SQL语句的执行时间,并通过MySQL Query Profiler获取CPU和内存使用量以及系统锁和表锁的时间。同时介绍了效能分析的三种方法:瓶颈分析、工作负载分析和基于比率的分析。 ... [详细]
  • Imtryingtofigureoutawaytogeneratetorrentfilesfromabucket,usingtheAWSSDKforGo.我正 ... [详细]
  • Spring常用注解(绝对经典),全靠这份Java知识点PDF大全
    本文介绍了Spring常用注解和注入bean的注解,包括@Bean、@Autowired、@Inject等,同时提供了一个Java知识点PDF大全的资源链接。其中详细介绍了ColorFactoryBean的使用,以及@Autowired和@Inject的区别和用法。此外,还提到了@Required属性的配置和使用。 ... [详细]
author-avatar
你不懂_de_笑
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有