XAML in Xamarin.Forms 基礎篇 電子書

XAML in Xamarin.Forms 基礎篇 電子書
XAML in Xamarin.Forms 基礎篇 電子書

Xamarin.Forms 快速入門 電子書

Xamarin.Forms 快速入門 電子書
Xamarin.Forms 快速入門 電子書

2016/10/23

Xamarin.Forms 開放資料跨平台應用程式開發 工具列使用

接著,您需要在這個練習內,完成更多使用者的互動操作機制,例如,可以透過工具列的使用,讓使用者進行資料過濾、與資料重新整理或者是進行資料分享。這些許多與裝置互動的行為,都可以透過不同的插件(Plug-ins)的使用協助您做到這些應用,而您也不再需要分別了解不同平台下要如何呼叫個平台的 API,這些全部都由 插件 幫您處理掉了。
在這個階段的練習,您將會學會底下的 Xamarin.Forms 的開發技術:
  1. 在 Xamarin.Forms 使用圖片資源
  2. 創業空間明細的地圖導航
  3. 創業空間明細的撥打電話
  4. 創業空間明細的分享內容與連結
  5. 創業空間明細的開啟網頁連結
  6. 創業空間明細的訊息對話窗
  7. 工具列之資料重新整理
  8. 工具列之資料過濾設定
  9. 資料清單之互動選擇
在這個練習專案中,您將會用到的圖片檔案,可以從https://github.com/vulcanlee/XFAppSample/tree/master/XFCreative/Images 下載下來。
這篇章節的練習專案的原始程式碼將會存放在 GitHubhttps://github.com/vulcanlee/XFAppSample/tree/master/XFCreative/3.ItemDetail 內

在 Xamarin.Forms 使用圖片資源

  1. 請先下載圖片到本機電腦上的某個目錄下
    複製圖片
  2. 將這些圖片全選,並且拖拉到 XFCreative.Droid 專案下的 Resources > drawable 目錄內
  3. 將這些圖片全選,並且拖拉到 XFCreative.iOS 專案下的 Resources 目錄內
  4. 將這些圖片全選,並且拖拉到 XFCreative.UWP 專案下的 Assets 目錄內
  5. 重新檢視 核心PCL XFCreative 專案內的 BusinessSpaceDetailPage.xaml 檔案,您將會看到在最前面有個 Image 項目將會用來顯示圖片,不過,在這裡使用了 OnPlatform 功能,分別指定了不同平台的圖片資源。
    請在 BusinessSpaceDetailPage.xaml 檔案第 27 行,找到 的宣告,使用底下 XAML 宣告,置換這個節點的定義內容
        <Image.Source>
          <OnPlatform x:TypeArguments="ImageSource"
            iOS="info.png"
            Android="info.png"
            WinPhone="Assets/info.png" />
        </Image.Source>
  1. 請在使用 Android 專案來執行這次修改的應用程式,您將會到如下圖,有個 i 的圖片顯示出來
    工具列使用

創業空間明細的工具列應用

  1. 開啟 核心PCL XFCreative 專案內 Views 資料夾的 BusinessSpaceDetailPage.xaml 檔案
  2. 在 </ContentPage.Resources> (第18行) 宣告之後,加入底下程式碼
  <ContentPage.ToolbarItems>
    <ToolbarItem Command="{Binding 查看地圖Command}" Text="查看地圖" Order="Secondary" Priority="0" />
    <ToolbarItem Command="{Binding 撥打電話Command}" Text="撥打電話" Order="Secondary" Priority="0" />
    <ToolbarItem Command="{Binding 發送簡訊Command}" Text="發送簡訊" Order="Secondary" Priority="0" />
    <ToolbarItem Command="{Binding 發送電子郵件Command}" Text="發送電子郵件" Order="Secondary" Priority="0" />
    <ToolbarItem Command="{Binding 分享內容Command}" Text="分享內容" Order="Secondary" Priority="0" />
    <ToolbarItem Command="{Binding 分享連結Command}" Text="分享連結" Order="Secondary" Priority="0" />
    <ToolbarItem Command="{Binding 查看官網Command}" Text="查看官網" Order="Secondary" Priority="0" />
  </ContentPage.ToolbarItems>
  1. 上面 XAML 宣告了七個工具列項目,可以讓使用者針對這個頁面內容,進行不同動作的操作。
  2. 開啟 核心PCL XFCreative 專案內 ViewModels 資料夾的 BusinessSpaceDetailPageViewModel.cs 檔案,並且以底下程式碼置換這個檔案內容。
  3. 在建構式內,使用 Prism Unity 注入的 IPageDialogService 實作物件,這個物件可以提供一個對話窗機制,例如,這行指令,將會執行出如下圖結果
    await _dialogService.DisplayAlert("抱歉", $"此功能尚未建置", "確定");
    工具列使用2
  4. 在建構式內,產生了許多 DelegateCommand 物件,這些物件將會綁定到檢視內的ContentPage.ToolbarItems 裡面的各個工具列的宣告。
  5. 當取得了某個 GPS 座標點,想要顯示外部地圖程式,導航到這個座標點,可以使用底下程式碼。
    var success = await CrossExternalMaps.Current.NavigateTo(創業空間明細.創業空間名稱, fooLat, fooLon, Plugin.ExternalMaps.Abstractions.NavigationType.Default);
  6. 想要使用裝置撥打電話,可以使用底下程式碼
    phoneCallTask.MakePhoneCall(創業空間明細.連絡電話);
  7. 要分享某段文字內容,也是相當容易的,使用底下程式碼來執行,此時,手機將會顯示並且請您選擇要分享的應用程式,選擇之後,就可以把這段文字分享出去。
    await CrossShare.Current.Share(message, title);
  8. 要分享URL連結,也是相當容易的,使用底下程式碼來執行,此時,手機將會顯示並且請您選擇要分享的應用程式,選擇之後,就可以把URL連結分享出去。
    await CrossShare.Current.ShareLink(url, message, title);
  9. 想要發送電子郵件,您可以參考下面程式碼 發送電子郵件() 方法內的註解程式碼
  10. 想要發送簡訊,您可以參考下面程式碼 發送簡訊() 方法內的註解程式碼
  11. 當取得一個網址,想要使用外部瀏覽器開啟這個網址,可以使用底下程式碼
    await CrossShare.Current.OpenBrowser(創業空間明細.官方網站);
上述的許多功能都可以正常使用,這是因為您一開建立專案的時候,您有加入許多 NuGet 套件或者插件,而在這個檢視模型的程式碼內,就可以直接呼叫這些套件方法;此時,您只管需要呼叫這些功能,至於每個平台的專屬特性與功能,套件與插件會幫您處理掉這些繁雜問題。

BusinessSpaceDetailPageViewModel.cs

using Newtonsoft.Json;
using Plugin.ExternalMaps;
using Plugin.Messaging;
using Plugin.Share;
using Prism.Commands;
using Prism.Mvvm;
using Prism.Navigation;
using Prism.Services;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using XFCreative.Models;
using XFCreative.Services;

namespace XFCreative.ViewModels
{
    public class BusinessSpaceDetailPageViewModel : BindableBase, INavigationAware
    {
        private readonly INavigationService _navigationService;
        public readonly IPageDialogService _dialogService;


        public DelegateCommand 查看空間資訊Command { get; set; }
        public DelegateCommand 查看價格方案Command { get; set; }
        public DelegateCommand 查看地圖Command { get; set; }
        public DelegateCommand 撥打電話Command { get; set; }
        public DelegateCommand 發送簡訊Command { get; set; }
        public DelegateCommand 發送電子郵件Command { get; set; }
        public DelegateCommand 分享內容Command { get; set; }
        public DelegateCommand 分享連結Command { get; set; }
        public DelegateCommand 查看官網Command { get; set; }

        private 創業空間明細 _創業空間明細;
        public 創業空間明細 創業空間明細
        {
            get { return _創業空間明細; }
            set { SetProperty(ref _創業空間明細, value); }
        }
        private string _title;
        public string Title
        {
            get { return _title; }
            set { SetProperty(ref _title, value); }
        }

        public BusinessSpaceDetailPageViewModel(INavigationService navigationService, IPageDialogService dialogService)
        {
            // 取得頁面導航的實作
            _navigationService = navigationService;
            _dialogService = dialogService;

            查看空間資訊Command = new DelegateCommand(查看空間資訊);
            查看價格方案Command = new DelegateCommand(查看價格方案);
            查看地圖Command = new DelegateCommand(查看地圖);
            撥打電話Command = new DelegateCommand(撥打電話);
            發送簡訊Command = new DelegateCommand(發送簡訊);
            發送電子郵件Command = new DelegateCommand(發送電子郵件);
            分享內容Command = new DelegateCommand(分享內容);
            分享連結Command = new DelegateCommand(分享連結);
            查看官網Command = new DelegateCommand(查看官網);
        }

        public void OnNavigatedFrom(NavigationParameters parameters)
        {
        }

        public void OnNavigatedTo(NavigationParameters parameters)
        {
            if (parameters.ContainsKey("創業空間Selected"))
            {
                var foo創業空間Selected = parameters["創業空間Selected"] as 創業空間NodeViewModel;
                系統初始化(foo創業空間Selected);
            }

        }

        public void 系統初始化(創業空間NodeViewModel 創業空間NodeViewModel)
        {
            var fooItem = GlobalData.創業空間Repository.Items.FirstOrDefault(x => x.創業空間名稱 == 創業空間NodeViewModel.創業空間名稱);
            if (fooItem != null)
            {
                創業空間明細 = new 創業空間明細()
                {
                    創業空間名稱 = fooItem.創業空間名稱,
                    使用坪數 = fooItem.使用坪數,
                    使用時間 = fooItem.使用時間,
                    修改時間 = fooItem.修改時間,
                    備註 = fooItem.備註,
                    價格方案 = fooItem.價格方案,
                    創業空間類型 = fooItem.創業空間類型,
                    地址 = fooItem.地址,
                    官方網站 = fooItem.官方網站,
                    座標經度 = fooItem.座標經度,
                    座標緯度 = fooItem.座標緯度,
                    建物現況 = fooItem.建物現況,
                    建立時間 = fooItem.建立時間,
                    建築類型 = fooItem.建築類型,
                    建造材質 = fooItem.建造材質,
                    所屬單位 = fooItem.所屬單位,
                    招募團隊類型 = fooItem.招募團隊類型,
                    樓別樓高 = fooItem.樓別樓高,
                    標籤 = fooItem.標籤,
                    空間主照片 = fooItem.空間主照片,
                    空間是否出租 = fooItem.空間是否出租,
                    空間資訊 = fooItem.空間資訊,
                    縣市區域 = fooItem.縣市區域,
                    聯絡email = fooItem.聯絡email,
                    聯絡人 = fooItem.聯絡人,
                    詳細照片 = fooItem.詳細照片,
                    連絡電話 = fooItem.連絡電話,
                    進駐使用人數 = fooItem.進駐使用人數,
                };
            }
        }

        private async void 查看空間資訊()
        {
            var fooNavigationParameters = new NavigationParameters();
            var fooItem = new 網頁資料NodeViewModel()
            {
                標題名稱 = "空間資訊",
                網頁內容 = 創業空間明細.空間資訊,
            };

            fooNavigationParameters.Add("網頁資料NodeViewModel", fooItem);
            await _navigationService.Navigate("WebView更多資訊Page", fooNavigationParameters);
        }

        private async void 查看價格方案()
        {
            var fooNavigationParameters = new NavigationParameters();
            var fooItem = new 網頁資料NodeViewModel()
            {
                標題名稱 = "價格方案",
                網頁內容 = 創業空間明細.價格方案,
            };

            fooNavigationParameters.Add("網頁資料NodeViewModel", fooItem);
            await _navigationService.Navigate("WebView更多資訊Page", fooNavigationParameters);
        }

        private async void 查看地圖()
        {
            if (string.IsNullOrEmpty(創業空間明細.座標經度) == false && string.IsNullOrEmpty(創業空間明細.座標緯度) == false)
            {
                var fooLat = Convert.ToDouble(創業空間明細.座標緯度);
                var fooLon = Convert.ToDouble(創業空間明細.座標經度);
                var success = await CrossExternalMaps.Current.NavigateTo(創業空間明細.創業空間名稱, fooLat, fooLon, Plugin.ExternalMaps.Abstractions.NavigationType.Default);
            }
        }

        private void 撥打電話()
        {
            if (string.IsNullOrEmpty(創業空間明細.連絡電話) == false)
            {
                // Make Phone Call
                var phoneCallTask = MessagingPlugin.PhoneDialer;
                if (phoneCallTask.CanMakePhoneCall)
                    phoneCallTask.MakePhoneCall(創業空間明細.連絡電話);
            }
        }

        private async void 分享內容()
        {
            if (string.IsNullOrEmpty(創業空間明細.官方網站) == false)
            {
                var title = "我找到一個好地方";
                var message = 創業空間明細.創業空間名稱;

                // Share message and an optional title.
                await CrossShare.Current.Share(message, title);
            }
        }

        private async void 分享連結()
        {
            if (string.IsNullOrEmpty(創業空間明細.官方網站) == false)
            {
                var title = "我找到一個好地方";
                var message = 創業空間明細.創業空間名稱;
                var url = 創業空間明細.官方網站;

                // Share a link and an optional title and message.
                await CrossShare.Current.ShareLink(url, message, title);
            }
        }

        private async void 發送電子郵件()
        {
            await _dialogService.DisplayAlert("抱歉", $"此功能尚未建置", "確定");
            //var emailTask = MessagingPlugin.EmailMessenger;
            //if (emailTask.CanSendEmail)
            //{
            //    // Send simple e-mail to single receiver without attachments, CC, or BCC.
            //    emailTask.SendEmail("plugins@xamarin.com", "Xamarin Messaging Plugin", "Hello from your friends at Xamarin!");

            //    // Send a more complex email with the EmailMessageBuilder fluent interface.
            //    var email = new EmailMessageBuilder()
            //      .To("plugins@xamarin.com")
            //      .Cc("plugins.cc@xamarin.com")
            //      .Bcc(new[] { "plugins.bcc@xamarin.com", "plugins.bcc2@xamarin.com" })
            //      .Subject("Xamarin Messaging Plugin")
            //      .Body("Hello from your friends at Xamarin!")
            //      .Build();

            //    emailTask.SendEmail(email);
            //}
        }

        private async void 發送簡訊()
        {
            await _dialogService.DisplayAlert("抱歉", $"此功能尚未建置", "確定");
            //var smsMessenger = MessagingPlugin.SmsMessenger;
            //if (smsMessenger.CanSendSms)
            //    smsMessenger.SendSms("+272193343499", "Hello from your friends at Xamarin!");
        }

        private async void 查看官網()
        {
            if (string.IsNullOrEmpty(創業空間明細.官方網站) == false)
            {
                await CrossShare.Current.OpenBrowser(創業空間明細.官方網站);
            }
        }

    }
}

工具列之資料重新整理

  1. 開啟 核心PCL XFCreative 專案內 Views 資料夾的 BusinessSpacePage.xaml 檔案
  2. 在 <Grid 項目前 (第11行),加入底下 XAML 程式碼宣告
  3. 上述新增加的 XAML 宣告,加入了 ContentPage.ToolbarItems 項目 (Element),也就是可以在 ContentPage 的上方顯示這個新定義的工具列項目。

BusinessSpacePage.xaml

  <ContentPage.ToolbarItems>
    <ToolbarItem
      Command="{Binding 搜尋Command}"
      Text="搜尋"
      Order="Primary"
      Priority="0">
      <ToolbarItem.Icon>
        <OnPlatform x:TypeArguments="FileImageSource"
          iOS="search.png"
          Android="search.png"
          WinPhone="Assets/search.png" />
      </ToolbarItem.Icon>
    </ToolbarItem>
    <ToolbarItem
      Command="{Binding RefreshDataCommand}"
      Text="重新整理"
      Order="Primary"
      Priority="0">
      <ToolbarItem.Icon>
        <OnPlatform x:TypeArguments="FileImageSource"
          iOS="refresh.png"
          Android="refresh.png"
          WinPhone="Assets/refresh.png" />
      </ToolbarItem.Icon>
    </ToolbarItem>
  </ContentPage.ToolbarItems>
  1. 請找到 <ViewCell> 宣告,在這個項目之後,加入底下 XAML 宣告,這將定義了當使用者長按了某個清單項目,就會彈出這兩個新定義的功能表清單,如下圖所示
    工具列使用3
            <ViewCell.ContextActions>
              <MenuItem
                Command="{Binding 查看地圖Command}"
                CommandParameter="{Binding .}"
                Text="查看地圖" />
              <MenuItem
                Command="{Binding 查看官網Command}"
                CommandParameter="{Binding .}"
                Text="查看官網" IsDestructive="True" />
            </ViewCell.ContextActions>
  1. 開啟 核心PCL XFCreative 專案內 ViewModels 資料夾的 BusinessSpacePageViewModel.cs 檔案
  2. 將底下程式碼置換掉這個檔案內容
    在建構式內增加了兩個 DelegateCommand 物件,用來綁定上方工具列的 Command 屬性。
    RefreshData() 命令方法則是會切換到應用程式一開始啟動的頁面,也就是要重新抓取網路上最新的開放資料;而在 Navigate 的第一個參數使用的絕對位置標示 "/MainPage" ,這表示當切換到新的葉面之後,導航堆疊內的所有項目都要清空。
    搜尋() 命令方法將會切換到另外一個頁面,設定要過濾的條件,這部分會在下一小節實作出來。

BusinessSpacePageViewModel.cs

using Newtonsoft.Json;
using Prism.Commands;
using Prism.Events;
using Prism.Mvvm;
using Prism.Navigation;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using XFCreative.Models;
using XFCreative.Services;
using System.Linq;
using Plugin.Share;
using Plugin.ExternalMaps;

namespace XFCreative.ViewModels
{
    public class BusinessSpacePageViewModel : BindableBase, INavigationAware
    {
        private readonly INavigationService _navigationService;
        private readonly IEventAggregator _eventAggregator;
        public DelegateCommand RefreshDataCommand { get; set; }
        public DelegateCommand 搜尋Command { get; set; }
        public DelegateCommand 創業空間ItemSelectedCommand { get; set; }

        #region 創業空間NodeViewModel 清單
        private ObservableCollection<創業空間NodeViewModel> _創業空間Nodes = new ObservableCollection<創業空間NodeViewModel>();
        public ObservableCollection<創業空間NodeViewModel> 創業空間Nodes
        {
            get { return this._創業空間Nodes; }
            set { this.SetProperty(ref this._創業空間Nodes, value); }
        }
        #endregion

        #region 創業空間Selected
        public 創業空間NodeViewModel 創業空間Selected { get; set; }
        #endregion

        public Action 回到ListView最前面;

        private string _title;
        public string Title
        {
            get { return _title; }
            set { SetProperty(ref _title, value); }
        }

        public BusinessSpacePageViewModel(INavigationService navigationService, IEventAggregator eventAggregator)
        {
            // 取得頁面導航的實作
            _navigationService = navigationService;
            _eventAggregator = eventAggregator;

            RefreshDataCommand = new DelegateCommand(RefreshData);
            搜尋Command = new DelegateCommand(搜尋);

            創業空間ItemSelectedCommand = new DelegateCommand(創業空間ItemSelected);

            _eventAggregator.GetEvent<需要篩選資料Event>().Subscribe(需要篩選資料HandleEvent);
        }

        public void OnNavigatedFrom(NavigationParameters parameters)
        {

        }

        public void OnNavigatedTo(NavigationParameters parameters)
        {
            if (parameters.ContainsKey("title"))
                Title = (string)parameters["title"] + " ...";

            if (parameters.ContainsKey("City"))
            {
                var foo = (string)parameters["City"];
                if (foo == "All")
                {
                    系統初始化();
                }
            }
        }

        public void 系統初始化()
        {
            創業空間Nodes.Clear();
            var foo創業空間s = GlobalData.創業空間Repository.Items;

            foreach (var item in foo創業空間s)
            {
                var fooItem = new 創業空間NodeViewModel()
                {
                    縣市區域 = item.縣市區域,
                    使用坪數 = item.使用坪數,
                    創業空間名稱 = item.創業空間名稱,
                    地址 = item.地址,
                    空間主照片 = item.空間主照片.Replace("https", "http")
                };

                創業空間Nodes.Add(fooItem);
            }
        }

        private async void RefreshData()
        {
            var fooNavPara = new NavigationParameters();
            fooNavPara.Add("title", "使用者要求重新整理");
            await _navigationService.Navigate("/MainPage", fooNavPara);
        }

        private async void 搜尋()
        {
            await _navigationService.Navigate("SelectCityPage");
        }

        private async void 創業空間ItemSelected()
        {
            var foo創業空間Selected = new NavigationParameters();
            foo創業空間Selected.Add("創業空間Selected", 創業空間Selected);
            await _navigationService.Navigate("BusinessSpaceDetailPage", foo創業空間Selected);
        }

        private void 需要篩選資料HandleEvent(string obj)
        {
            創業空間Nodes = new ObservableCollection<創業空間NodeViewModel>();
            var fooItems = GlobalData.創業空間Repository.Items.Where(x => x.縣市區域 == obj);
            foreach (var item in fooItems)
            {
                var fooItem = new 創業空間NodeViewModel()
                {
                    縣市區域 = item.縣市區域,
                    使用坪數 = item.使用坪數,
                    創業空間名稱 = item.創業空間名稱,
                    地址 = item.地址,
                    空間主照片 = item.空間主照片.Replace("https", "http")
                };

                創業空間Nodes.Add(fooItem);
            }

            回到ListView最前面?.Invoke();

            //if (回到ListView最前面 != null)
            //{
            //    回到ListView最前面();
            //}

        }
    }
}
  1. 開啟 核心PCL XFCreative 專案內 ViewModels 資料夾的 創業空間NodeViewModel.cs 檔案
  2. 將底下程式碼置換掉這個檔案內容

創業空間NodeViewModel.cs

using Plugin.ExternalMaps;
using Plugin.Share;
using Prism.Commands;
using Prism.Mvvm;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using XFCreative.Services;

namespace XFCreative.ViewModels
{
    public class 創業空間NodeViewModel : BindableBase
    {
        public DelegateCommand<創業空間NodeViewModel> 查看地圖Command { get; set; }
        public DelegateCommand<創業空間NodeViewModel> 查看官網Command { get; set; }

        #region 縣市區域
        private string _縣市區域;
        public string 縣市區域
        {
            get { return _縣市區域; }
            set { SetProperty(ref _縣市區域, value); }
        }
        #endregion

        #region 創業空間名稱
        private string _創業空間名稱;
        public string 創業空間名稱
        {
            get { return _創業空間名稱; }
            set { SetProperty(ref _創業空間名稱, value); }
        }
        #endregion

        #region 空間主照片
        private string _空間主照片;

        public string 空間主照片
        {
            get { return _空間主照片; }
            set { SetProperty(ref _空間主照片, value); }
        }
        #endregion

        #region 使用坪數
        private string _使用坪數;

        public string 使用坪數
        {
            get { return _使用坪數; }
            set { SetProperty(ref _使用坪數, value); }
        }
        #endregion

        #region 地址
        private string _地址;

        public string 地址
        {
            get { return _地址; }
            set { SetProperty(ref _地址, value); }
        }

        #endregion

        public 創業空間NodeViewModel()
        {
            查看地圖Command = new DelegateCommand<創業空間NodeViewModel>(查看地圖);
            查看官網Command = new DelegateCommand<創業空間NodeViewModel>(查看官網);
        }


        private async void 查看官網(創業空間NodeViewModel obj)
        {
            var 創業空間明細 = GlobalData.創業空間Repository.Items.FirstOrDefault(x => x.創業空間名稱 == obj.創業空間名稱);
            if (創業空間明細 != null)
            {
                if (string.IsNullOrEmpty(創業空間明細.官方網站) == false)
                {
                    await CrossShare.Current.OpenBrowser(創業空間明細.官方網站);
                }
            }
        }

        private async void 查看地圖(創業空間NodeViewModel obj)
        {
            var 創業空間明細 = GlobalData.創業空間Repository.Items.FirstOrDefault(x => x.創業空間名稱 == obj.創業空間名稱);
            if (創業空間明細 != null)
            {
                if (string.IsNullOrEmpty(創業空間明細.座標經度) == false && string.IsNullOrEmpty(創業空間明細.座標緯度) == false)
                {
                    var fooLat = Convert.ToDouble(創業空間明細.座標緯度);
                    var fooLon = Convert.ToDouble(創業空間明細.座標經度);
                    var success = await CrossExternalMaps.Current.NavigateTo(創業空間明細.創業空間名稱, fooLat, fooLon, Plugin.ExternalMaps.Abstractions.NavigationType.Default);
                }
            }
        }
    }
}

工具列之資料過濾設定

最後,將要來處理過濾資料的需求,這部分除了需要建立一個檢視,讓使用者選擇過濾資料之外,還需要在這個過濾條件檢視出現之後,而使用者選擇完成之後,可以讓主清單畫面可以根據剛剛設定的條件,過濾顯示最新的清單項目;在這裡,您將會使用 Prism 提供的 IEventAggregator。
IEventAggregator 介面的實作物件所提供的功能,與 Xamarin.Forms 提供的訊息中心(MessageCenter) 類似,不過,IEventAggregator使用了弱參考(Weak Reference);使用這樣的機制的好處就是,當您訂閱一個事件之後,您不需要解除訂閱,且也不會造成記憶體洩漏(Memory Leak) 的問題。
不過,在使用 IEventAggregator 之前,需要定義一個事件專屬類別,用來區別不同的事件。

建立 事件專屬類別

  1. 在核心PCL XFCreative 專案內,使用滑鼠右鍵點選 Services 資料夾,接著,選擇 加入 > 類別
  2. 在 加入新項目 - XFCreative 對話窗中,點選 Visual C# > 類別
  3. 在底下名稱欄位內,輸入 需要篩選資料Event,接著,點選 新增 按鈕
  4. 使用底下程式碼替換掉剛剛產生的檔案內容
  5. 您所自訂的類別,必須要繼承 PubSubEvent 類別,其中,型別泛型的部分,則是定義要傳送的資料類型。

需要篩選資料Event.cs

using Prism.Events;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace XFCreative.Services
{
    public class 需要篩選資料Event : PubSubEvent<string>
    {
    }
}

建立篩選過濾條件頁面檢視與檢視模型

篩選過濾條件頁面 View

  1. 在核心PCL XFCreative 專案內,使用滑鼠右鍵點選 Views 資料夾,接著,選擇 加入 > 新增項目
  2. 在 加入新項目 - XFCreative 對話窗中,點選 Visual C# > Prism > Prism ContentPage (Forms)
    加入項目HomePage
  3. 在底下名稱欄位內,輸入 SelectCityPage,接著,點選 新增 按鈕
  4. 使用底下程式碼替換掉剛剛產生的檔案內容

SelectCityPage.xaml

  1. 在這裡的根項目 (Root Element) 使用的是 ContentPage
<?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:behaviors="clr-namespace:Behaviors;assembly=Behaviors"
  xmlns:prism="clr-namespace:Prism.Mvvm;assembly=Prism.Forms"
  prism:ViewModelLocator.AutowireViewModel="True"
  Title="請選擇要篩選的縣市"
  x:Class="XFCreative.Views.SelectCityPage">

  <Grid
    HorizontalOptions="Fill" VerticalOptions="Fill"
    >
    <ListView
  x:Name="listview所有縣市"
  ItemsSource="{Binding 所有縣市, Mode=TwoWay}"
  SelectedItem="{Binding 所有縣市Selected, Mode=TwoWay}"
  CachingStrategy="RecycleElement" 
      >
      <ListView.Behaviors>
        <behaviors:EventHandlerBehavior EventName="ItemTapped">
          <behaviors:InvokeCommandAction Command="{Binding 所有縣市ItemSelectedCommand}"  />
        </behaviors:EventHandlerBehavior>
      </ListView.Behaviors>
      <ListView.ItemTemplate>
        <DataTemplate>
          <ViewCell>
            <ViewCell.View>
              <Grid
                VerticalOptions="Center"
                  >
                <Label
                  Text ="{Binding 縣市}"
                  Margin="10,5"
                  FontSize="22"
                  HorizontalOptions="Start" VerticalOptions="Center"
                  LineBreakMode="WordWrap"
                    />
              </Grid>
            </ViewCell.View>
          </ViewCell>
        </DataTemplate>
      </ListView.ItemTemplate>
    </ListView>
  </Grid>
</ContentPage>

篩選過濾條件頁面 ViewModel

  1. 在核心PCL XFCreative 專案內,使用滑鼠右鍵點選 ViewModels 資料夾,接著,選擇 加入 > 類別
  2. 在 加入新項目 - XFCreative 對話窗中,點選 Visual C# > 類別
  3. 在底下名稱欄位內,輸入 SelectCityPageViewModel,接著,點選 新增 按鈕
  4. 使用底下程式碼替換掉剛剛產生的檔案內容
  5. 這裡要回傳使用者選擇過濾條件的值,是透過了 Prism 的 IEventAggregator 介面,不過,您也可以在呼叫 _navigationService.GoBack 方法的時候,傳入一個 NavigationParameters 物件,這樣,就可以在原先呼叫端的頁面的 OnNavigatedTo 事件方法中,接收到這個選擇過濾的條件值。
            var fooPara = new NavigationParameters();
            fooPara.Add("City", "AllXXX");
            _navigationService.GoBack(fooPara);

SelectCityPageViewModel.cs

using Newtonsoft.Json;
using Prism.Commands;
using Prism.Events;
using Prism.Mvvm;
using Prism.Navigation;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using XFCreative.Models;
using XFCreative.Services;

namespace XFCreative.ViewModels
{
    public class SelectCityPageViewModel : BindableBase, INavigationAware
    {
        private readonly INavigationService _navigationService;
        private readonly IEventAggregator _eventAggregator;

        #region 所有縣市 清單
        private ObservableCollection<縣市節點ViewModel> _所有縣市 = new ObservableCollection<縣市節點ViewModel>();
        public ObservableCollection<縣市節點ViewModel> 所有縣市
        {
            get { return this._所有縣市; }
            set { this.SetProperty(ref this._所有縣市, value); }
        }
        #endregion

        public DelegateCommand 所有縣市ItemSelectedCommand { get; set; }
        public 縣市節點ViewModel 所有縣市Selected { get; set; }

        public SelectCityPageViewModel(INavigationService navigationService, IEventAggregator eventAggregator)
        {
            // 取得頁面導航的實作
            _navigationService = navigationService;
            _eventAggregator = eventAggregator;

            所有縣市ItemSelectedCommand = new DelegateCommand(所有縣市ItemSelected);
        }

        public void OnNavigatedFrom(NavigationParameters parameters)
        {

        }

        public async void OnNavigatedTo(NavigationParameters parameters)
        {
            await 系統初始化();
        }

        public async Task 系統初始化()
        {
            所有縣市.Clear();
            var fooItems = GlobalData.創業空間Repository.Items.Select(x => x.縣市區域).Distinct().OrderByDescending(x => x);
            foreach (var item in fooItems)
            {
                var fooItem = new 縣市節點ViewModel
                {
                    縣市 = item,
                };
                所有縣市.Add(fooItem);
            }
        }

        private void 所有縣市ItemSelected()
        {
            _eventAggregator.GetEvent<需要篩選資料Event>().Publish(所有縣市Selected.縣市);
            _navigationService.GoBack();
        }

    }
}

建立 縣市節點ViewModel ViewModel

  1. 在核心PCL XFCreative 專案內,使用滑鼠右鍵點選 ViewModels 資料夾,接著,選擇 加入 > 類別
  2. 在 加入新項目 - XFCreative 對話窗中,點選 Visual C# > 類別
  3. 在底下名稱欄位內,輸入 縣市節點ViewModel,接著,點選 新增 按鈕
  4. 使用底下程式碼替換掉剛剛產生的檔案內容

縣市節點ViewModel.cs

using Prism.Mvvm;

namespace XFCreative.ViewModels
{
    public class 縣市節點ViewModel : BindableBase
    {
        #region 縣市
        private string _縣市;
        public string 縣市
        {
            get { return _縣市; }
            set { SetProperty(ref _縣市, value); }
        }
        #endregion
    }
}

顯示創意空間清單的檢視模型

創業空間項目的詳細頁面 ViewModel

  1. 在核心PCL XFCreative 專案內,在 ViewModels 資料夾,開啟 BusinessSpacePageViewModel.cs
  2. 使用底下程式碼替換掉剛剛產生的檔案內容
  3. 在建構式哩,透過 Prism Unity 容器,注入了一個實作的 IEventAggregator 物件,讓您可以透過他來使用非同步且若參考的事件通知機制。而在建構式的後面,使用了程式碼 _eventAggregator.GetEvent<需要篩選資料Event>().Subscribe(需要篩選資料HandleEvent); 訂閱了這個事件,當其他的物件送出這個訊息,需要篩選資料HandleEvent 的方法,就會立刻執行。
  4. 當 需要篩選資料HandleEvent 開始執行的時候,會根據所收到的訊息參數值,過濾出所有符合條件的資料,並且顯示在螢幕上。另外,會再透過檢視(View)的 Code Behind(後製程式碼),將清單捲動到第一個項目上,這是透過了執行這個方法: 回到ListView最前面?.Invoke();

BusinessSpaceDetailPageViewModel.cs

using Newtonsoft.Json;
using Prism.Commands;
using Prism.Events;
using Prism.Mvvm;
using Prism.Navigation;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using XFCreative.Models;
using XFCreative.Services;
using System.Linq;
using Plugin.Share;
using Plugin.ExternalMaps;

namespace XFCreative.ViewModels
{
    public class BusinessSpacePageViewModel : BindableBase, INavigationAware
    {
        private readonly INavigationService _navigationService;
        private readonly IEventAggregator _eventAggregator;
        public DelegateCommand RefreshDataCommand { get; set; }
        public DelegateCommand 搜尋Command { get; set; }
        public DelegateCommand 創業空間ItemSelectedCommand { get; set; }

        #region 創業空間NodeViewModel 清單
        private ObservableCollection<創業空間NodeViewModel> _創業空間Nodes = new ObservableCollection<創業空間NodeViewModel>();
        public ObservableCollection<創業空間NodeViewModel> 創業空間Nodes
        {
            get { return this._創業空間Nodes; }
            set { this.SetProperty(ref this._創業空間Nodes, value); }
        }
        #endregion

        #region 創業空間Selected
        public 創業空間NodeViewModel 創業空間Selected { get; set; }
        #endregion

        public Action 回到ListView最前面;

        private string _title;
        public string Title
        {
            get { return _title; }
            set { SetProperty(ref _title, value); }
        }

        public BusinessSpacePageViewModel(INavigationService navigationService, IEventAggregator eventAggregator)
        {
            // 取得頁面導航的實作
            _navigationService = navigationService;
            _eventAggregator = eventAggregator;

            RefreshDataCommand = new DelegateCommand(RefreshData);
            搜尋Command = new DelegateCommand(搜尋);

            創業空間ItemSelectedCommand = new DelegateCommand(創業空間ItemSelected);

            _eventAggregator.GetEvent<需要篩選資料Event>().Subscribe(需要篩選資料HandleEvent);
        }

        public void OnNavigatedFrom(NavigationParameters parameters)
        {

        }

        public void OnNavigatedTo(NavigationParameters parameters)
        {
            if (parameters.ContainsKey("title"))
                Title = (string)parameters["title"] + " ...";

            if (parameters.ContainsKey("City"))
            {
                var foo = (string)parameters["City"];
                if (foo == "All")
                {
                    系統初始化();
                }
            }
        }

        public void 系統初始化()
        {
            創業空間Nodes.Clear();
            var foo創業空間s = GlobalData.創業空間Repository.Items;

            foreach (var item in foo創業空間s)
            {
                var fooItem = new 創業空間NodeViewModel()
                {
                    縣市區域 = item.縣市區域,
                    使用坪數 = item.使用坪數,
                    創業空間名稱 = item.創業空間名稱,
                    地址 = item.地址,
                    空間主照片 = item.空間主照片.Replace("https", "http")
                };

                創業空間Nodes.Add(fooItem);
            }
        }

        private async void RefreshData()
        {
            var fooNavPara = new NavigationParameters();
            fooNavPara.Add("title", "使用者要求重新整理");
            await _navigationService.Navigate("/MainPage", fooNavPara);
        }

        private async void 搜尋()
        {
            await _navigationService.Navigate("SelectCityPage");
        }

        private async void 創業空間ItemSelected()
        {
            var foo創業空間Selected = new NavigationParameters();
            foo創業空間Selected.Add("創業空間Selected", 創業空間Selected);
            await _navigationService.Navigate("BusinessSpaceDetailPage", foo創業空間Selected);
        }

        private void 需要篩選資料HandleEvent(string obj)
        {
            創業空間Nodes = new ObservableCollection<創業空間NodeViewModel>();
            var fooItems = GlobalData.創業空間Repository.Items.Where(x => x.縣市區域 == obj);
            foreach (var item in fooItems)
            {
                var fooItem = new 創業空間NodeViewModel()
                {
                    縣市區域 = item.縣市區域,
                    使用坪數 = item.使用坪數,
                    創業空間名稱 = item.創業空間名稱,
                    地址 = item.地址,
                    空間主照片 = item.空間主照片.Replace("https", "http")
                };

                創業空間Nodes.Add(fooItem);
            }

            回到ListView最前面?.Invoke();

            //if (回到ListView最前面 != null)
            //{
            //    回到ListView最前面();
            //}

        }
    }
}

創業空間項目的詳細頁面 View

  1. 在核心PCL XFCreative 專案內,在 ViewModels 資料夾,開啟 BusinessSpacePage.xaml.cs
  2. 使用底下程式碼替換掉剛剛產生的檔案內容
  3. 這裡會定義一個 Code Behind 的方法,回到ListView最前面Delegate,這個方法將會捲動到回到第一個清單項目。

BusinessSpacePage.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using Xamarin.Forms;
using XFCreative.ViewModels;

namespace XFCreative.Views
{
    public partial class BusinessSpacePage : ContentPage
    {
        BusinessSpacePageViewModel BusinessSpacePageViewModel;
        public BusinessSpacePage()
        {
            InitializeComponent();

            BusinessSpacePageViewModel = this.BindingContext as BusinessSpacePageViewModel;

            BusinessSpacePageViewModel.回到ListView最前面 = 回到ListView最前面Delegate;
        }

        private void 回到ListView最前面Delegate()
        {
            if (BusinessSpacePageViewModel.創業空間Nodes.Count > 0)
            {
                var fooItem = BusinessSpacePageViewModel.創業空間Nodes[0];
                listview創業空間.ScrollTo(fooItem, ScrollToPosition.Center, false);
            }
        }
    }
}

修改 MainPageViewModel 檢視模型

  1. 在核心PCL XFCreative 專案內的 ViewModels資料夾,開啟 MainPageViewModel.cs
  2. 使用底下程式碼替換掉剛剛產生的檔案內容
  3. 這裡修正了,當要導航到 BusinessSpacePage 頁面,需要傳遞一個 City 名稱的參數,其參數值為 All,表示,當進入到 BusinessSpacePage 頁面後,要顯示所有的資料。

App.xaml.cs

using Newtonsoft.Json;
using Prism.Commands;
using Prism.Mvvm;
using Prism.Navigation;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using XFCreative.Models;
using XFCreative.Services;

namespace XFCreative.ViewModels
{
    public class MainPageViewModel : BindableBase, INavigationAware
    {
        private readonly INavigationService _navigationService;

        private string _title;
        public string Title
        {
            get { return _title; }
            set { SetProperty(ref _title, value); }
        }

        public MainPageViewModel(INavigationService navigationService)
        {
            // 取得頁面導航的實作
            _navigationService = navigationService;
        }

        public void OnNavigatedFrom(NavigationParameters parameters)
        {

        }

        public async void OnNavigatedTo(NavigationParameters parameters)
        {
            if (parameters.ContainsKey("title"))
                Title = (string)parameters["title"] + " ...";

            await 系統初始化();

            var fooPara = new NavigationParameters();
            fooPara.Add("City", "All");
            await _navigationService.Navigate("/HomePage/BusinessSpacePage", fooPara);
        }

        public async Task 系統初始化()
        {
            await GlobalData.創業空間Repository.取得最新資料();
        }
    }
}

使用 Unity 容器註冊新建立的 View

  1. 在核心PCL XFCreative 專案內,開啟 App.xaml.cs
  2. 使用底下程式碼替換掉剛剛產生的檔案內容

App.xaml.cs

using Prism.Unity;
using XFCreative.Views;

namespace XFCreative
{
    public partial class App : PrismApplication
    {
        protected override void OnInitialized()
        {
            InitializeComponent();

            NavigationService.Navigate("MainPage?title=請稍後,正在更新資料");
        }

        protected override void RegisterTypes()
        {
            Container.RegisterTypeForNavigation<MainPage>();
            Container.RegisterTypeForNavigation<HomePage>();
            Container.RegisterTypeForNavigation<BusinessSpacePage>();
            Container.RegisterTypeForNavigation<BusinessSpaceDetailPage>();
            Container.RegisterTypeForNavigation<WebView更多資訊Page>();
            Container.RegisterTypeForNavigation<SelectCityPage>();
        }
    }
}

執行結果

Android 執行結果

請在方案總管內,滑鼠右擊 XFCreative.Droid 專案,選擇 設定為起始專案,接著按下 F5 開始執行。
工具列使用1 工具列使用1工具列使用1 工具列使用1 工具列使用1 工具列使用1