Chinaunix首页 | 论坛 | 博客
  • 博客访问: 6541647
  • 博文数量: 915
  • 博客积分: 17977
  • 博客等级: 上将
  • 技术积分: 8846
  • 用 户 组: 普通用户
  • 注册时间: 2005-08-26 09:59
个人简介

一个好老好老的老程序员了。

文章分类

全部博文(915)

文章存档

2022年(9)

2021年(13)

2020年(10)

2019年(40)

2018年(88)

2017年(130)

2015年(5)

2014年(12)

2013年(41)

2012年(36)

2011年(272)

2010年(1)

2009年(53)

2008年(65)

2007年(47)

2006年(81)

2005年(12)

分类: Android平台

2017-11-26 18:57:07

Model-View-ViewModel(MVVM)体系结构模式是在XAML的基础上发明的。 该模式强制三个软件层之间的分离 - XAML用户界面,称为视图; 基础数据,称为模型; 以及View和Model之间的中介,称为ViewModel。 View和ViewModel通常通过XAML文件中定义的数据绑定进行连接。 视图的BindingContext通常是ViewModel的一个实例。

一个简单的ViewModel

作为ViewModels的介绍,我们先来看一个没有的程序。 早些时候,您看到了如何定义一个新的XML名称空间声明,以允许XAML文件引用其他程序集中的类。 这是一个为System命名空间定义XML名称空间声明的程序:

点击(此处)折叠或打开

  1. xmlns:sys="clr-namespace:System;assembly=mscorlib"
该程序可以使用x:Static从静态DateTime.Now属性获取当前日期和时间,并将该DateTime值设置为StackLayout上的BindingContext:

点击(此处)折叠或打开

  1. <StackLayout BindingContext="{x:Static sys:DateTime.Now}">
BindingContext是一个非常特殊的属性:当你在一个元素上设置BindingContext时,它被该元素的所有子元素继承。 这意味着StackLayout的所有子节点都具有相同的BindingContext,并且可以包含对该对象属性的简单绑定。
在One-Shot DateTime程序中,其中两个子项包含对该DateTime值的属性的绑定,但另外两个子项包含似乎缺少绑定路径的绑定。 这意味着DateTime值本身用于StringFormat:
	

点击(此处)折叠或打开

  1. <ContentPage xmlns=""
  2.              xmlns:x=""
  3.              xmlns:sys="clr-namespace:System;assembly=mscorlib"
  4.              x:Class="XamlSamples.OneShotDateTimePage"
  5.              Title="One-Shot DateTime Page">
  6.     <StackLayout BindingContext="{x:Static sys:DateTime.Now}"
  7.                  HorizontalOptions="Center"
  8.                  VerticalOptions="Center">
  9.         <Label Text="{Binding Year, StringFormat='The year is {0}'}" />
  10.         <Label Text="{Binding StringFormat='The month is {0:MMMM}'}" />
  11.         <Label Text="{Binding Day, StringFormat='The day is {0}'}" />
  12.         <Label Text="{Binding StringFormat='The time is {0:T}'}" />
  13.     </StackLayout>
  14. </ContentPage>

当然,最大的问题是,页面初建时的日期和时间是一次设置的,绝不会改变:

点击(此处)折叠或打开

  1. <ContentPage xmlns=""
  2.              xmlns:x=""
  3.              xmlns:sys="clr-namespace:System;assembly=mscorlib"
  4.              x:Class="XamlSamples.OneShotDateTimePage"
  5.              Title="One-Shot DateTime Page">

  6.     <StackLayout BindingContext="{x:Static sys:DateTime.Now}"
  7.                  HorizontalOptions="Center"
  8.                  VerticalOptions="Center">

  9.         <Label Text="{Binding Year, StringFormat='The year is {0}'}" />
  10.         <Label Text="{Binding StringFormat='The month is {0:MMMM}'}" />
  11.         <Label Text="{Binding Day, StringFormat='The day is {0}'}" />
  12.         <Label Text="{Binding StringFormat='The time is {0:T}'}" />

  13.     </StackLayout>
  14. </ContentPage>
当然,最大的问题是,页面初建时的日期和时间是一次设置的,绝不会改变:


一个XAML文件可以显示一个始终显示当前时间的时钟,但它需要一些代码来帮助。从MVVM的角度来看,Model和ViewModel是完全用代码编写的类。 View通常是一个XAML文件,通过数据绑定引用ViewModel中定义的属性。
一个合适的Model对于ViewModel是无知的,一个合适的ViewModel对这个View是无知的。但是,程序员通常会将ViewModel公开的数据类型定制为与特定用户界面相关的数据类型。例如,如果一个Model访问包含8位字符ASCII字符串的数据库,则ViewModel需要将这些字符串转换为Unicode字符串,以便在用户界面中独占使用Unicode。
在MVVM的简单例子中(例如这里所示的例子),通常根本不存在Model,而模式只涉及与数据绑定关联的View和ViewModel。
下面是一个时钟的ViewModel,只有一个名为DateTime的属性,但是每秒更新一次DateTime属性:

点击(此处)折叠或打开

  1. using System;
  2. using System.ComponentModel;
  3. using Xamarin.Forms;

  4. namespace XamlSamples
  5. {
  6.     class ClockViewModel : INotifyPropertyChanged
  7.     {
  8.         DateTime dateTime;

  9.         public event PropertyChangedEventHandler PropertyChanged;

  10.         public ClockViewModel()
  11.         {
  12.             this.DateTime = DateTime.Now;

  13.             Device.StartTimer(TimeSpan.FromSeconds(1), () =>
  14.                 {
  15.                     this.DateTime = DateTime.Now;
  16.                     return true;
  17.                 });
  18.         }

  19.         public DateTime DateTime
  20.         {
  21.             set
  22.             {
  23.                 if (dateTime != value)
  24.                 {
  25.                     dateTime = value;

  26.                     if (PropertyChanged != null)
  27.                     {
  28.                         PropertyChanged(this, new PropertyChangedEventArgs("DateTime"));
  29.                     }
  30.                 }
  31.             }
  32.             get
  33.             {
  34.                 return dateTime;
  35.             }
  36.         }
  37.     }
  38. }
ViewModels通常实现INotifyPropertyChanged接口,这意味着只要其中一个属性发生变化,该类就会触发一个PropertyChanged事件。 Xamarin.Forms中的数据绑定机制将一个处理程序附加到此PropertyChanged事件,以便在属性更改时通知它,并使目标更新为新值。
基于这个ViewModel的时钟可以像这样简单:

点击(此处)折叠或打开

  1. <ContentPage xmlns=""
  2.              xmlns:x=""
  3.              xmlns:local="clr-namespace:XamlSamples;assembly=XamlSamples"
  4.              x:Class="XamlSamples.ClockPage"
  5.              Title="Clock Page">

  6.     <Label Text="{Binding DateTime, StringFormat='{0:T}'}"
  7.            FontSize="Large"
  8.            HorizontalOptions="Center"
  9.            VerticalOptions="Center">
  10.         <Label.BindingContext>
  11.             <local:ClockViewModel />
  12.         </Label.BindingContext>
  13.     </Label>
  14. </ContentPage>
请注意ClockViewModel如何使用属性元素标签设置为Label的BindingContext。 或者,您可以在Resources集合中实例化ClockViewModel,并通过StaticResource标记扩展将其设置为BindingContext。 或者,代码隐藏文件可以实例化ViewModel。
标签的文本属性上的绑定标记扩展名格式的日期时间属性。 这是显示器:



通过使用句点分隔属性,也可以访问ViewModel的DateTime属性的单独属性:

点击(此处)折叠或打开

  1. <Label Text="{Binding DateTime.Second, StringFormat='{0}'}">



	

交互式MVVMWS

MVVM通常用于基于底层数据模型的交互式视图的双向数据绑定。
这是一个名为HslViewModel的类,它将Color值转换为Hue,Saturation和Luminosity值,反之亦然:

点击(此处)折叠或打开

  1. using System;
  2. using System.ComponentModel;
  3. using Xamarin.Forms;

  4. namespace XamlSamples
  5. {
  6.     public class HslViewModel : INotifyPropertyChanged
  7.     {
  8.         double hue, saturation, luminosity;
  9.         Color color;

  10.         public event PropertyChangedEventHandler PropertyChanged;

  11.         public double Hue
  12.         {
  13.             set
  14.             {
  15.                 if (hue != value)
  16.                 {
  17.                     hue = value;
  18.                     OnPropertyChanged("Hue");
  19.                     SetNewColor();
  20.                 }
  21.             }
  22.             get
  23.             {
  24.                 return hue;
  25.             }
  26.         }

  27.         public double Saturation
  28.         {
  29.             set
  30.             {
  31.                 if (saturation != value)
  32.                 {
  33.                     saturation = value;
  34.                     OnPropertyChanged("Saturation");
  35.                     SetNewColor();
  36.                 }
  37.             }
  38.             get
  39.             {
  40.                 return saturation;
  41.             }
  42.         }

  43.         public double Luminosity
  44.         {
  45.             set
  46.             {
  47.                 if (luminosity != value)
  48.                 {
  49.                     luminosity = value;
  50.                     OnPropertyChanged("Luminosity");
  51.                     SetNewColor();
  52.                 }
  53.             }
  54.             get
  55.             {
  56.                 return luminosity;
  57.             }
  58.         }

  59.         public Color Color
  60.         {
  61.             set
  62.             {
  63.                 if (color != value)
  64.                 {
  65.                     color = value;
  66.                     OnPropertyChanged("Color");

  67.                     Hue = value.Hue;
  68.                     Saturation = value.Saturation;
  69.                     Luminosity = value.Luminosity;
  70.                 }
  71.             }
  72.             get
  73.             {
  74.                 return color;
  75.             }
  76.         }

  77.         void SetNewColor()
  78.         {
  79.             Color = Color.FromHsla(Hue, Saturation, Luminosity);
  80.         }

  81.         protected virtual void OnPropertyChanged(string propertyName)
  82.         {
  83.             PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
  84.         }
  85.     }
  86. }
对“色相”,“饱和度”和“亮度”属性所做的更改会导致Color属性发生更改,而更改为Color将导致其他三个属性发生更改。 这可能看起来像一个无限循环,除非该类不调用PropertyChanged事件,除非该属性实际上已经改变。 这终止了不可控制的反馈回路。
以下XAML文件包含其Color属性绑定到ViewModel的Color属性的BoxView,以及绑定到Hue,Saturation和Luminosity属性的三个Slider和三个Label视图:

点击(此处)折叠或打开

  1. <ContentPage xmlns=""
  2.              xmlns:x=""
  3.              xmlns:local="clr-namespace:XamlSamples;assembly=XamlSamples"
  4.              x:Class="XamlSamples.HslColorScrollPage"
  5.              Title="HSL Color Scroll Page">
  6.     <ContentPage.BindingContext>
  7.         <local:HslViewModel Color="Aqua" />
  8.     </ContentPage.BindingContext>

  9.     <StackLayout Padding="10, 0">
  10.         <BoxView Color="{Binding Color}"
  11.                  VerticalOptions="FillAndExpand" />

  12.         <Label Text="{Binding Hue, StringFormat='Hue = {0:F2}'}"
  13.                HorizontalOptions="Center" />

  14.         <Slider Value="{Binding Hue, Mode=TwoWay}" />

  15.         <Label Text="{Binding Saturation, StringFormat='Saturation = {0:F2}'}"
  16.                HorizontalOptions="Center" />

  17.         <Slider Value="{Binding Saturation, Mode=TwoWay}" />

  18.         <Label Text="{Binding Luminosity, StringFormat='Luminosity = {0:F2}'}"
  19.                HorizontalOptions="Center" />

  20.         <Slider Value="{Binding Luminosity, Mode=TwoWay}" />
  21.     </StackLayout>
  22. </ContentPage>
每个Label上的绑定是默认的OneWay。 它只需要显示值。 但每个滑块的绑定是双向的。 这允许Slider从ViewModel初始化。 注意,当ViewModel被实例化时,Color属性被设置为蓝色。 但是滑块的改变也需要为ViewModel中的属性设置一个新的值,然后计算一个新的颜色。



用ViewModels命令

在许多情况下,MVVM模式仅限于处理ViewModel中View数据对象中的数据项:用户界面对象。
但是,View有时需要包含在ViewModel中触发各种操作的按钮。 但是ViewModel不能包含按钮的单击处理程序,因为这将把ViewModel绑定到特定的用户界面范例。
为了允许ViewModel更独立于特定的用户界面对象,但仍允许在ViewModel中调用方法,则存在命令界面。 Xamarin.Forms中的以下元素支持此命令接口:

  • Button
  • MenuItem
  • ToolbarItem
  • SearchBar
  • TextCell (ImageCell也是如此)
  • ListView
  • TapGestureRecognizer

除SearchBar和ListView元素外,这些元素定义了两个属性:

  • Command ,类型是System.Windows.Input.ICommand
  • CommandParameter,类型是Object

SearchBar定义了SearchCommand和SearchCommandParameter属性,而ListView定义了一个ICommand类型的RefreshCommand属性。
ICommand接口定义了两个方法和一个事件:


  • void Execute(object arg)
  • bool CanExecute(object arg)
  • event EventHandler CanExecuteChanged

ViewModel可以定义ICommand类型的属性。然后,您可以将这些属性绑定到每个Button或其他元素的Command属性,或者实现此接口的自定义视图。您可以选择设置CommandParameter属性来标识绑定到此ViewModel属性的各个Button对象(或其他元素)。在内部,只要用户点击Button,传递给Execute方法的CommandParameter,Button就会调用Execute方法。
CanExecute方法和CanExecuteChanged事件用于Button按钮可能当前无效的情况,在这种情况下,Button应该禁用它自己。当Command属性第一次被设置和CanExecuteChanged事件被触发时,Button调用CanExecute。如果CanExecute返回false,则Button将自行禁用,并不会生成执行调用。
这两个类定义了几个构造函数以及ViewModel可以调用的ChangeCanExecute方法来强制Command对象触发CanExecuteChanged事件。
这是一个用于输入电话号码的简单键盘的ViewModel。注意Execute和CanExecute方法在构造函数中被定义为lambda函数:

点击(此处)折叠或打开

  1. using System;
  2. using System.ComponentModel;
  3. using System.Windows.Input;
  4. using Xamarin.Forms;

  5. namespace XamlSamples
  6. {
  7.     class KeypadViewModel : INotifyPropertyChanged
  8.     {
  9.         string inputString = "";
  10.         string displayText = "";
  11.         char[] specialChars = { '*', '#' };

  12.         public event PropertyChangedEventHandler PropertyChanged;

  13.         // Constructor
  14.         public KeypadViewModel()
  15.         {
  16.             AddCharCommand = new Command<string>((key) =>
  17.                 {
  18.                     // Add the key to the input string.
  19.                     InputString += key;
  20.                 });

  21.             DeleteCharCommand = new Command(() =>
  22.                 {
  23.                     // Strip a character from the input string.
  24.                     InputString = InputString.Substring(0, InputString.Length - 1);
  25.                 },
  26.                 () =>
  27.                 {
  28.                     // Return true if there's something to delete.
  29.                     return InputString.Length > 0;
  30.                 });
  31.         }

  32.         // Public properties
  33.         public string InputString
  34.         {
  35.             protected set
  36.             {
  37.                 if (inputString != value)
  38.                 {
  39.                     inputString = value;
  40.                     OnPropertyChanged("InputString");
  41.                     DisplayText = FormatText(inputString);

  42.                     // Perhaps the delete button must be enabled/disabled.
  43.                     ((Command)DeleteCharCommand).ChangeCanExecute();
  44.                 }
  45.             }

  46.             get { return inputString; }
  47.         }

  48.         public string DisplayText
  49.         {
  50.             protected set
  51.             {
  52.                 if (displayText != value)
  53.                 {
  54.                     displayText = value;
  55.                     OnPropertyChanged("DisplayText");
  56.                 }
  57.             }
  58.             get { return displayText; }
  59.         }

  60.         // ICommand implementations
  61.         public ICommand AddCharCommand { protected set; get; }

  62.         public ICommand DeleteCharCommand { protected set; get; }

  63.         string FormatText(string str)
  64.         {
  65.             bool hasNonNumbers = str.IndexOfAny(specialChars) != -1;
  66.             string formatted = str;

  67.             if (hasNonNumbers || str.Length < 4 || str.Length > 10)
  68.             {
  69.             }
  70.             else if (str.Length < 8)
  71.             {
  72.                 formatted = String.Format("{0}-{1}",
  73.                                           str.Substring(0, 3),
  74.                                           str.Substring(3));
  75.             }
  76.             else
  77.             {
  78.                 formatted = String.Format("({0}) {1}-{2}",
  79.                                           str.Substring(0, 3),
  80.                                           str.Substring(3, 3),
  81.                                           str.Substring(6));
  82.             }
  83.             return formatted;
  84.         }

  85.         protected void OnPropertyChanged(string propertyName)
  86.         {
  87.             PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
  88.         }
  89.     }
  90. }
这个ViewModel假定AddCharCommand属性绑定到几个按钮的Command属性(或其他任何具有命令接口的属性),每个按钮都由CommandParameter标识。 这些按钮将字符添加到InputString属性,然后将其格式化为DisplayText属性的电话号码。
另外还有一个名为DeleteCharCommand的ICommand类型的第二个属性。 这是绑定到一个后退间隔按钮,但该按钮应该被禁用,如果没有字符删除。
下面的键盘不像视觉上那么复杂。 相反,标记已经降到最低,以更清楚地展示命令接口的使用:

点击(此处)折叠或打开

  1. <ContentPage xmlns=""
  2.              xmlns:x=""
  3.              xmlns:local="clr-namespace:XamlSamples;assembly=XamlSamples"
  4.              x:Class="XamlSamples.KeypadPage"
  5.              Title="Keypad Page">

  6.     <Grid HorizontalOptions="Center"
  7.           VerticalOptions="Center">
  8.         <Grid.BindingContext>
  9.             <local:KeypadViewModel />
  10.         </Grid.BindingContext>

  11.         <Grid.RowDefinitions>
  12.             <RowDefinition Height="Auto" />
  13.             <RowDefinition Height="Auto" />
  14.             <RowDefinition Height="Auto" />
  15.             <RowDefinition Height="Auto" />
  16.             <RowDefinition Height="Auto" />
  17.         </Grid.RowDefinitions>

  18.         <Grid.ColumnDefinitions>
  19.             <ColumnDefinition Width="80" />
  20.             <ColumnDefinition Width="80" />
  21.             <ColumnDefinition Width="80" />
  22.         </Grid.ColumnDefinitions>

  23.         <!-- Internal Grid for top row of items -->
  24.         <Grid Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="3">
  25.             <Grid.ColumnDefinitions>
  26.                 <ColumnDefinition Width="*" />
  27.                 <ColumnDefinition Width="Auto" />
  28.             </Grid.ColumnDefinitions>

  29.             <Frame Grid.Column="0"
  30.                    OutlineColor="Accent">
  31.                 <Label Text="{Binding DisplayText}" />
  32.             </Frame>

  33.             <Button Text="?"
  34.                     Command="{Binding DeleteCharCommand}"
  35.                     Grid.Column="1"
  36.                     BorderWidth="0" />
  37.         </Grid>

  38.         <Button Text="1"
  39.                 Command="{Binding AddCharCommand}"
  40.                 CommandParameter="1"
  41.                 Grid.Row="1" Grid.Column="0" />

  42.         <Button Text="2"
  43.                 Command="{Binding AddCharCommand}"
  44.                 CommandParameter="2"
  45.                 Grid.Row="1" Grid.Column="1" />

  46.         <Button Text="3"
  47.                 Command="{Binding AddCharCommand}"
  48.                 CommandParameter="3"
  49.                 Grid.Row="1" Grid.Column="2" />

  50.         <Button Text="4"
  51.                 Command="{Binding AddCharCommand}"
  52.                 CommandParameter="4"
  53.                 Grid.Row="2" Grid.Column="0" />

  54.         <Button Text="5"
  55.                 Command="{Binding AddCharCommand}"
  56.                 CommandParameter="5"
  57.                 Grid.Row="2" Grid.Column="1" />

  58.         <Button Text="6"
  59.                 Command="{Binding AddCharCommand}"
  60.                 CommandParameter="6"
  61.                 Grid.Row="2" Grid.Column="2" />

  62.         <Button Text="7"
  63.                 Command="{Binding AddCharCommand}"
  64.                 CommandParameter="7"
  65.                 Grid.Row="3" Grid.Column="0" />

  66.         <Button Text="8"
  67.                 Command="{Binding AddCharCommand}"
  68.                 CommandParameter="8"
  69.                 Grid.Row="3" Grid.Column="1" />

  70.         <Button Text="9"
  71.                 Command="{Binding AddCharCommand}"
  72.                 CommandParameter="9"
  73.                 Grid.Row="3" Grid.Column="2" />

  74.         <Button Text="*"
  75.                 Command="{Binding AddCharCommand}"
  76.                 CommandParameter="*"
  77.                 Grid.Row="4" Grid.Column="0" />

  78.         <Button Text="0"
  79.                 Command="{Binding AddCharCommand}"
  80.                 CommandParameter="0"
  81.                 Grid.Row="4" Grid.Column="1" />

  82.         <Button Text="#"
  83.                 Command="{Binding AddCharCommand}"
  84.                 CommandParameter="#"
  85.                 Grid.Row="4" Grid.Column="2" />
  86.     </Grid>
  87. </ContentPage>
出现在该标记中的第一个Button的Command属性绑定到DeleteCharCommand; 剩下的都绑定到AddCharCommand,CommandParameter与Button面上出现的字符相同。 以下是正在实施的计划:



调用异步方法

命令也可以调用异步方法。 这是通过在指定Execute方法时使用async和await关键字来实现的:

点击(此处)折叠或打开

  1. DownloadCommand = new Command (async () => await DownloadAsync ());
这表明DownloadAsync方法是一个任务,应该等待:

点击(此处)折叠或打开

  1. async Task DownloadAsync ()
  2. {
  3.     await Task.Run (() => Download ());
  4. }

  5. void Download ()
  6. {
  7.     ...
  8. }



	

实现一个导航菜单

包含本系列文章中所有源代码的XamlSamples程序使用ViewModel作为其主页。 这个ViewModel是一个短类的定义,它有三个名为Type,Title和Description的属性,它们包含了每个样例页面的类型,一个标题和一个简短描述。 另外,ViewModel定义了一个名为All的静态属性,它是程序中所有页面的集合:

点击(此处)折叠或打开

  1. public class PageDataViewModel
  2. {
  3.     public PageDataViewModel(Type type, string title, string description)
  4.     {
  5.         Type = type;
  6.         Title = title;
  7.         Description = description;
  8.     }

  9.     public Type Type { private set; get; }

  10.     public string Title { private set; get; }

  11.     public string Description { private set; get; }

  12.     static PageDataViewModel()
  13.     {
  14.         All = new List<PageDataViewModel>
  15.         {
  16.             // Part 1. Getting Started with XAML
  17.             new PageDataViewModel(typeof(HelloXamlPage), "Hello, XAML",
  18.                                   "Display a Label with many properties set"),

  19.             new PageDataViewModel(typeof(XamlPlusCodePage), "XAML + Code",
  20.                                   "Interact with a Slider and Button"),

  21.             // Part 2. Essential XAML Syntax
  22.             new PageDataViewModel(typeof(GridDemoPage), "Grid Demo",
  23.                                   "Explore XAML syntax with the Grid"),

  24.             new PageDataViewModel(typeof(AbsoluteDemoPage), "Absolute Demo",
  25.                                   "Explore XAML syntax with AbsoluteLayout"),

  26.             // Part 3. XAML Markup Extensions
  27.             new PageDataViewModel(typeof(SharedResourcesPage), "Shared Resources",
  28.                                   "Using resource dictionaries to share resources"),

  29.             new PageDataViewModel(typeof(StaticConstantsPage), "Static Constants",
  30.                                   "Using the x:Static markup extensions"),

  31.             new PageDataViewModel(typeof(RelativeLayoutPage), "Relative Layout",
  32.                                   "Explore XAML markup extensions"),

  33.             // Part 4. Data Binding Basics
  34.             new PageDataViewModel(typeof(SliderBindingsPage), "Slider Bindings",
  35.                                   "Bind properties of two views on the page"),

  36.             new PageDataViewModel(typeof(SliderTransformsPage), "Slider Transforms",
  37.                                   "Use Sliders with reverse bindings"),

  38.             new PageDataViewModel(typeof(ListViewDemoPage), "ListView Demo",
  39.                                   "Use a ListView with data bindings"),

  40.             // Part 5. From Data Bindings to MVVM
  41.             new PageDataViewModel(typeof(OneShotDateTimePage), "One-Shot DateTime",
  42.                                   "Obtain the current DateTime and display it"),

  43.             new PageDataViewModel(typeof(ClockPage), "Clock",
  44.                                   "Dynamically display the current time"),

  45.             new PageDataViewModel(typeof(HslColorScrollPage), "HSL Color Scroll",
  46.                                   "Use a view model to select HSL colors"),

  47.             new PageDataViewModel(typeof(KeypadPage), "Keypad",
  48.                                   "Use a view model for numeric keypad logic")
  49.         };
  50.     }

  51.     public static IList<PageDataViewModel> All { private set; get; }
  52. }
MainPage的XAML文件定义了一个ListBox,其ItemsSource属性被设置为All属性,并包含一个TextCell用于显示每个页面的Title和Description属性:

点击(此处)折叠或打开

  1. <ContentPage xmlns=""
  2.              xmlns:x=""
  3.              xmlns:local="clr-namespace:XamlSamples"
  4.              x:Class="XamlSamples.MainPage"
  5.              Padding="5, 0"
  6.              Title="XAML Samples">

  7.     <ListView ItemsSource="{x:Static local:PageDataViewModel.All}"
  8.               ItemSelected="OnListViewItemSelected">
  9.         <ListView.ItemTemplate>
  10.             <DataTemplate>
  11.                 <TextCell Text="{Binding Title}"
  12.                           Detail="{Binding Description}" />
  13.             </DataTemplate>
  14.         </ListView.ItemTemplate>
  15.     </ListView>
  16. </ContentPage>
页面显示在一个可滚动列表中:



代码隐藏文件中的处理程序在用户选择某个项目时被触发。 该处理程序将ListBox的SelectedItem属性设置为null,然后实例化所选页面并导航到它:

点击(此处)折叠或打开

  1. private async void OnListViewItemSelected(object sender, SelectedItemChangedEventArgs args)
  2. {
  3.     (sender as ListView).SelectedItem = null;

  4.     if (args.SelectedItem != null)
  5.     {
  6.         PageDataViewModel pageData = args.SelectedItem as PageDataViewModel;
  7.         Page page = (Page)Activator.CreateInstance(pageData.Type);
  8.         await Navigation.PushAsync(page);
  9.     }
  10. }



	

概要

XAML是在Xamarin.Forms应用程序中定义用户界面的强大工具,特别是在使用数据绑定和MVVM时。 其结果是一个干净,优雅,并可能toolable表示的用户界面代码中的所有后台支持。

阅读(7223) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~