2017/2/28

Xamarin.Forms 教學系列文(六.壹)Button




學習目標
  • Button Clicked 事件
  • sender 就是物件!
  • StyleId 可為 View 物件唯一 Id

一般來說 Visual Elements 的特性可以簡單區分為 "純顯示" 和 "互動"。
例如,Label 就是一個 "純顯示" 的元件; 而最常見的 "互動" 元件就是 Button

當手指按下 Button 時,手機會先給予使用者畫面上的回饋,等手指移開 Button 後,Clicked 事件才會被觸發。

若在 Clicked 事件內需要取得更多資訊時,可以使用事件參數(EventArgs)來傳遞資料。


Button Clicked 事件

直接看範例了解如何將方法加入 Button 的 Clicked 事件內(當然未來會看到 Xaml 的用法),

按鈕被按下後,會在 stacklayout 內新增一筆時間資訊:
public class ButtonLoggerPage : ContentPage  
{      
    StackLayout loggerLayout = new StackLayout();  
  
    public ButtonLoggerPage()      
    {          
        // Create the Button and attach Clicked handler.          
        Button button = new Button 
        {              
            Text = "Log the Click Time"          
        };          
        button.Clicked += OnButtonClicked;  
  
        this.Padding = new Thickness(5, Device.OnPlatform(20, 0, 0), 5, 0);  
  
        // Assemble the page.          
        this.Content = new StackLayout          
        {              
            Children =  
            {                  
                button,                  
                new ScrollView                  
                {                      
                    VerticalOptions = LayoutOptions.FillAndExpand,                      
                    Content = loggerLayout                  
                }              
            }          
        };      
    }  
  
    void OnButtonClicked(object sender, EventArgs args)      
    {          
        // Add Label to scrollable StackLayout.          
        loggerLayout.Children.Add(new Label          
        {              
            Text = "Button clicked at " + DateTime.Now.ToString("T")          
        });      
    }  
}



如圖所示,每一平台上的按鈕都長得不一樣,因為按鈕是經由原生碼去轉換的。

Button 可更改的屬性如下:
  • FontFamily of type string
  • FontSize of type double 
  • FontAttributes of type FontAttributes  
  • TextColor of type Color (default is Color.Default)  
  • BorderColor of type Color (default is Color.Default)
  • BorderWidth of type double (default is 0)
  • BorderRadius of type double (default is 5)
  • Image (將在 13 章討論)

當然,Button 是繼承至 VisualElement,也有 BackgroundColor 等屬性可以更改。


Walking the visual tree

這標題我實在不知道怎翻...

這小節要講的是,可以用 Clicked 的 sender 參數,去取得與此物件相關的父元素或子元素。

以此例來說,可以將 sender 轉換成 Button 本身物件如下:
Button button = (Button)sender; 
若要取得父元素可以如下:
StackLayout outerLayout = (StackLayout)button.Parent;
而要取得父元素底下的子元素時可以如下:
ScrollView scrollView = (ScrollView)outerLayout.Children[1];
不過齁,盡量不要這樣去抓取元素,會讓程式碼變得有點亂,若 StackLayout 有改變,抓取子元素就要重新 "Walking the visual tree"


事件共享方法

把同一方法指派給不同按鈕時,可能會遇到一個問題,
若不同按鈕要在同一方法內執行不同事情時,需要判斷物件是哪位。

底下程式碼有兩個顆按鈕共用一個方法,
判斷是哪個物件則直接用按鈕 (object)本身當作條件:
public class TwoButtonsPage : ContentPage  
{      
    Button addButton, removeButton;      
    StackLayout loggerLayout = new StackLayout();  
  
    public TwoButtonsPage()      
    {          
        // Create the Button views and attach Clicked handlers.          
        addButton = new Button          
        {              
            Text = "Add",              
            HorizontalOptions = LayoutOptions.CenterAndExpand          
        };          
         
        addButton.Clicked += OnButtonClicked;  
  
        removeButton = new Button          
        {              
            Text = "Remove",              
            HorizontalOptions = LayoutOptions.CenterAndExpand,              
            IsEnabled = false          
        };          
         
        removeButton.Clicked += OnButtonClicked;  
  
        this.Padding = new Thickness(5, Device.OnPlatform(20, 0, 0), 5, 0);  
  
        // Assemble the page.          
        this.Content = new StackLayout          
        {              
            Children =              
            {                  
                new StackLayout                  
                {                      
                    Orientation = StackOrientation.Horizontal,                      
                    Children =                      
                    {                          
                        addButton,                          
                        removeButton                      
                    }  
                },  
                new ScrollView                  
                {                      
                    VerticalOptions = LayoutOptions.FillAndExpand,                      
                    Content = loggerLayout                  
                }              
            }          
        };      
    }  
  
    void OnButtonClicked(object sender, EventArgs args)      
    {         
        Button button = (Button)sender;  
  
        //用物件本身來做判斷...
        if (button == addButton)          
        {              
            // Add Label to scrollable StackLayout.              
            loggerLayout.Children.Add(new Label              
            {                  
                Text = "Button clicked at " + DateTime.Now.ToString("T")              
            });          
        }          
        else        
        {              
            // Remove topmost Label from StackLayout.              
            loggerLayout.Children.RemoveAt(0);          
        }  
  
        // Enable "Remove" button only if children are present.          
         
        removeButton.IsEnabled = loggerLayout.Children.Count > 0;      
    }  

結果如下:


如有看前一章節,應該看過用 Lambda 寫在事件內的方法,

Button Clicked 事件也可以用 Lambda 來寫如下:
button.Clicked += (sender, args) => 
{
    label.Text = "Button clicked at " + DateTime.Now.ToString("T");
}

StyleId

若按鈕變多,全部都用物件(object)做條件判斷的話,程式會相當亂

Visual Element 類別有提供一個 StyleId 屬性,
由於此屬性在 Xamairn.Forms 其他地方不會用到,可以指派任何你想放入的字串,也可以當作物件唯一的 Id 來使用。

底下的範例程式有五個 StackLayout,最外層一個 StackLayout 包含四個 StackLayout,而這四個 StackLayout 分別包含著 0 ~ 9 按鈕和一個退回按鈕。
每顆按鈕都設定其 StyleId,拿來當作唯一 Id。
 public class SimplestKeypadPage : ContentPage 
    { 
        Label displayLabel; Button backspaceButton; public SimplestKeypadPage() 
        { // Create a vertical stack for the entire keypad.  
            StackLayout mainStack = new StackLayout 
            { 
                VerticalOptions = LayoutOptions.Center, 
                HorizontalOptions = LayoutOptions.Center 
            }; 
 
            // First row is the Label.  
 
            displayLabel = new Label 
            { 
                FontSize = Device.GetNamedSize(NamedSize.Large, typeof(Label)), 
                VerticalOptions = LayoutOptions.Center, 
                HorizontalTextAlignment = TextAlignment.End 
            }; 
            mainStack.Children.Add(displayLabel); 
 
            // Second row is the backspace Button.  
            backspaceButton = new Button 
            { 
                Text = "\u21E6", 
                FontSize = Device.GetNamedSize(NamedSize.Large, typeof(Button)), 
                IsEnabled = false 
            }; 
            backspaceButton.Clicked += OnBackspaceButtonClicked; 
            mainStack.Children.Add(backspaceButton); 
 
            // Now do the 10 number keys.  
            StackLayout rowStack = null; 
            for (int num = 1; num <= 10; num++) 
            { 
                if ((num - 1) % 3 == 0) 
                { 
                    rowStack = new StackLayout 
                    { 
                        Orientation = StackOrientation.Horizontal 
                    }; 
 
                    mainStack.Children.Add(rowStack); 
                } 
 
                Button digitButton = new Button 
                { 
                    Text = (num % 10).ToString(), 
                    FontSize = Device.GetNamedSize(NamedSize.Large, typeof(Button)), 
                    StyleId = (num % 10).ToString() 
                }; 
 
                digitButton.Clicked += OnDigitButtonClicked; 
 
                // For the zero button, expand to fill horizontally.  
                if (num == 10) 
                { 
                    digitButton.HorizontalOptions = LayoutOptions.FillAndExpand; 
                } 
 
                rowStack.Children.Add(digitButton); 
            } this.Content = mainStack; 
        } 
 
        void OnDigitButtonClicked(object sender, EventArgs args) 
        { 
 
            Button button = (Button)sender; 
            displayLabel.Text += (string)button.StyleId; 
            backspaceButton.IsEnabled = true; 
        } 
 
        void OnBackspaceButtonClicked(object sender, EventArgs args) 
        { 
            string text = displayLabel.Text; 
            displayLabel.Text = text.Substring(0, text.Length - 1); 
            backspaceButton.IsEnabled = displayLabel.Text.Length > 0; 
        } 
    }

畫面如下:



9 則留言:

  1. button.Clicked += OnButtonClicked;
    是用+=好神奇怎麼不是=

    回覆刪除
    回覆
    1. 這故事最早要從委派(delegate)講起了...委派也是 Lambda 的前身~
      C# 這裡的意思其實是將 OnButtonClicked handler 註冊到 Clicked event 內
      寫法就是 +=
      補一下官方的介紹~
      https://developer.xamarin.com/guides/ios/application_fundamentals/delegates,_protocols,_and_events/
      中間有一小段 C# 可以看到早期的委派寫法

      刪除
    2. =的话有可能只加一个,但是+=可以加任意多个哦。

      刪除
  2. 到這邊 我的模擬器打開了 但部署好慢= =

    這是為什麼??

    回覆刪除
    回覆
    1. 有幾件事要注意耶...
      1. 通常第一次會比較慢,載入的元件較多
      2. 用模擬器的話電腦配備會影響建置和部屬速度
      3. 我通常都用實體裝置來做測試會快很多...
      4. 如果想快速看到畫面的話也可以試試 Live Player

      ps.官方自己建議用 Android Studio 內的模擬器,可以試試看狀況會不會比較好

      刪除
    2. 好的 我只好慢慢等了 謝謝解惑

      我是用桌機接網路線 Live Player沒辦法用

      刪除
    3. Live Player是透過網路,只要桌機和手機都有網路應該就接的上了,
      不用點對點

      刪除
    4. 我最近試了好幾次 都是連線失敗耶

      會不會是公司網路問題??

      連公司的wifi會出現

      "無法部署至 Sony D5833 Player,請確認 Xamarin Live Player 應用程式已開啟,且裝置與 Visual Studio 在相同網路上。"

      刪除