2022年8月5日 星期五

動手學 .NET MAUI 開發 : 建立專案與核心服務

建立專案與核心服務

一旦開發 MAUI App 的工具與環境都設定好之後,現在將要準備開始進行這個專案開發的工作;不過,在此將會先建立起使用 Prism.Maui 框架的 MAUI 應用程式專案,緊接著將整個應用程式專案的架構建立起來,然後會建立這個應用程式會使用到的服務與資料模型和支援服務類別,因此,在這裡準備要進行的工作可以整理為:

  • 建立 Prism.Maui 專案
  • 了解 MAUI 專案結構
  • 安裝相關 NuGet 套件
  • 建立方案資料夾
  • 建立 資料模型 Model 類別
  • 建立 服務 Service 類別與註冊該服務
  • 建立 支援 Helper 類別

建立 Prism.Maui 專案

  • 開啟 Visual Studio 2022 Preview 版本

  • 點選螢幕右下角的 [建立新的專案] 按鈕

  • 切換右上角的 [所有專案類型] 下拉選單控制項

  • 找到並且點選 [MAUI] 這個選項

  • 從清單中找到並選擇 [Prism .NET MAUI App (Dan Siegel)] 這個專案範本

    A project for creating a Prism .NET MAUI application for iOS, Android, Mac Catalyst, WinUI and Tizen

  • 點選右下角的 [下一步] 按鈕

  • 當出現了 [設定新的專案] 對話窗

  • 在 [專案名稱] 欄位內,輸入 PrismMonkey

  • 點選右下角的 [建立] 按鈕

了解 MAUI 專案結構 Todo

現在使用 Prism 開發框架的 MAUI 專案已經成功建立了

底下是建立好的整個專案結構

初次體驗在 Android 平台執行專案

  • 點選中間上方工具列的 [Windows Machine] 這個工具列按鈕旁的下拉選單三角形

  • 從彈出功能表中,找到 [Android Emulators] 內的任何一個模擬器

  • 接者,開始執行這個專案,讓他可以在 Android 模擬器出現

  • 底下是執行後的結果

現在確定這個專案是有效的,而且可以建置與成功在 Android 模擬器上運行,接下來要來進行開發這個專案會用到的 NuGet 套件安裝。

安裝相關 NuGet 套件

加入 PropertyChanged.Fody 的 NuGet 套件

  • 滑鼠右擊該專案的 [相依性] 節點

  • 從彈出功能表中選擇 [管理 NuGet 套件] 功能選項

  • 此時,[NuGet: PrismMonkey] 視窗將會出現

  • 點選 [瀏覽] 標籤頁次

  • 在左上方的搜尋文字輸入盒內輸入 PropertyChanged.Fody 關鍵字

  • 現在,將會看到 PropertyChanged.Fody 套件出現在清單內

  • 點選這個 PropertyChanged.Fody 套件

  • 點選右上方的 [安裝] 按鈕,安裝這個套件到這個專案內。

加入 Newtonsoft.Json 的 NuGet 套件

  • 滑鼠右擊該專案的 [相依性] 節點

  • 從彈出功能表中選擇 [管理 NuGet 套件] 功能選項

  • 此時,[NuGet: PrismMonkey] 視窗將會出現

  • 點選 [瀏覽] 標籤頁次

  • 在左上方的搜尋文字輸入盒內輸入 Newtonsoft.Json 關鍵字

  • 現在,將會看到 Newtonsoft.Json 套件出現在清單內

  • 點選這個 Newtonsoft.Json 套件

  • 點選右上方的 [安裝] 按鈕,安裝這個套件到這個專案內。

檢查這個專案內安裝了那些 NuGet 套件

  • 滑鼠雙擊 [PrismMonkey] 這個專案節點
  • 可以在這個 [PrismMonkey.csproj] 檔案中,看到底下內容
<ItemGroup>
  <PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
  <PackageReference Include="Prism.DryIoc.Maui" Version="8.1.191-beta" />
  <PackageReference Include="PropertyChanged.Fody" Version="3.4.1" />
</ItemGroup>
  • 這裡描述了這個專案內,已經安裝了那些 NuGet 套件

建立方案資料夾

  • 滑鼠右擊 [PrismMonkey] 專案節點

  • 從彈出功能表中,點選 [加入] > [新增資料夾]

  • 將這個新資料夾重新命名為 Helpers

    這個資料夾將會用來儲存此專案會用到的支援類別 .cs 檔案

  • 滑鼠右擊 [PrismMonkey] 專案節點

  • 從彈出功能表中,點選 [加入] > [新增資料夾]

  • 將這個新資料夾重新命名為 Models

    這個資料夾將會用來儲存此專案會用各個資料模型 Model 類別 .cs 檔案,這裡所謂的 Model,指的就是 MVVM Model-View-ViewModel 中的第一個字

  • 滑鼠右擊 [PrismMonkey] 專案節點

  • 從彈出功能表中,點選 [加入] > [新增資料夾]

  • 將這個新資料夾重新命名為 Services

    這個資料夾將會用來儲存此專案會用到的相關服務類別 .cs 檔案,例如,在這裡將會把要讀取遠端網路上的 JSON 資料這樣的需求,設計到一個服務類別內,接著註冊到相依性注入容器內,而後在 ViewModel 內透過建構式注入的方式,取得這個服務執行個體,便可以在 ViewModel 內進行讀取遠端 JSON 工作了。

底下是完成後的整個方案結構

建立 資料模型 Model 類別

  • 滑鼠右擊 [Models] 資料夾節點

  • 從彈出功能表中,點選 [加入] > [類別]

  • 當 [新增項目 - PrismMonkey] 對話窗出現後

  • 在此對話窗下方的 [名稱] 欄位內,出入 Monkey.cs

  • 點選此對話窗右下方的 [新增] 按鈕

  • 使用底下的 C# 程式碼,替換掉原有這個檔案內的內容

using System.ComponentModel;

namespace PrismMonkey.Models
{
    /// <summary>
    /// 猴子資料模型類別
    /// </summary>
    public class Monkey : INotifyPropertyChanged
    {
        /// <summary>
        /// 實作 INoifyPropertyChanged 介面內的事件成員
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;
        /// <summary>
        /// 姓名
        /// </summary>
        public string Name { get; set; }
        /// <summary>
        /// 地點
        /// </summary>
        public string Location { get; set; }
        /// <summary>
        /// 明細說明
        /// </summary>
        public string Details { get; set; }
        /// <summary>
        /// 圖片網址
        /// </summary>
        public string Image { get; set; }
        /// <summary>
        /// 分布數量
        /// </summary>
        public int Population { get; set; }
        /// <summary>
        /// 緯度
        /// </summary>
        public double Latitude { get; set; }
        /// <summary>
        /// 經度
        /// </summary>
        public double Longitude { get; set; }

    }
}

在這裡設計一個類別,名稱稱為 Monkey,不過,該類別會實作 INotifyPropertyChanged 這個介面,並且依照此介面的要求,要在此類別內宣告一個事件成員 public event PropertyChangedEventHandler PropertyChanged;。這個 PropertyChanged 事件,將會在 XAML 資料綁定中扮演著重大的角色。

在此類別內,也加入了七個 屬性 Property 成員:Name、Location、Details、Image、Population、Latitude、Longitude。會有這七個屬性的定義,這是因為等下要設計的服務類別,將會從 https://www.montemagno.com/monkeys.json URL 下載讀取關於猴子相關的 JSON 資訊。

每個猴子紀錄定在 JSON 使用底下的內容來表示,在這個 JSON 物件中,將會看到有七個屬性存在,這些屬性將會分別對應到這個 C# 類別的屬性。

{
  "Name": "Baboon",
  "Location": "Africa & Asia",
  "Details": "Baboons are African and Arabian Old World monkeys belonging to the genus Papio, part of the subfamily Cercopithecinae.",
  "Image": "https://raw.githubusercontent.com/jamesmontemagno/app-monkeys/master/baboon.jpg",
  "Population": 10000,
  "Latitude": -8.783195,
  "Longitude": 34.508523
}

建立 服務 Service 類別與註冊該服務

完成了資料模型 Model 的設計,現在要來進行從網路讀取 JSON 紀錄並且將這個 JSON 內容反序列化成為 C# 內的集合 Collection 物件

  • 滑鼠右擊 [Services] 資料夾節點

  • 從彈出功能表中,點選 [加入] > [類別]

  • 當 [新增項目 - PrismMonkey] 對話窗出現後

  • 在此對話窗下方的 [名稱] 欄位內,出入 MonkeyService.cs

  • 點選此對話窗右下方的 [新增] 按鈕

  • 使用底下的 C# 程式碼,替換掉原有這個檔案內的內容

using Newtonsoft.Json;
using PrismMonkey.Models;

namespace PrismMonkey.Services
{
    /// <summary>
    /// 猴子服務類別
    /// </summary>
    public class MonkeyService
    {
        /// <summary>
        /// 可以讀取網路服務端點的猴子清單資訊物件
        /// </summary>
        public HttpClient Client { get; set; } = new HttpClient();
        /// <summary>
        /// 從網路讀取到的猴子集合物件
        /// </summary>
        public List<Monkey> Monkeys { get; set; }

        /// <summary>
        /// 取得遠端網路上的猴子資訊清單
        /// </summary>
        /// <returns></returns>
        public async Task<List<Monkey>> GetMonkeysAsync()
        {
            // 若已經有猴子資料,則不會聯網抓取
            if (Monkeys?.Count > 0)
                return Monkeys;

            // 透過服務端點,線上抓取猴子 JSON 清單資訊
            var result = await Client.GetStringAsync("https://www.montemagno.com/monkeys.json");
            Monkeys = JsonConvert.DeserializeObject<List<Monkey>>(result); 

            return Monkeys;
        }
    }
}

在這個 MonkeyService 猴子服務類別內,將會透過 HttpClient 這個執行個體來存取網路服務端點資訊,因此在這裡宣告了一個型別為 HttpClient 的 Client 屬性。對於讀取到的所有猴子紀錄,將會透過集合型別 List<Monkey> 所建立的 Monkey 屬性來儲存起來。

在這個猴子服務類別內,僅簡單的設計一個公開方法,其函式簽章為 public async Task<List<Monkey>> GetMonkeysAsync() ,這表示了,當呼叫了 GetMonkeysAsync 這個方法之後,將會使用非同步方式來執行這個方法,最後,將會回傳這個 List<Monkey> 集合型別的物件回到呼叫這個方法程式碼內。

在這個 GetMonkeysAsync 方法內,首先會檢查 Monkeys 這個集合物件內是否有任何其他物件存在,若有,則會立即結束執行,否則,將會透過這個類別內型別為 HttpClient 的 Client 物件,使用 Client.GetStringAsync("https://www.montemagno.com/monkeys.json") 敘述來將所指定 URL,取得字串內容,由於 GetStringAsync 會回傳一個非同步工作,因此,在這裡將會使用 await 運算子來進行非封鎖式的等待,避免 UI 執行緒被 封鎖 Block 而阻擋了其他程式碼的執行;由於使用了 await 關鍵字,因此,這個方法需要加上 async 修飾詞,使得這個方法將會從同步方法,轉變成為一個非同步方法(這裡講的非同步方法,將會是由編譯器所產生出來的相關程式碼,使其可以使用非同步的方式來執行)。

當取得指定 URL 網址上的字串內容,將會儲存在區域變數 result 內,接著,透過呼叫 JsonConvert.DeserializeObject<List<Monkey>>(result) 方法,把這裡的字串文字,反序列會成為 .NET C# 內一個型別為 List<Monkey> 的集合物件,這裡會將集合物件儲存到 Monkeys 屬性內。

最後,將取得所有猴子集合物件,回傳到呼叫端。

建立 支援 Helper 類別

最後,要來完成一個支援類別的設計,這裡將會設計一個類別,把這個專案內使用到的神奇字串,都儲存到這個類別內,方便管理與讓程式碼更加好維護。

  • 滑鼠右擊 [Helpers] 資料夾節點

  • 從彈出功能表中,點選 [加入] > [類別]

  • 當 [新增項目 - PrismMonkey] 對話窗出現後

  • 在此對話窗下方的 [名稱] 欄位內,出入 ConstantHelper.cs

  • 點選此對話窗右下方的 [新增] 按鈕

  • 使用底下的 C# 程式碼,替換掉原有這個檔案內的內容

namespace PrismMonkey.Helpers
{
    /// <summary>
    /// 管理該專案使用到的字串資源
    /// </summary>
    public static class ConstantHelper
    {
        #region 該應用程式中的頁面名稱字串
        /// <summary>
        /// 猴子集合物件清單的頁面名稱
        /// </summary>
        public static readonly string MonkeyListPage = nameof(MonkeyListPage);
        /// <summary>
        /// 猴子明細資訊的頁面名稱
        /// </summary>
        public static readonly string MonkeyDetailPage = nameof(MonkeyDetailPage);
        #endregion

        #region 頁面導航用到的參數鍵值
        /// <summary>
        /// 傳入猴子參數的鍵值名稱
        /// </summary>
        public static readonly string NavigationKeyMonkey = "Monkey";
        #endregion
    }
}

在這裡設計一個靜態 ConstantHelper 類別,在此靜態類別內,建立了三個唯讀字串,分別為 MonkeyListPage、MonkeyDetailPage 與 NavigationKeyMonkey ,前面兩者將會表示接下來會用到的頁面字串名稱,而這裡將會透過 C# 6.0 所提供的 nameof 運算式功能來設定這些頁面文字內容,在官方網頁上是這麼描述的:運算式 nameof 會產生變數、類型或成員的名稱做為字串常數;而最後一個唯讀字串,將會是用做於頁面導航過程中,將相關資訊物件,傳遞到其他頁面時候會用到的參數鍵值名稱。

完成後的專案結構

底下螢幕截圖,將會是完成這些步驟之後,所看到的專案結構,在這裡建立三個資料夾與三個類別檔案

 









2022年8月4日 星期四

在 Visual Studio 2022 來建立 Android 原生 App 應用程式教學

在 Visual Studio 2022 來建立 Android 原生 App 應用程式

在這篇文章中,將會說明如何建立一個 Android 原生應用程式,在這裡將會設計一個 Hello World 應用程式,其中將會包含一個可以輸入文字的 Widget 與 一個按鈕 和 一個顯示文字的 Widget。

使用者可以輸入文字的控制項中輸入任何的文字內容,接著,點選這個 [Hello] 按鈕,此時,將會觸發一個按鈕事件,在這個按鈕事件委派方法內,將會把剛剛輸入的文字,在其前面加入一個 Hello 文字,並且顯示到一個顯示文字的 Widget 上。

建立 Android 原生 App 專案

  • 啟動 Visual Studio 2022
  • 看到 Visual Studio 2022 對話窗
  • 請點選右下方的 [建立新的專案] 表示透過程式碼 Scaffolding 選擇專案範本以開始使用
  • 當出現 [建立新專案] 對話窗
  • 在中間最上方有三個下拉選單控制項
  • 切換 [所有語言] 下拉選單控制項為 [C#]
  • 切換 [所有平台] 下拉選單控制項為 [Android]
  • 切換 [所有專案類型] 下拉選單控制項為 [行動裝置]
  • 此時,在中間區域將會看到有三種專案範本可以選擇
  • 請點選最上方那個 [Android Application] A project for creating a .NET Android Application
  • 最後,點選右下方的 [下一步] 按鈕

  • 在 [設定新的專案] 對話窗出現後

  • 在 [專案名稱] 欄位內輸入 MyFirstAndroidApp

  • 點選右下方的 [下一步] 按鈕

  • 看到 [其他資訊] 對話窗,點選右下方的 [建立] 按鈕

  • 稍微等候 Visual Studio 建立這個專案

  • 底下是建立好的 Android 原生應用程式 整體方案的結構

設計該應用程式的頁面

  • 在該專案下,找到 [Resources] 資料夾 > [layout] 資料夾 > [activity_main.xml] 檔案,並且打開這個檔案

  • 請將底下的內容,複製到這個檔案內

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <TextView 
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:text="@string/app_text"
    />
    <EditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/editText1" />
    <Button
        android:text="Hello"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/editText1"
        android:id="@+id/button1" />
    <TextView
        android:text="Large Text"
        android:textAppearance="?android:attr/textAppearanceLarge"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/button1"
        android:id="@+id/textView1" />

</RelativeLayout>

在這個 XML 檔案中,宣告了一個 [EditText] Widget,用來讓使用者可以輸入任何的文字,在其下方有個 [Button] Widget ,其按鈕名稱為 [Hello],而最後加入的一個是 [TextView] Widget,將會用來顯示一個問好的唯讀文字在螢幕上。

  • 現在,找到 [MainActivity.cs] 檔案,並打開這個檔案,使用底下的程式碼將其替換
namespace MyFirstAndroidApp
{
    [Activity(Label = "@string/app_name", MainLauncher = true)]
    public class MainActivity : Activity
    {
        protected override void OnCreate(Bundle? savedInstanceState)
        {
            base.OnCreate(savedInstanceState);

            // Set our view from the "main" layout resource
            SetContentView(Resource.Layout.activity_main);

            EditText editText = FindViewById<EditText>(Resource.Id.editText1);
            TextView textView = FindViewById<TextView>(Resource.Id.textView1);
            Button button = FindViewById<Button>(Resource.Id.button1);

            button.Click += (s, e) =>
            {
                textView.Text = "Hi " + editText.Text;
            };

        }
    }
}

在這個 [MainActivity] 類別中,將會有個 [OnCreate] 覆寫方法,請在這個方法內,使用 [FindViewById] 方法,找出畫面中剛剛加入的三個 Widget,並且綁定到 .NET 中的三個區域變數內,最後,宣告 button 這個物件內的 [Click] 事件,一旦這個按鈕被使用者點選之後,將會觸發與執行這個委派事件,如此將會把剛剛輸入的文字,在其前面加入 [Hello] 文字,並且顯示在螢幕上。

執行並且檢視其執行結果

  • 在最上方工具列中間區域,會看到一個綠色實體三角形,請下拉這個下拉選單控制項,從這裡個清單中,選擇 [Android Emulators] > [Pixel 5 - API 32 (Android 12.1 - API 32)] 這個選項

這裡將會是執行結果



 

2022年8月3日 星期三

MAUI : 使用 Prism Template 來建立 MAUI 應用程式

使用 Prism Template 來建立 MAUI 應用程式

在這個時間點,想要開發 MAUI 應用程式,需先要安裝 Visual Studio 2022 的 17.3 Preview 版本,因此,這裡已經安裝好可以開發 MAUI 專案的 17.3 Preview 開發工具了。

首先,要先安裝 Prism Template for MAUI ,想要做到這樣的需求,請先使用管理者權限來開啟 命令提示字元視窗,接著,在該視窗內輸入

dotnet new --install Prism.Templates::8.1.97

就可以把這個專案範本安裝到 Visual Studio 內,底下是下達這個命令之後的執行結果內容。

C:\Windows\system32>dotnet new --install Prism.Templates::8.1.97

歡迎使用 .NET 6.0!
---------------------
SDK 版本: 6.0.400-preview.22330.6

遙測
---------
.NET 工具會收集使用資料,協助我們改進您的體驗。資料會由 Microsoft 收集,並分享給社群使用。您可以選擇退出遙測,只要使用您慣用的殼層,將 DOTNET_CLI_TELEMETRY_OPTOUT 環境變數設定為 '1' 或 'true' 即可。

閱讀更多有關 .NET CLI 工具遙測的內容: https://aka.ms/dotnet-cli-telemetry

----------------
已安裝 ASP.NET Core HTTPS 開發憑證。
若要信任憑證,請執行 'dotnet dev-certs https --trust' (僅限 Windows 與 macOS )。
深入了解 HTTPS: https://aka.ms/dotnet-https
----------------
撰寫第一個應用程式: https://aka.ms/dotnet-hello-world
了解全新功能: https://aka.ms/dotnet-whats-new
探索文件: https://aka.ms/dotnet-docs
於 GitHub 回報問題和尋找來源: https://github.com/dotnet/core
Use 'dotnet --help' 查看可用的命令或瀏覽: https://aka.ms/dotnet-cli
--------------------------------------------------------------------------------------
將安裝下列範本套件:
   Prism.Templates::8.1.97

成功: Prism.Templates::8.1.97 已安裝下列範本:
範本名稱                         簡短名稱         語言  標記
-------------------------------  ---------------  ----  ------------------------------------------------------------
Prism .NET MAUI App              prism-maui       [C#]  MAUI/Android/iOS/macOS/Mac Catalyst/Windows/Tizen
Prism Blank App (Uno Platform)   uno-blank        [C#]  Prism/Xamarin/Uno Platform/WebAssembly/iOS/Android/WinUI/UWP
Prism Blank App (WPF)            wpf-core-blank   [C#]  Desktop
Prism Blank App (Xamarin.Forms)  xf-blank         [C#]  Prism/Xamarin/Xamarin.Forms
Prism Full App (WPF)             wpf-core-full    [C#]  Desktop
Prism Module (WPF)               wpf-module-core  [C#]  Desktop
Prism Module (Xamarin)           xf-module        [C#]  Prism/Xamarin/Xamarin.Forms

建立一個可以支援 Prism 開發框架的 MAUI 專案

  • 開啟 Visual Studio 2022 Preview 版本

  • 點選螢幕右下角的 [建立新的專案] 按鈕

  • 在最上方的 [搜尋範本] 文字輸入盒內

  • 輸入 prism 找出可用的專案範本

  • 現在可以從 [建立新專案] 對話窗內出現了 [Prism .NET MAUI App (Dan Siegel)] 這個專案範本

  • 選擇這個專案範本

  • 點選右下角的 [下一步] 按鈕

  • 當出現了 [設定新的專案] 對話窗

  • 在 [專案名稱] 欄位內,輸入 MyFirstPrismMaui

  • 點選右下角的 [建立] 按鈕

現在使用 Prism 開發框架的 MAUI 專案已經成功建立了

底下是建立好的整個專案結構

首先打開 [MauiProgram.cs] 這個檔案,將會看到底下的內容

namespace MyFirstPrismMaui;

public static class MauiProgram
{
    public static MauiApp CreateMauiApp()
    {
        var builder = MauiApp.CreateBuilder();
        builder
            .UsePrismApp<App>(PrismStartup.Configure)
            .ConfigureFonts(fonts =>
            {
                fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
                fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
            });

        return builder.Build();
    }
}

在這裡所看到的是標準 MAUI 專案的啟動內容,不過,在此使用了

UsePrismApp(PrismStartup.Configure)

敘述,指定要使用 Prism 作為這個專案的開發框架

因此,在這個專案根目錄下,找到並且打開 [PrismStartup.cs] 這個檔案,底下是這個檔案的內容

using MyFirstPrismMaui.Views;

namespace MyFirstPrismMaui;

internal static class PrismStartup
{
    public static void Configure(PrismAppBuilder builder)
    {
        builder.RegisterTypes(RegisterTypes)
                .OnAppStart("NavigationPage/MainPage");
    }

    private static void RegisterTypes(IContainerRegistry containerRegistry)
    {
        containerRegistry.RegisterForNavigation<MainPage>()
                     .RegisterInstance(SemanticScreenReader.Default);
    }
}

在這裡將會看到 Prism 在這個專案中的設定程式碼,首先,對於 DI / IoC Container 相依性注入容器,並不是使用微軟內建的原件,而是 Prism 自己的,因此,想要注入的相關服務,要在這裡來進行註冊,當然,要使用到的頁面,也需要在這裡來宣告。

點選中間上方工具列的 [Windows Machine] 這個工具列按鈕旁的下拉選單三角形

從彈出功能表中,找到 [Android Emulators] 內的任何一個模擬器

接者,開始執行這個專案,讓他可以在 Android 模擬器出現

底下是執行後的結果