學習目標
- Visual Element 與 Command 的綁定
- CanExecute - 控制 Command 能否執行
Data binding 很強很好用,能幫我們把 ViewModel 的屬性和 View 的屬性連結起來,不用寫半行 事件控制 。
但,(萬惡的 But)
不是所有東西都是屬性啊... 有時候 ViewModel 的屬性要更改,還是得由事件來觸發。
問題來了*
當 Button 點擊時,同時要去更改 ViewModel 的屬性,該怎麼做?
回頭寫 Clicked 嗎?? 錯!!
ViewModel 提供了一個接口,叫做 Command Interface
簡單的說:
Button 可以綁定 ViewModel 內的方法,點擊時順便去更改 ViewModel 的屬性,再經由 INPC 去通知 View 做更動。
Command Interface
Visual Element 提供了兩個 Command 要用的屬性:
- Command // 拿來綁定 ViewModel 的 ICommand
- CommandParameter //參數傳遞
而 Xamarin 有 8 個 Class 支援 Command Interface:
- Button
- MenuItem
- SearchBar
- TextCell, ImageCell
- ListView
- TapGestureRecognizer
為了實現 Commanding,我們會有兩個步驟:
- ViewModel 宣告 ICommand 屬性
- 於建構子將 Command 實作並注入宣告的 ICommand
ICommand 介面定義了兩個方法和一個事件:
public interface ICommand
{
void Execute(object arg); // Business logic
bool CanExecute(object arg); // 設定此 Command 是否能被執行
event EventHandler CanExecuteChanged; // 此事件觸發時會去執行 CanExecute() 方法
}
這裡只有定義介面... 所以我說那個實作呢 ?
對此,Xamarin 很貼心的提供便便的 Command 和 Command<T> 類別給我們這群懶人使用~
來看個簡單的範例,這程式利用按鈕的增減來控制 指數:
ViewModel 內定義了兩個按鈕要用的 ICommand ,並在建構子初始化,
最後將含有方法的 Command 類別指派給這兩個 ICommand 屬性。
最後將含有方法的 Command 類別指派給這兩個 ICommand 屬性。
ViewModel:
class PowersViewModel : ViewModelBase { double exponent, power; // 先宣告 Command 屬性 public ICommand IncreaseExponentCommand { private set; get; } public ICommand DecreaseExponentCommand { private set; get; } public PowersViewModel(double baseValue) { // 初始值 BaseValue = baseValue; Exponent = 0; // 初始 ICommand IncreaseExponentCommand = new Command(ExecuteIncreaseExponent); DecreaseExponentCommand = new Command(ExecuteDecreaseExponent); } // Command 要做的事,指數 +1 void ExecuteIncreaseExponent() { Exponent += 1; } // 指數 -1 void ExecuteDecreaseExponent() { Exponent -= 1; } public double BaseValue { private set; get; } // 指數 public double Exponent { private set { if (SetProperty(ref exponent, value)) { Power = Math.Pow(BaseValue, exponent); } } get { return exponent; } } public double Power { private set { SetProperty(ref power, value); } get { return power; } } }
IncreaseExponentCommand = new Command(() => { Exponent += 1; }); DecreaseExponentCommand = new Command(() => { Exponent -= 1; });
XAML:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:local="clr-namespace:PowersOfThree" x:Class="PowersOfThree.PowersOfThreePage"> <ContentPage.Resources> <ResourceDictionary> <local:PowersViewModel x:Key="viewModel"> <x:Arguments> <x:Double>3</x:Double> </x:Arguments> </local:PowersViewModel> </ResourceDictionary> </ContentPage.Resources> <StackLayout BindingContext="{StaticResource viewModel}"> <StackLayout Orientation="Horizontal" Spacing="0" HorizontalOptions="Center" VerticalOptions="CenterAndExpand"> <Label FontSize="Large" Text="{Binding BaseValue, StringFormat='{0}'}" /> <Label FontSize="Small" Text="{Binding Exponent, StringFormat='{0}'}" /> <Label FontSize="Large" Text="{Binding Power, StringFormat=' = {0}'}" /> </StackLayout> <StackLayout Orientation="Horizontal" VerticalOptions="CenterAndExpand"> <!-- 指數 +1 --> <Button Text="Increase" Command="{Binding IncreaseExponentCommand}" HorizontalOptions="CenterAndExpand" /> <!-- 指數 -1 --> <Button Text="Decrease" Command="{Binding DecreaseExponentCommand}" HorizontalOptions="CenterAndExpand" /> </StackLayout> </StackLayout> </ContentPage>
執行畫面:
更進一步
接著更進一步來熟悉 Execute、CanExecute 和 CommandParameter
底下是一個只有加法的計算機,
這小節不會將所有程式碼貼出,完整範例可以參考原文書 p.523 。
這個計算機有個規則,按下數字時,加法的按鈕才能執行,當加法的按鈕按下時,小數點的按鈕不能按,除非你再按下數字。
來看幾段 ViewModel 的程式碼:
一般數字鍵:
NumericCommand = new Command( execute: (string parameter) => { // 從 View 帶進來的 parameter if (isSumDisplayed || CurrentEntry == "0") CurrentEntry = parameter; else CurrentEntry += parameter; isSumDisplayed = false; RefreshCanExecutes(); }, canExecute: (string parameter) => { // 未加總或目前長度小於16 return isSumDisplayed || CurrentEntry.Length < 16; });
Backspace 按鍵:
BackspaceCommand = new Command( execute: () => { CurrentEntry = CurrentEntry.Substring(0, CurrentEntry.Length - 1); if (CurrentEntry.Length == 0) { CurrentEntry = "0"; } RefreshCanExecutes(); }, canExecute: () => { //還未加總且畫面上有值時,回傳 true,這 Command 可按!!! return !isSumDisplayed && (CurrentEntry.Length > 1 || CurrentEntry[0] != '0'); });
可以注意一下 Execute 最後有一個 RefreshCanExecutes() ,內容如下:
當 Command 呼叫 ChangeCanExecute 時,就會執行此 Command 的 canExecute 方法。
void RefreshCanExecutes() { ((Command)BackspaceCommand).ChangeCanExecute(); ((Command)NumericCommand).ChangeCanExecute(); ((Command)DecimalPointCommand).ChangeCanExecute(); ((Command)AddCommand).ChangeCanExecute(); }
補一下 XAML:
<Button Text="⇦" Command="{Binding BackspaceCommand}" /> <Button Text="7" Command="{Binding NumericCommand}" CommandParameter="7" /> <Button Text="8" Command="{Binding NumericCommand}" CommandParameter="8" />
沒有留言:
張貼留言
注意:只有此網誌的成員可以留言。