2017/5/18

Xamarin.Forms 教學系列文(十五.肆)互動元件 -- SearchBar & DatePicker & TimePicker




學習目標
  • SearchBar
  • DatePicker
  • TimePicker
看標題應該是知道這章節在介紹什麼了...


SearchBar

SearchBar 定義了五個屬性:
  • Text
  • Placehol
  • CancelButtonColor
  • SearchCommand - data binding 用
  • SearchCommandParameter - data binding 用

兩個事件:
  • TextChanged
  • SearchButtonPressed - 相當於 Entry 的 Completed 事件

來看範例:
程式會從一份 TXT 檔案 搜尋文字,並顯示所有搜尋結果在文章內的位置

*Oplatform 寫法已更改,請參考此篇文章
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" 
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
             x:Class="SearchBarDemo.SearchBarDemoPage">
    
    <ContentPage.Padding>
        <OnPlatform x:TypeArguments="Thickness" 
                    iOS="10, 20, 10, 0" 
                    Android="10, 0" 
                    WinPhone="10, 0" />
    </ContentPage.Padding>
    
    <StackLayout>
        <!--SearchBar-->
        <SearchBar x:Name="searchBar" 
                   Placeholder="Search text"
                   TextChanged="OnSearchBarTextChanged" 
                   SearchButtonPressed="OnSearchBarButtonPressed" />
        
        <ScrollView x:Name="resultsScroll" VerticalOptions="FillAndExpand">
            <StackLayout x:Name="resultsStack" />
        </ScrollView>
    </StackLayout>
</ContentPage>

各平台 SearchBar 畫面:

.cs 的部分為 搜尋 .txt 內嵌資源 的實作:
如何製作內嵌資源在 13-1 Bitmap 有介紹,更深入的 I/O 會出現在 20 章。
    public partial class SearchBarDemoPage : ContentPage
    {
        const double MaxMatches = 100;
        string bookText;

        public SearchBarDemoPage()
        {
            InitializeComponent();

            // 讀取 .txt 內嵌資源.
            string resourceID = "SearchBarDemo.Texts.MobyDick.txt";
            Assembly assembly = GetType().GetTypeInfo().Assembly;

            using (Stream stream = assembly.GetManifestResourceStream(resourceID))
            {
                using (StreamReader reader = new StreamReader(stream))
                {
                    bookText = reader.ReadToEnd();
                }
            }
        }

        //文字改變時先清空畫面
        void OnSearchBarTextChanged(object sender, TextChangedEventArgs args)
        {
            resultsStack.Children.Clear();
        }

        void OnSearchBarButtonPressed(object sender, EventArgs args)
        {
            //清空
            resultsScroll.Content = null;
            resultsStack.Children.Clear();

            //搜尋
            SearchBookForText(searchBar.Text);

            // Reattach resultsStack to layout.
            resultsScroll.Content = resultsStack;
        }

        void SearchBookForText(string searchText)
        {
            int count = 0;
            bool isTruncated = false;

            using (StringReader reader = new StringReader(bookText))
            {
                int lineNumber = 0;
                string line;

                //用迴圈搜尋每一行
                while (null != (line = reader.ReadLine()))
                {
                    lineNumber++; int index = 0;

                    while (-1 != (index = (line.IndexOf(searchText, index,
                                                                     StringComparison.OrdinalIgnoreCase))))
                    {
                        if (count == MaxMatches)
                        {
                            isTruncated = true;
                            break;
                        }

                        index += 1;
                        
                    }

                    // Add the information to the StackLayout. 
                    resultsStack.Children.Add( new Label
                    {
                        Text = String.Format("Found at line {0}, offset {1}", 
                                                lineNumber, index)
                    });

                    count++;
                }

                if (isTruncated)
                {
                    break;
                }
            }

            // Add final count to the StackLayout. 
            resultsStack.Children.Add( new Label 
            {
                Text = String.Format("{0} match{1} found{2}",
                                        count, 
                                        count == 1 ? "" : "es", 
                                        isTruncated ? " - stopped" : "") 
            }); 
        } 
    }


執行結果:


DatePicker

DatePicker 三個跟時間有關的屬性:
  • MinimumDate, 預設 1900/01/01
  • MaximumDate, 預設 2100/12/31
  • Date, 預設今天

選完日期的事件:
  • DateSelected

若要在 XAML 內設定值,只要填入 DateTime.Parse 可以轉換的格式就好:
    <DatePicker … >
        <DatePicker.MinimumDate>
            03/01/2016
        </DatePicker.MinimumDate>
        
        <DatePicker.MaximumDate>
            10/31/2016
        </DatePicker.MaximumDate>
        
        <DatePicker.Date>
            04/24/2016
        </DatePicker.Date>
    </DatePicker>

DatePicker 可以設定 Format,決定顯示的日期格式,舉例來說
<DatePicker Format="D" /> 
大 D 顯示的字串為 Long Date Format : dddd, MMMM dd, yyyy,其他格式可以參考微軟官網



TimePicker

TimePicker 就是 DatePicker 的簡化版,只有 Time Format 兩個屬性,如果選完時間要觸發事件的話可以寫在 PropertyChanged 裡面。

我們來利用 TimePicker 來做個簡易的鬧鐘:
畫面上有一個 switch 開關和 Timer ~
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" 
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
             x:Class="SetTimer.SetTimerPage" Padding="50">
    
    <StackLayout Spacing="20" 
                 VerticalOptions="Center">
        
        <TimePicker x:Name="timePicker" 
                    PropertyChanged="OnTimePickerPropertyChanged" />
        
        <Switch x:Name="switch" 
                HorizontalOptions="End"
                Toggled="OnSwitchToggled" />
        
        <Entry x:Name="entry" 
               Text="Sample Timer"
               Placeholder="label" />
    </StackLayout>
    
</ContentPage>


.cs:
初始利用 Device.StartTimer ,每隔一秒判斷現在的時間是否大於鬧鐘時間,若大於就跳出警示:

這範例有一個新東西 DisplayAlert,定義於 Page 並由 ContentPage 繼承,可以直接呼叫使用。
    public partial class SetTimerPage : ContentPage
    {
        DateTime triggerTime;

        public SetTimerPage()
        {
            InitializeComponent();

            //每秒判斷
            Device.StartTimer(TimeSpan.FromSeconds(1), OnTimerTick);
        }

        bool OnTimerTick()
        {
            //若超過時間警示訊息
            if (@switch.IsToggled && DateTime.Now >= triggerTime)
            {
                @switch.IsToggled = false;
                
                DisplayAlert("Timer Alert", "The '" + entry.Text + "' timer has elapsed", "OK");
            }

            return true;
        }

        //TimePicker 更改時間時
        void OnTimePickerPropertyChanged(object obj, PropertyChangedEventArgs args)
        {
            if (args.PropertyName == "Time")
            {
                SetTriggerTime();
            }
        }

        // Switch 改變時
        void OnSwitchToggled(object obj, ToggledEventArgs args)
        {
            SetTriggerTime();
        }

        void SetTriggerTime()
        {
            if (@switch.IsToggled)
            {
                triggerTime = DateTime.Today + timePicker.Time;

                //若設定的時間已過,則設定成明天同一時間
                if (triggerTime < DateTime.Now)
                {
                    triggerTime += TimeSpan.FromDays(1);
                }
            }
        }
    }


執行結果:






沒有留言:

張貼留言