WPF 不帶 TargetPlatformVersion 顯示 Win10 的 Toast 通知的方法

lindexi發表於2024-07-31

本文將告訴大家如何在 WPF 不安裝 WindowsAppSDK 包,且不在 TargetFramework 帶上 TargetPlatformVersion 而彈出 Win10 的 Toast 通知的方法

本文這裡的 TargetPlatformVersion 指的是在 TargetFramework 裡面的內容,如下面的程式碼裡的 10.0.17763.0 就是 TargetPlatformVersion 的值

  <PropertyGroup>
    <TargetFramework>net9.0-windows10.0.17763.0</TargetFramework>
  </PropertyGroup>

不帶 TargetPlatformVersion 即不在 TargetFramework 里加上 10.0.x 的版本號

預設微軟官方推薦使用的是千年不更新的 Microsoft.Toolkit.Uwp.Notifications 庫,配合設定了 TargetPlatformVersion 至少為 10.0.17763.0 版本進行 Toast 通知

其預設推薦方法的 csproj 內容大概如下

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>WinExe</OutputType>
    <TargetFramework>net9.0-windows10.0.17763.0</TargetFramework>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
    <UseWPF>true</UseWPF>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="Microsoft.Toolkit.Uwp.Notifications" Version="7.1.3" />
  </ItemGroup>
</Project>

此方式需要引用 Microsoft.Toolkit.Uwp.Notifications 且在 TargetFramework 里加上 10.0.17763.0 版本。其使用方法非常簡單,如下面程式碼即可彈出文字

        var builder = new ToastContentBuilder()
                .AddText("林德熙是逗比")
            ;
        builder.Show();

然而以上方法我感覺不夠清真。接下來來將告訴大家一個我感覺比較清真的方法

使用 WPF 不安裝 WindowsAppSDK 使用 WinRT 功能的方法 這篇部落格提到的方法,即可不用指定 TargetPlatformVersion 就可以使用 WinRT 的功能

正好 Toast 就是 WinRT 的功能

具體的做法是先取出 Microsoft.Windows.SDK.NET.dll 和 WinRT.Runtime.dll 兩個檔案作為引用,我這裡放在了我的 C:\lindexi\Library 資料夾裡,修改 csproj 引用這兩個檔案,修改之後的 csproj 檔案程式碼如下

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>WinExe</OutputType>
    <TargetFramework>net9.0-windows</TargetFramework>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
    <UseWPF>true</UseWPF>
  </PropertyGroup>

  <ItemGroup>
    <Reference Include="Microsoft.Windows.SDK.NET">
      <HintPath>C:\lindexi\Library\Microsoft.Windows.SDK.NET.dll</HintPath>
    </Reference>
    <Reference Include="WinRT.Runtime">
      <HintPath>C:\lindexi\Library\WinRT.Runtime.dll</HintPath>
    </Reference>
  </ItemGroup>
  
</Project>

如此可以看到 csproj 不需要加上 TargetPlatformVersion 的引用,也沒有 WindowsAppSDK 的引用。看起來清真

完成以上程式碼之後,我在 MainWindow 的 Loaded 事件嘗試彈出通知內容。先根據 https://learn.microsoft.com/en-us/windows/apps/design/shell/tiles-and-notifications/adaptive-interactive-toasts?tabs=xml 文件構建出 XML 程式碼,我這裡的例子程式碼只顯示一行文字

 <toast>
     <visual>
         <binding template='ToastText01'>
             <text id="1">顯示文字內容</text>
         </binding>
     </visual>
 </toast>

完成構建 XML 程式碼之後,需要轉換為 XmlDocument 物件,程式碼如下

            var xmlDocument = new XmlDocument();
            // lang=xml
            var toast = """
                      <toast>
                          <visual>
                              <binding template='ToastText01'>
                                  <text id="1">顯示文字內容</text>
                              </binding>
                          </visual>
                      </toast>
                      """;
            xmlDocument.LoadXml(xml: toast);

使用 XML 直接寫比較適合簡單的業務,可以看到以上的程式碼十分簡單

除了直接編寫 XML 之外,還可以使用模版輔助,如下面程式碼,在 ToastNotificationManager 裡面獲取模版,然後在模版裡面新增內容

xmlDocument = ToastNotificationManager.GetTemplateContent(ToastTemplateType.ToastText01);
XmlNodeList stringElements = xmlDocument.GetElementsByTagName("text");
stringElements[0].AppendChild(xmlDocument.CreateTextNode("顯示文字內容"));

以上這兩個方式的效果都是差不多的,大家可以選自己喜歡的方式

完成基礎配置之後,接下來使用 ToastNotificationManager 將通知彈出,程式碼如下

            var toastNotification = new ToastNotification(xmlDocument);
            var toastNotificationManagerForUser = ToastNotificationManager.GetDefault();
            var toastNotifier = toastNotificationManagerForUser.CreateToastNotifier(applicationId: "應用名");
            toastNotifier.Show(toastNotification);

以上程式碼有一個細節是 CreateToastNotifier 需要傳入應用名,如果沒有傳入將炸異常,這是微軟設計問題

最後別忘記了在開始呼叫 WinRT 之前,使用 ComWrappersSupport 進行初始化

            global::WinRT.ComWrappersSupport.InitializeComWrappers();

完成之後的程式碼如下

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        Loaded += MainWindow_Loaded;
    }

    private void MainWindow_Loaded(object sender, RoutedEventArgs e)
    {
        if (OperatingSystem.IsWindowsVersionAtLeast(10, 0, 15063))
        {
            global::WinRT.ComWrappersSupport.InitializeComWrappers();

            // 以下 XML 的構建,請看
            // https://learn.microsoft.com/en-us/windows/apps/design/shell/tiles-and-notifications/adaptive-interactive-toasts?tabs=xml
            var xmlDocument = new XmlDocument();
            // lang=xml
            var toast = """
                      <toast>
                          <visual>
                              <binding template='ToastText01'>
                                  <text id="1">顯示文字內容</text>
                              </binding>
                          </visual>
                      </toast>
                      """;
            xmlDocument.LoadXml(xml: toast);

            var toastNotification = new ToastNotification(xmlDocument);
            var toastNotificationManagerForUser = ToastNotificationManager.GetDefault();
            var toastNotifier = toastNotificationManagerForUser.CreateToastNotifier(applicationId: "應用名");
            toastNotifier.Show(toastNotification);
        }
    }
}

嘗試執行以上程式碼,就可以看到在視窗載入之後,彈出一條通知訊息

本文程式碼放在 githubgitee 上,可以使用如下命令列拉取程式碼。我整個程式碼倉庫比較龐大,使用以下命令列可以進行部分拉取,拉取速度比較快

先建立一個空資料夾,接著使用命令列 cd 命令進入此空資料夾,在命令列裡面輸入以下程式碼,即可獲取到本文的程式碼

git init
git remote add origin https://gitee.com/lindexi/lindexi_gd.git
git pull origin ffad2f4c67a9e53fb9121f5d807191a5a913098d

以上使用的是國內的 gitee 的源,如果 gitee 不能訪問,請替換為 github 的源。請在命令列繼續輸入以下程式碼,將 gitee 源換成 github 源進行拉取程式碼。如果依然拉取不到程式碼,可以發郵件向我要程式碼

git remote remove origin
git remote add origin https://github.com/lindexi/lindexi_gd.git
git pull origin ffad2f4c67a9e53fb9121f5d807191a5a913098d

獲取程式碼之後,進入 WPFDemo/LenukelbawChejeabecacar/HeregemdibeHeaqereweganilai 資料夾,即可獲取到原始碼

更多技術部落格,請參閱 部落格導航

相關文章