學習目標
- 建構子參數傳遞
- 呼叫上下頁方法
當我學會了頁面導覽後,心中還是充滿了疑問,若我兩個頁面之間要做參數的傳遞,到底要怎麼做?
App 又不像網頁的 Post、Get,甚至還用個 Session 來當作參數傳遞!!!
那這一小節會提到 六種 參數傳遞的方式,各有優缺點,我會分成三個部分來說。
- 直觀型傳遞 - 建構子 & 呼叫方法
- 中介型傳遞 - App & Events
- 特殊型傳遞 - Messaging Center & ViewModel
第一部分會先來談談最簡單的方式,利用建構子去傳遞參數,或是呼叫上下頁的方法
建構子傳值
道理很簡單,在 Xamarin.Forms,其實每個頁面都是一個 Class,
那我們建立這個 Class 時,就能在建構時把參數帶進去啦!!
來看範例,有兩個頁面,
第一頁 SchoolPage 包含著一個 ListView,
當點擊某一項目時,將資料導向下一頁 StudentPage,並顯示其資料:
SchoolPage
XAML:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="SchoolAndStudents.SchoolPage" Title="School"> <StackLayout BindingContext="{Binding StudentBody}"> <Label Text="{Binding School}" FontSize="Large" FontAttributes="Bold" HorizontalTextAlignment="Center" /> <!--顯示資料的 ListView--> <ListView x:Name="listView" ItemsSource="{Binding Students}" ItemSelected="OnListViewItemSelected"> <ListView.ItemTemplate> <DataTemplate> <ImageCell ImageSource="{Binding PhotoFilename}" Text="{Binding FullName}" Detail="{Binding GradePointAverage, StringFormat='G.P.A. = {0:F2}'}" /> </DataTemplate> </ListView.ItemTemplate> </ListView> </StackLayout> </ContentPage>
.cs
public partial class SchoolPage : ContentPage { public SchoolPage() { InitializeComponent(); BindingContext = new SchoolViewModel(); } async void OnListViewItemSelected(object sender, SelectedItemChangedEventArgs args) { // 選中的資料 Student student = args.SelectedItem as Student; if (student != null) { // 清空選中的屬性 listView.SelectedItem = null; // 導向下一頁並將 student 帶入 await Navigation.PushAsync(new StudentPage(student)); } } }
執行結果:
從這頁點一筆資料後,將點選的資料帶入 StudentPage 頁。
StudentPage
.cs:
public partial class StudentPage : ContentPage { //從前一頁接收 studetn 值 public StudentPage(Student student) { InitializeComponent(); BindingContext = student; } }
XAML:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="SchoolAndStudents.StudentPage" Title="Student"> <StackLayout> <!-- Name --> <StackLayout Orientation="Horizontal" HorizontalOptions="Center" Spacing="0"> <StackLayout.Resources> <ResourceDictionary> <Style TargetType="Label"> <Setter Property="FontSize" Value="Large" /> <Setter Property="FontAttributes" Value="Bold" /> </Style> </ResourceDictionary> </StackLayout.Resources> <Label Text="{Binding LastName}" /> <Label Text="{Binding FirstName, StringFormat=', {0}'}" /> <Label Text="{Binding MiddleName, StringFormat=' {0}'}" /> </StackLayout> <!-- Photo --> <Image Source="{Binding PhotoFilename}" VerticalOptions="FillAndExpand" /> <!-- Sex --> <Label Text="{Binding Sex, StringFormat='Sex = {0}'}" HorizontalOptions="Center" /> <!-- GPA --> <Label Text="{Binding GradePointAverage, StringFormat='G.P.A. = {0:F2}'}" HorizontalOptions="Center" /> </StackLayout> </ContentPage>
執行結果:
*也可以在 Push 時就設定 BindingContext 如下:
await Navigation.PushAsync(new StudentPage { BindingContext = student });
呼叫方法
假設有兩個頁面,HomePage 和 InfoPage,當 HomePage 導覽至 InfoPage 後,因為一些原因,必須將 InfoPage 的值回傳到 HomePage 上。
來看範例,
HomePage 可以導覽至 InfoPage 進行新增或編輯,
當新增或編輯結束後,要新增或更新 HomePage 內的 ListView:
(謎之聲:這種做完事情要另一個地方更改顯示內容的動作,是否可以用 ViewModel 處理呢?)
先建立一個等等會用到的 Information 類別:
public class Information { public string Name { set; get; } public string Email { set; get; } public string Language { set; get; } public DateTime Date { set; get; } public override string ToString() { return String.Format("{0} / {1} / {2} / {3:d}", String.IsNullOrWhiteSpace(Name) ? "???" : Name, String.IsNullOrWhiteSpace(Email) ? "???" : Email, String.IsNullOrWhiteSpace(Language) ? "???" : Language, Date); } }
HomePage
XAML:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="DataTransfer1.DataTransfer1HomePage" Title="Home Page"> <Grid> <!--新增按鈕--> <Button Text="Add New Item" Grid.Row="0" FontSize="Large" HorizontalOptions="Center" VerticalOptions="Center" Clicked="OnGetInfoButtonClicked" /> <!--新增完這 ListView要更新--> <ListView x:Name="listView" Grid.Row="1" ItemSelected="OnListViewItemSelected" /> </Grid> </ContentPage>
.cs
public partial class DataTransfer1HomePage : ContentPage { ObservableCollectionlist = new ObservableCollection (); public DataTransfer1HomePage() { InitializeComponent(); listView.ItemsSource = list; } // 按鈕按下去導向 InfoPage async void OnGetInfoButtonClicked(object sender, EventArgs args) { await Navigation.PushAsync(new DataTransfer1InfoPage()); } // 準備好方法讓 InfoPage 呼叫 public void InformationReady(Information info) { // 若物件已存在於 list 則取代 int index = list.IndexOf(info); if (index != -1) { list[index] = info; } // 不存在就新增 else { list.Add(info); } } }
執行結果:
InfoPage
XAML:
畫面上有兩個 Entry 和一個 Picker
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="DataTransfer1.DataTransfer1InfoPage" Title="Info Page"> <StackLayout Padding="20, 0" Spacing="20"> <Entry x:Name="nameEntry" Placeholder="Enter Name" /> <Entry x:Name="emailEntry" Placeholder="Enter Email Address" /> <Picker x:Name="languagePicker" Title="Favorite Programming Language"> <Picker.Items> <x:String>C#</x:String> <x:String>F#</x:String> <x:String>Objective C</x:String> <x:String>Swift</x:String> <x:String>Java</x:String> </Picker.Items> </Picker> <DatePicker x:Name="datePicker" /> </StackLayout> </ContentPage>
這邊輸入資料不會發生任何事…
直到按下返回鍵,才會覆寫 OnDisappearing 方法,並呼叫 HomePage 內的 InformationReady 方法
要注意的是用 NavigationStack 取得 HomePage 物件的方法。
public partial class DataTransfer1InfoPage : ContentPage { // 初始化 Information 物件 Information info = new Information(); public DataTransfer1InfoPage() { InitializeComponent(); } //覆寫本頁消失時的事件 protected override void OnDisappearing() { base.OnDisappearing(); // 取得畫面上輸入的資料並指派給 info 物件 info.Name = nameEntry.Text; info.Email = emailEntry.Text; int index = languagePicker.SelectedIndex; info.Language = index == -1 ? null : languagePicker.Items[index]; info.Date = datePicker.Date; // 利用 Stack 取得 HomePage 物件 NavigationPage navPage = (NavigationPage)Application.Current.MainPage; IReadOnlyListnavStack = navPage.Navigation.NavigationStack; int lastIndex = navStack.Count - 1; DataTransfer1HomePage homePage = navStack[lastIndex] as DataTransfer1HomePage; if (homePage == null) { homePage = navStack[lastIndex - 1] as DataTransfer1HomePage; } // 呼叫 HomePage 的方法,並把要傳遞的物件帶入 homePage.InformationReady(info); } }
執行畫面:
最後順手來寫個編輯,
當 HomePage 點擊 List 的項目時導向 InfoPage,
這邊示範則從 HomePage 去呼叫 InfoPage 的方法,並把值帶入 InfoPage 的畫面:
public partial class DataTransfer1HomePage : ContentPage { … // List 點擊時的事件 async void OnListViewItemSelected(object sender, SelectedItemChangedEventArgs args) { if (args.SelectedItem != null) { listView.SelectedItem = null; DataTransfer1InfoPage infoPage = new DataTransfer1InfoPage(); await Navigation.PushAsync(infoPage); infoPage.InitializeInfo((Information)args.SelectedItem); } } … }
InfoPage 取得值並帶入畫面:
public partial class DataTransfer1InfoPage : ContentPage { … public void InitializeInfo(Information info) { // 取代 info 物件 this.info = info; // 初始化畫面值 nameEntry.Text = info.Name ?? ""; emailEntry.Text = info.Email ?? ""; if (!String.IsNullOrWhiteSpace(info.Language)) { languagePicker.SelectedIndex = languagePicker.Items.IndexOf(info.Language); } datePicker.Date = info.Date; } … }
沒有留言:
張貼留言
注意:只有此網誌的成員可以留言。