2023年11月7日 星期二

在 .NET MAUI 專案內,如何透過資料綁定做到導航頁面的隱藏與顯示

在 .NET MAUI 專案內,如何透過資料綁定做到導航頁面的隱藏與顯示

當在進行 .NET MAUI 專案開發的時候,會因為當時設計需要,在某些情況下,需要能夠暫時隱藏導航頁面,接著在某些情境下,又需要將導航頁面顯示出來;我們會發現在導航頁面的顯示與隱藏,是透過導航頁面的 IsVisible 這個屬性來控制的,但是這個屬性並沒有提供資料綁定的功能,所以我們無法透過資料綁定的方式來控制導航頁面的顯示與隱藏。

在這裡將會介紹如何透過資料綁定的方式,來控制導航頁面的顯示與隱藏。

若要完成這篇文章的練習程式碼,請先參考這篇 使用 Vulcan.Maui.Template 專案範本來進行 MAUI for Prism 專案開發 文章,來準備專案開發模板的使用環境。

安裝 Prism for MAUI 專案的模板

  • 打開命令提示字元視窗

  • 輸入 dotnet new install Vulcan.Maui.Template

    這個命令將會安裝一個名稱為 Vulcan.Maui.Template 的專案範本,這個專案範本內含有 Prism for MAUI 專案開發所需要的所有檔案與設定,可以參考 https://www.nuget.org/packages/Vulcan.Maui.Template 這個網址,來查看這個專案範本的內容

  • 底下將會是解除安裝過程出現的內容

C:\>dotnet new install Vulcan.Maui.Template
將安裝下列範本套件:
   Vulcan.Maui.Template

成功: Vulcan.Maui.Template::0.2.2 已安裝下列範本:
範本名稱                                 簡短名稱     語言  標記
---------------------------------------  -----------  ----  -------------------------------------------
Vulcan Custom Full Prism .NET MAUI App   Full-Maui    [C#]  MAUI/Android/iOS/macOS/Mac Catalyst/Windows
Vulcan Custom Prism .NET MAUI App        Vulcan-Maui  [C#]  MAUI/Android/iOS/macOS/Mac Catalyst/Windows
Vulcan Custom Prism View and View Model  MVVMItem     [C#]  MAUI/Android/iOS/macOS/Mac Catalyst/Windows

現在來進行專案開發模板的使用

建立採用 Prism 開發框架的 MAUI 專案

  • 打開 Visual Studio 2022 IDE 應用程式

  • 從 [Visual Studio 2022] 對話窗中,點選右下方的 [建立新的專案] 按鈕

  • 在 [建立新專案] 對話窗右半部

    • 切換 [所有語言 (L)] 下拉選單控制項為 [C#]
    • 切換 [所有專案類型 (T)] 下拉選單控制項為 [MAUI]
  • 在中間的專案範本清單中,找到並且點選 [Vulcan Custom Prism .NET MAUI App] 專案範本選項

    若沒有看到這個專案範本,請參考 使用 Vulcan.Maui.Template 專案範本來進行 MAUI for Prism 專案開發 文章,進行安裝這個專案範本到 Visual Studio 2022 內

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

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

  • 在 [專案名稱] 欄位內輸入 MauiOnOffNavigationPage 做為這個專案名稱

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

  • 此時,將會建立一個可以用於 MAUI 開發的專案

建立 客製的導航頁面之 View & ViewModel

  • 滑鼠右擊這個專案節點,也就是 [MauiOnOffNavigationPage] 名稱

  • 從右鍵選單中,選擇 [開啟終端機] 選單指令

  • 請在命令提示字元視窗內輸入 dotnet new MVVMItem --namespace MauiOnOffNavigationPage --view-name Navi

    這個命令將會在這個專案內的 Views 資料夾內產生 NaviPage.xaml / NaviPage.xaml.cs 這個 View 檔案

    另外,會在 ViewModels 資料夾內,產生 NaviPageViewModel.cs 這個 ViewModel 檔案

    這兩個 View 與 ViewModel 檔案,都已經有預設一些內容在裡面

修正 客製的導航頁面之 View & ViewModel

  • 因為剛剛建立的 View & ViewModel 都是使用 ContentPage 這種類型,所以,需要修正這兩個檔案的內容,讓它們都改為使用 NavigationPage 這種類型
  • 打開 NaviPage.xaml 檔案
  • 將 <ContentPage> 這個標籤,改為 <NavigationPage>
  • 在 NavigationPage 節點內,加入這兩個屬性 BarBackgroundColor="Red" BarTextColor="White"
  • 底下是完成後的 NaviPage.xaml 檔案內容
<?xml version="1.0" encoding="utf-8" ?>
<NavigationPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="MauiOnOffNavigationPage.Views.NaviPage"
             xmlns:viewModel="clr-namespace:MauiOnOffNavigationPage.ViewModels"
             x:DataType="viewModel:NaviPageViewModel"
             BarBackgroundColor="Red" BarTextColor="White"
             >

</NavigationPage>
  • 打開 NaviPage.xaml.cs 檔案
  • 將 ContentPage 這個類別,改為 NavigationPage
  • 底下是完成後的 NaviPage.xaml.cs 檔案內容
namespace MauiOnOffNavigationPage.Views;

public partial class NaviPage : NavigationPage
{
    public NaviPage()
    {
        InitializeComponent();
    }
}

對 DI 容器註冊 客製的導航頁面之 View & ViewModel

  • 打開 [MauiProgram.cs] 檔案
  • 找到 container.RegisterForNavigation<MainPage, MainPageViewModel>(); 這行程式碼
  • 在這行程式碼上加入這一行 container.RegisterForNavigation<NaviPage, NaviPageViewModel>();
  • 底下為完成後的 [MauiProgram.cs] 檔案內容
using Prism.Ioc;
using MauiOnOffNavigationPage.ViewModels;
using MauiOnOffNavigationPage.Views;

namespace MauiOnOffNavigationPage;

public static class MauiProgram
{
    public static MauiApp CreateMauiApp()
    {
        var builder = MauiApp.CreateBuilder();
        builder
            .UseMauiApp<App>()
            .UsePrism(prism =>
            {

                prism.RegisterTypes(container =>
                      {
                          container.RegisterForNavigation<NaviPage, NaviPageViewModel>();
                          container.RegisterForNavigation<MainPage, MainPageViewModel>();
                      })
                     .OnInitialized(() =>
                      {
                          // Do some initializations here
                      })
                     .OnAppStart(async navigationService =>
                     {
                         // Navigate to First page of this App
                         var result = await navigationService
                         .NavigateAsync("NaviPage/MainPage");
                         if (!result.Success)
                         {
                             System.Diagnostics.Debugger.Break();
                         }
                     });
            })
            .ConfigureFonts(fonts =>
            {
                fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
                fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
            });

        return builder.Build();
    }
}

開始設計可以動態顯示或關閉導航面板的功能

  • 打開 [MainPageViewModel.cs] 檔案
  • 宣告一個可綁定屬性,用來控制導航頁面的顯示與隱藏
    [ObservableProperty]
    bool isOn = true;
  • 這裡使用了 ObservableProperty 這個屬性,這是 Microsoft.MVVMToolkit 套件所提供的功能,將會透過原始碼產生機制,產生出具有 INotifyPropertyChange 介面的相關程式碼,讓這個屬性可以進行資料綁定
  • 接著,將需要設計一個可綁定命令呼叫的方法,來控制導航頁面的顯示與隱藏
  • 將底下的方法加入到此 ViewModel 內
[RelayCommand]
void OnOff()
{
    IsOn = !IsOn;
}
  • 一旦這個方法被觸發之後,將會把 IsOn 這個屬性反轉為相反的值,透過這個 C# 屬性值得變化,來控制導航頁面的顯示與隱藏
  • 底下將會是完成後的 ViewModel 內容
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;

namespace MauiOnOffNavigationPage.ViewModels;

public partial class MainPageViewModel : ObservableObject, INavigatedAware
{
    #region Field Member
    private int _count;
    private readonly INavigationService navigationService;

    [ObservableProperty]
    string title = "Main Page";

    [ObservableProperty]
    string text = "Click me";
    #endregion

    #region Property Member
    [ObservableProperty]
    bool isOn = true;
    #endregion

    #region Constructor
    public MainPageViewModel(INavigationService navigationService)
    {
        this.navigationService = navigationService;
    }
    #endregion

    #region Method Member
    #region Command Method
    [RelayCommand]
    void OnOff()
    {
        IsOn = !IsOn;
    }
    [RelayCommand]
    private void Count()
    {
        _count++;
        if (_count == 1)
            Text = "Clicked 1 time";
        else if (_count > 1)
            Text = $"Clicked {_count} times";
    }
    #endregion

    #region Navigation Event
    public void OnNavigatedFrom(INavigationParameters parameters)
    {
    }

    public void OnNavigatedTo(INavigationParameters parameters)
    {
    }
    #endregion

    #region Other Method
    #endregion
    #endregion
}
  • 接著,打開 [MainPage.xaml] 檔案
  • 找到 ContentPage 這個節點,加入這個附加屬性的宣告 NavigationPage.HasNavigationBar="{Binding IsOn}"
  • 現在要加入一個按鈕,來觸發 ViewModel 內的方法
<Button Text="導航頁面"
    Command="{Binding OffCommand}"
    HorizontalOptions="Center" />
  • 這裡使用了 Command="{Binding OffCommand}" 宣告,設定這個按鈕被點擊之後,要呼叫 ViewModel 內的 OffCommand 方法,而我們剛剛有在 ViewModel 內設計這個方法,OnOff,並且有加入這個屬性宣告 [RelayCommand] ,所以,透過 Microsoft.MVVMToolkit 套件,將會產生出一個可以用於命令綁定的 OffCommand 屬性,這個屬性將會指向 OnOff 方法

  • 從方案總管來展開 [Dependencies] 節點,將會到如下圖的狀態

  • 其中 [MauiOnOffNavigationPage.ViewModels.MainPageViewModel.Count.g.cs] 這個檔案將會是編譯器產生的原始碼

  • 底下將會是編譯器產生的原始碼內容

// <auto-generated/>
#pragma warning disable
#nullable enable
namespace MauiOnOffNavigationPage.ViewModels
{
    partial class MainPageViewModel
    {
        /// <summary>The backing field for <see cref="OffCommand"/>.</summary>
        [global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.RelayCommandGenerator", "8.1.0.0")]
        private global::CommunityToolkit.Mvvm.Input.RelayCommand? offCommand;
        /// <summary>Gets an <see cref="global::CommunityToolkit.Mvvm.Input.IRelayCommand"/> instance wrapping <see cref="OnOff"/>.</summary>
        [global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.RelayCommandGenerator", "8.1.0.0")]
        [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
        public global::CommunityToolkit.Mvvm.Input.IRelayCommand OffCommand => offCommand ??= new global::CommunityToolkit.Mvvm.Input.RelayCommand(new global::System.Action(OnOff));
    }
}
  • 從這裡可以看出,編譯器產生了一個 OffCommand 屬性,這個屬性將會指向 OnOff 方法 (最後一行程式碼敘述)
  • 底下將會是完成後的 [MainPageViewModel.cs] 檔案內容
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;

namespace MauiOnOffNavigationPage.ViewModels;

public partial class MainPageViewModel : ObservableObject, INavigatedAware
{
    #region Field Member
    private int _count;
    private readonly INavigationService navigationService;

    [ObservableProperty]
    string title = "Main Page";

    [ObservableProperty]
    string text = "Click me";
    #endregion

    #region Property Member
    [ObservableProperty]
    bool isOn = true;
    #endregion

    #region Constructor
    public MainPageViewModel(INavigationService navigationService)
    {
        this.navigationService = navigationService;
    }
    #endregion

    #region Method Member
    #region Command Method
    [RelayCommand]
    void OnOff()
    {
        IsOn = !IsOn;
    }
    [RelayCommand]
    private void Count()
    {
        _count++;
        if (_count == 1)
            Text = "Clicked 1 time";
        else if (_count > 1)
            Text = $"Clicked {_count} times";
    }
    #endregion

    #region Navigation Event
    public void OnNavigatedFrom(INavigationParameters parameters)
    {
    }

    public void OnNavigatedTo(INavigationParameters parameters)
    {
    }
    #endregion

    #region Other Method
    #endregion
    #endregion
}

執行與確認結果

底下是在 Android 模擬器內執行的結果



2023年11月6日 星期一

MongoDB 系列 - 在 Windows 作業系統上安裝 Docker

MongoDB 系列 - 在 Windows 作業系統上安裝 Docker

這是一系列的文章,主要是在說明如何使用 .NET / C# 來進行 NoSQL 之 MongoDB 的程式開發需求,不過,既然是要探討 MongoDB 的程式設計方式,就當然需要有 MongoDB 的服務存在,這可以有三種選擇:使用 MongoDB 提供的 Atlas 雲端服務、安裝 MongoDB 服務到本機電腦或者網路主機上、直接使用現有的 Docker MongoDB Container 容器。

因為我之前並沒有特別去接觸與使用 Docker 這樣的工具,這也許是因為工作的關係與環境問題,不過,這裡,將會介紹如何在 Windows 作業系統上安裝 Docker Desktop,並且,使用 Docker Desktop 來啟動 MongoDB 的 Container 容器。

首先,我們先來看看 Docker 的網站,這裡,可以看到 Docker 這個工具的定義:

Docker is an open platform for developing, shipping, and running applications. Docker enables you to separate your applications from your infrastructure so you can deliver software quickly. With Docker, you can manage your infrastructure in the same ways you manage your applications. By taking advantage of Docker’s methodologies for shipping, testing, and deploying code quickly, you can significantly reduce the delay between writing code and running it in production.

Docker 是一個開放平台,用來開發、運送與執行應用程式。Docker 可以讓你將應用程式與基礎設施分開,這樣,你就可以快速的交付軟體。使用 Docker,你可以使用相同的方式來管理基礎設施與應用程式。透過使用 Docker 的方法來運送、測試與部署程式碼,你可以大幅的減少撰寫程式碼與執行程式碼的時間。

對於第一次接觸 Docker 的開發者而言,這裡有一個簡單的說明,可以讓你快速的了解 Docker 的運作方式:

Docker is a platform for running applications in an isolated environment called a "container" (or Docker container). Applications run differently in containers than they do in virtual machines (VM). A VM virtualizes the hardware, while containers virtualize the operating system. The result is a smaller footprint and better performance than a VM. You can run Docker on Windows and Linux.

Docker 是一個平台,用來在一個稱為「容器」的隔離環境中執行應用程式。應用程式在容器中的執行方式與在虛擬機器中的執行方式不同。虛擬機器會將硬體虛擬化,而容器則是將作業系統虛擬化。結果就是容器的佔用空間比虛擬機器小,而且,效能也比虛擬機器好。你可以在 Windows 與 Linux 上執行 Docker。

在我自身開發環境中,通常幾乎都會僅使用 Windows 作業系統,因此,這裡,將會介紹如何在 Windows 作業系統上安裝 Docker Desktop,確認Docker環境是可以正常運作,在下一篇文章中,將會使用 Docker Desktop 來啟動 MongoDB 的 Container 容器。

若要在 Windows 10 / 11 作業系統中使用 Docker 這個工具,將會有兩種選擇,使用 Hyper-V 或者使用 WSL,這裡將會選擇使用 WSL 的方式。

因此,要能夠執行 Docker 的開發電腦環境,根據官網描述必須具備底下條件

  • WSL 版本 1.1.3.0 或更高版本。
  • Windows 11 64 位:家庭版或專業版 21H2 或更高版本,或企業版或教育版 21H2 或更高版本。
  • Windows 10 64 位:家庭版或專業版 22H2(版本 19045)或更高版本,或企業版或教育版 22H2(版本 19045)或更高版本。
  • 在 Windows 上打開 WSL 2 功能。有關詳細說明,請參閱 Microsoft 文檔。
  • 要在 Windows 10 或 Windows 11 上成功運行 WSL 2,需要以下硬件先決條件:
    • 具有第二級地址轉換 (SLAT) 的 64 位處理器
    • 4GB 系統 RAM
    • 在 BIOS 中啟用硬件虛擬化。有關更多信息,請參閱虛擬化。

現在,我們的開發電腦已經具備上述條件了,那麼,準備開始進行 Docker Desktop 的安裝作業。

安裝 Docker Desktop

  • 開啟 Docker 網頁

  • 登入到 Docker 網站

  • 點選 [Download for Windows] 按鈕 

  • 下載完成後,執行 [Docker Desktop Installer.exe] 這個安裝檔案 

  • 此時,出現 [Installing Docker Desktop 4.25.0(126437)] 這個對話出 

    這裡提示需要使用 WSL 2 來替換 Hyper-V

    何謂 WSL 2?

    WSL 2 是 Windows Subsystem for Linux 的第二代版本,它是一種在 Windows 10 上執行 Linux 二進位應用程式的方法。它包含一個 Linux 核心,可以直接在 Windows 上執行,而不是使用虛擬機器。有關 WSL 2 的詳細資訊,請參閱 Microsoft 文檔

    何謂 Hyper-V?

    Hyper-V 是 Microsoft 的一個硬體虛擬化產品,可讓您在單個物理硬體上運行多個虛擬機器 (VM)。每個 VM 都是一個完整的作業系統,可以獨立運行自己的程序和應用程序。

  • 點選 [Ok] 按鈕

  • 此時,安裝程式正在進行解壓縮與安裝 

  • 安裝完成之後,點選 [Close and restart] 重新啟動作業系統 

  • 重新開機完成後,將會出現 [Docker Subscription Service Agreement] 對話窗

  • 點選右下方的 [Accept] 按鈕

  • 現在看到 [Finish setting up Docker Desktop] 對話窗

  • 這裡使用預設設定選項,接著,點選 [Finish] 按鈕,完成 Docker 的安裝

  • [Docker Desktop] 視窗將會出現在螢幕上,點選 [Sing in] 連結,登入到 Docker 網站 

  • 登入完成之後,將會重新導入到 [Docker Desktop] 視窗

  • 可以回答問卷題目,或者點選 [Skip] 按鈕

  • 現在,經可以看到 Docker Desktop 應用程式了 

確認 Docker 可以正常運作

  • 開啟命令提示字元視窗
  • 再命令提示字元視窗內輸入 docker --version
  • 將會看到現在安裝的 Docker 版本 Docker version 24.0.6, build ed223bc 
  • 現在要來確認 Docker 的容器是否可以正常運作
  • 在命令提示字元視窗內,輸入 docker run hello-world
  • 此時,將會發現到本地端沒有這個 hello-world 影像 Image 檔案存在,所以,將會從 Docker Hub 上拉取下來
C:\Users\vulcan>docker run hello-world
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
719385e32844: Pull complete
Digest: sha256:88ec0acaa3ec199d3b7eaf73588f4518c25f9d34f58ce9a0df68429c5af48e8d
Status: Downloaded newer image for hello-world:latest
  • 一旦 Image 拉取下來之後,就會透過 Container 容器來執行
  • 執行後,將會看到底下文字內容
Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
    (amd64)
 3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
 4. The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
 $ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker ID:
 https://hub.docker.com/

For more examples and ideas, visit:
 https://docs.docker.com/get-started/

  • 緊接著輸入命令 [docker image ls --all],確認 Image 真的有下載下來
C:\Users\vulcan>docker image ls --all
REPOSITORY    TAG       IMAGE ID       CREATED        SIZE
hello-world   latest    9c7a54a9a43c   6 months ago   13.3kB
  • 輸入 [docker container ls --all] 確認有容器啟動了
C:\Users\vulcan>docker container ls --all
CONTAINER ID   IMAGE         COMMAND    CREATED         STATUS                     PORTS     NAMES 

654e829a338a hello-world "/hello" 6 minutes ago Exited (0) 6 minutes ago peaceful_williams