昨天學習了實際透過 HttpClient 由行動裝置讀取發行在 Azure Web App 的 ASP.NET Core Web API 所實作的 REST API 回傳儲存於 Azure SQL Database 資料示在 ListView 上,今天就再進一步學習如下圖所示,當按下 ListView 的項目(Item)時可將該項目所含的資料帶到下一頁,並在下一頁使用該資料的地址資訊顯示出 Google 地圖:
繫結事件處理常式
首先在 MyFirstPage.xaml 的 ListView 加入 ItemTapped="OnItemTapped"
也就是當項目(Item)被按下時執行命名為 OnItemTapped (可自行命名)的事件處理常式,如下所示:
<ListView RowHeight="90"
ItemsSource="{Binding Addresses}"
ItemTapped="OnItemTapped">
.......
.......
.......
</ListView>
接著在 MyFirstPage.xaml.cs 裡加入如下的事件處理常式實作:
namespace Demae.App.Views
{
public partial class MyFirstPage : ContentPage
{
....
....
....
private void OnItemTapped(object sender, ItemTappedEventArgs e)
{
((MyFirstPageViewModel)BindingContext).AddressSelectedCommand.Execute((AddressModel)e.Item);
}
}
}
請回憶一下,在 Prism MVVM Framework 裡 View 與 ViewModel 會自動繫結(Binding)所以頁面的繫結內容(BindingContext) 是 MyFirstPageViewModel 因此將 BindingContext
轉型成 MyFirstPageViewModel
是合適的 。 而項目所包含的資料型別為 AddressModel 因此將其轉型傳遞給實作在 MyFirstPageViewModel 的 AddressSelectedCommand 執行。
目前這個在後置(Code Behind)中直接繫結事件處理常式的作法,在 MVVM Pattern 中,認為耦合度過高,應該是要使用 EventToCommandBehavior 將程式碼寫在 ViewModel 內,目前為了方便解說暫時這麼做,等過段時間再來學習比較正確(理想?)的作法。
實作命令
接著在 MyFirstPageViewModel.cs 加入 AddressSelectedCommand 的實作,如下所示:
using Demae.App.Models;
using Demae.App.Services;
using Prism.Commands;
using Prism.Mvvm;
using Prism.Navigation;
using System.Collections.ObjectModel;
namespace Demae.App.ViewModels
{
public class MyFirstPageViewModel : BindableBase, INavigationAware
{
......
......
private INavigationService _navigationService;
public MyFirstPageViewModel(IAddressService apiService,INavigationService navigationService)
{
_apiService = apiService;
_navigationService = navigationService;
}
private DelegateCommand<AddressModel> _addressSelectedCommand;
public DelegateCommand<AddressModel> AddressSelectedCommand =>
_addressSelectedCommand ?? (_addressSelectedCommand = new DelegateCommand<AddressModel>(AddressSelected));
private void AddressSelected(AddressModel obj)
{
var p = new NavigationParameters
{
{"addressModel", obj }
};
_navigationService.NavigateAsync("GoogleMapPage", p);
}
.....
.....
.....
}
}
比較值的留意是如下的 NavigationParameters 可組出如 {"addressModel", obj } 的 { 參數名稱, 傳遞物件} 將本例的 AddressModel 傳遞到命名為 GoogleMapPage 的頁面:
var p = new NavigationParameters
{
{"addressModel", obj }
};
實作 GoogleMapPage
接著再新增一個命名為 GoogleMapPage 的 View 並在 ContentPage 內加入用來嵌入網頁瀏覽器的 WebView 元件,並將 WebView 的來源(也就是網址)繫結到對應 ViewModel 的 MapUrl 屬性,而 ContentPage 的 Title 繫結到 AddressModel.Address 屬性:
<?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:prism="clr-namespace:Prism.Mvvm;assembly=Prism.Forms"
prism:ViewModelLocator.AutowireViewModel="True"
x:Class="Demae.App.Views.GoogleMapPage"
Title="{Binding AddressModel.Address}">
<WebView Source="{Binding MapUrl}" />
</ContentPage>
實作 GooleMapPageViewModel 內的對應屬性
接者在 GoogleMapPageViewModel 內新增兩個屬性,並於 OnNavigatingTo() 事件處理常式中,由傳遞過來的參數設定這兩個屬性值:
using Demae.App.Models;
using Prism.Mvvm;
using Prism.Navigation;
namespace Demae.App.ViewModels
{
public class GoogleMapPageViewModel : BindableBase, INavigationAware
{
private AddressModel _addressModel;
public AddressModel AddressModel
{
get { return _addressModel; }
set { SetProperty(ref _addressModel, value); }
}
private string _mapUrl;
public string MapUrl
{
get { return _mapUrl; }
set { SetProperty(ref _mapUrl, value); }
}
......
......
public void OnNavigatingTo(NavigationParameters parameters)
{
if (parameters.ContainsKey("addressModel"))
{
AddressModel = (AddressModel)parameters["addressModel"];
MapUrl = string.Format("https://www.google.com.tw/maps/place/{0}", AddressModel.Address);
}
}
}
}
OK!今天就學習到這裡,明天再繼續吧。