1. 程式人生 > >C#使用Xamarin開發可移植移動應用(3.進階篇MVVM雙向綁定和命令綁定)附源碼

C#使用Xamarin開發可移植移動應用(3.進階篇MVVM雙向綁定和命令綁定)附源碼

www. 變更 ges ntp image 上下文 判斷 接口 images

前言

系列目錄

C#使用Xamarin開發可移植移動應用目錄

源碼地址:https://github.com/l2999019/DemoApp

可以Star一下,隨意 - -

說點什麽..

嗯..前面3篇就是基礎內容..後面就開始逐漸要加深了,進階篇開始了.

今天的學習內容?

今天我們講講Xamarin中的MVVM雙向綁定,嗯..需要有一定的MVVM基礎.,具體什麽是MVVM - -,請百度,我就不多講了

效果如下:

技術分享

正文

1.簡單的入門Demo

這個時間的功能很簡單,就是一個時間的動態顯示.

我們首先創建一個基礎的頁面如下:

<?xml version="1.0" encoding="utf-8" 
?> <ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:local="clr-namespace:DemoApp.MVVMDemo.ViewModel" x:Class="DemoApp.MVVMDemo.MVVMPageDemo"> <ContentPage.Content> <
StackLayout > <Label Text="{Binding DateTime,StringFormat=‘{0:F}‘}"> <Label.BindingContext> <local:TimeViewModel></local:TimeViewModel> </Label.BindingContext> </Label> </
StackLayout> </ContentPage.Content> </ContentPage>

大家可以發現,我們這次多了一些內容.

首先,我們會發現ContentPage的xmlns定義中多了一個local的定義.這個很重要,他是用來讓我們在xaml中引用其他程序集中的類,類似於Using的作用.

剩下的BindingContext和Bingding關鍵字,後面我們慢慢講

接下來,我們創建一個ViewModel的類如下:

  public class TimeViewModel : INotifyPropertyChanged
    {
        //定義一個時間類型
        DateTime dateTime;

        //實現接口的事件屬性
        public event PropertyChangedEventHandler PropertyChanged;

        //創建構造函數,定義一個定時執行程序
        public TimeViewModel()
        {
            this.DateTime = DateTime.Now;

            //定義定時執行程序,1秒刷新一下時間屬性
            Device.StartTimer(TimeSpan.FromSeconds(1), () =>
            {
                this.DateTime = DateTime.Now;
                return true;
            });
        }

        //定義時間屬性,創建SetGet方法,在Set中使用PropertyChanged事件,來更新這個時間
        public DateTime DateTime
        {
            set
            {
                if (dateTime != value)
                {
                    dateTime = value;

                    if (PropertyChanged != null)
                    {
                        PropertyChanged(this,
                            new PropertyChangedEventArgs("DateTime"));
                    }
                }
            }
            get
            {
                return dateTime;
            }
        }
    }

我們繼承了INotifyPropertyChanged,從類名就可以看出來,這個是關於實現屬性變更事件的一個接口.

他包含一個PropertyChanged,屬性變更事件,我們需要在每個屬性變更的時候(也就是Set中),調用它

在具體的開發過程中,如果你需要使用MVVM那麽你所有的ViewModel都應該繼承它.

很多解釋我都寫在了註釋裏面,請仔細看註釋

然後我們回到Xaml中的BindingContext,它的作用就一目了然了,給這個Xaml控件,綁定一個上下文對象,也就是你定義的ViewModel,來方便你綁定其中的屬性

<Label Text="{Binding DateTime,StringFormat=‘{0:F}‘}"> 這句的意思就是,綁定其中的DateTime屬性,並格式化顯示.

我們在構造函數中啟動的定時程序,就會一直更新DateTime,對應的,頁面上也會一直隨著變更.這樣我們就實現了一個基礎的MVVM

效果如圖:

技術分享

2.學會與控件相聯系,並綁定命令事件

通過上面的小栗子,我們學習了一下基本的綁定關系和綁定方法.

那麽下面就來一個比較復雜,比較難的例子.效果是這樣的,如圖:

技術分享

我們創建三個數值,他們與控件Slider來綁定,並控制.更新值的同時,求和.得到NumSun的值.

在界面中,我們有一個清空的Button來清除這個ViewModel中的值.

首先,我們創建xaml代碼如下:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:DemoApp.MVVMDemo.ViewModel"
             x:Class="DemoApp.MVVMDemo.MVVMDemoPage2">
    <ContentPage.BindingContext>
        <local:AddNumViewModel></local:AddNumViewModel>
    </ContentPage.BindingContext>
    <ContentPage.Content>
        <StackLayout>
            <Label  Text="{Binding NumSun,Mode=TwoWay,StringFormat=‘總數NumSun={0:F2}‘}" />
            <Label Text="{Binding Num1,
                      StringFormat=‘Num1 = {0:F2}‘}" />
            <Slider Value="{Binding Num1,Mode=TwoWay}" />
            <Label Text="{Binding Num2,
                      StringFormat=‘Num2 = {0:F2}‘}" />
            <Slider Value="{Binding Num2,Mode=TwoWay}" />
            <Label Text="{Binding Num3,
                      StringFormat=‘Num3 = {0:F2}‘}" />
            <Slider Value="{Binding Num3,Mode=TwoWay}" />
            <Button Text="清空" Command="{Binding CleanCommand}" />
        </StackLayout>
    </ContentPage.Content>
</ContentPage>

然後創建我們的ViewModel代碼如下:

    public class AddNumViewModel : INotifyPropertyChanged
    {
        //定義屬性值
        double num1, num2, num3,numSun;
        public event PropertyChangedEventHandler PropertyChanged;
        //定義清空的命令
        public ICommand CleanCommand { protected set; get; }

        public AddNumViewModel()
        {
            //實現清空
            this.CleanCommand = new Command((key) =>
            {
                this.NumSun = 0;
                this.Num1 = 0;
                this.Num2 = 0;
                this.Num3 = 0;
            });

        }

        /// <summary>
        /// 統一的屬性變更事件判斷方法
        /// </summary>
        /// <param name="propertyName"></param>
        protected virtual void  OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this,
                    new PropertyChangedEventArgs(propertyName));
            }
        }

        public double Num1
        {
            set
            {
                if (num1 != value)
                {
                    num1 = value;
                    OnPropertyChanged("Num1");
                    SetNewSunNum();
                }
            }
            get
            {
                return num1;
            }
        }

        public double Num2
        {
            set
            {
                if (num2 != value)
                {
                    num2 = value;
                    OnPropertyChanged("Num2");
                    SetNewSunNum();
                }
            }
            get
            {
                return num2;
            }
        }

        public double Num3
        {
            set
            {
                if (num3 != value)
                {
                    num3 = value;
                    OnPropertyChanged("Num3");
                    SetNewSunNum();
                }
            }
            get
            {
                return num3;
            }
        }

        public double NumSun
        {
            set
            {
                if (numSun != value)
                {
                    numSun = value;
                    OnPropertyChanged("NumSun");
                   
                }
            }
            get
            {
                return numSun;
            }
        }

        /// <summary>
        /// 把數值加起來的方法(業務邏輯)
        /// </summary>
        void SetNewSunNum()
        {
            this.NumSun = this.Num1 + this.Num2 + this.Num3;
        }



    }

很簡單,我們創建了Num1,Num2,Num3和NumSun四個屬性.實現了一個SetNewSunNum的方法,來求和.

然後就一一對應的在xaml中綁定了相關的屬性.所有的Slider綁定中都有個Mode=TwoWay,意思就是,這個屬性為雙向綁定,在控件中變更它的同時,也會在ViewModel中變更.

然後我們在來看看清空按鈕的命令綁定.

先解釋一下,為什麽會有命令綁定這個東西,因為我們使用雙向綁定的時候,頁面的點擊事件,並不能直接調用到ViewModel,所以就衍生了一個叫命令綁定的東西.來和我們控件的各種事件相關聯.

我們回到代碼,會發現,在AddNumViewModel中,我們定義了一個繼承自 ICommandCleanCommand 的命令,並在構造函數中實現了它

在我們的xaml中,buttom綁定了這個事件 <Button Text="清空" Command="{Binding CleanCommand}" />

這樣,就可以直接調用到ViewModel了,當然你的命令也可以傳遞參數,如下:

<Button Text="清空" Command="{Binding CleanCommand}" CommandParameter="aaa" />

aaa就是你傳遞的參數.

接收也很簡單,稍微改一下.CleanCommand 如下:

技術分享

這個key就是你傳遞進來的參數了..

3.回顧一下.

今天主要學習了Xamarin中的MVVM雙向綁定和命令綁定,

需要雙向綁定的類,需要繼承INotifyPropertyChanged,需要綁定的命令,需要繼承:ICommand

最後,列一下可以使用命令綁定的控件.

  • Button

  • MenuItem

  • ToolbarItem

  • SearchBar

  • TextCell(所以也包含ImageCell

  • ListView

  • TapGestureRecognizer

除了SearchBar和 ListView這兩個控件之外,這些控件都可以使用CommandCommandParameter

嗯..,SearchBar定義SearchCommandSearchCommandParameter屬性,而ListView定義一個RefreshCommand屬性的類型ICommand

其實都是一樣的..名字換了一下..

 

寫在最後

嗯..沒啥好說的..持續更新中..

C#使用Xamarin開發可移植移動應用(3.進階篇MVVM雙向綁定和命令綁定)附源碼