概要
MaterialDesign ToolKit for xamlを使っていて、標準ダイアログでは我慢できなかった。
Snackbarはちょっと物足りないし、Windows10の通知みたいなものも使いたい。
調べたところ、MaterialDesignになじみそうなNotifications.Wpfと、名前がよく似ている(というかフォーク)のNotification.Wpfが見つかった。
2017年から更新されていない前者に対し、ProgressBarとかを追加して2020年現在メンテされてる模様。
いずれもNuGetからインストールできる。
ただMDに馴染むがWindowsの通知機能ではないので、そちらも使える方法を探すことにした。
前提環境
- WPF
- Windows(8.1↑)
- .NET Framework (4.7↑)
Notification.Wpfによる通知
GitHubにサンプルが動画であがっているので、そちらを参考にした。
完成系がこちら↓
Window内への通知
ここで通知を押すとさらに↓画面右下に以下のような通知が出る。
WinRTによる通知
日曜研究室さんの情報、およびリンク先で紹介されているWinRTの参照方法を参考にした。
みかづきメモによると8種類ほどテンプレートをWindowsが用意してくれていて、独自にToastをカスタマイズするにはXMLで行う模様。
完成系がこちら↓
Windowsでの通知なのでアクションセンターで履歴も表示される。
ソースコード
(Livetのプロジェクトで開発しはじめたのに、普通にコードビハインドつかってたりするのは無視してください…)
設定
Windows通知Toastを利用するにはWindowsSDKが必要な様子。はいってなければインストールする。
WinRTを読みこむため元情報にあるようにVisualStudio外でcsprojを編集した。
続いてVisualStudioで、「プロジェクト→プロジェクト参照の追加」で参照マネージャよりSystem.Runtime.WindowsRuntime
とWindows.winmd
の参照の追加を行った。 1
下のソースでなんとなく読み取れると思うが、それぞれProgramFiles(x86)以下のフォルダをたどって、直接指定している。
またNuGetでNotication.Wpfをインストールした。 2
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net472</TargetFramework>
<TargetPlatformVersion>8.0</TargetPlatformVersion><!-- ←これ -->
<UseWPF>true</UseWPF>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Caliburn.Micro.Core" Version="3.2.0" />
<PackageReference Include="LivetCask" Version="3.2.1" />
<PackageReference Include="Notification.WPF" Version="1.0.2.1" />
</ItemGroup>
<ItemGroup>
<Reference Include="System.Runtime.WindowsRuntime">
<HintPath>..\..\..\..\..\..\..\..\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETCore\v4.5\System.Runtime.WindowsRuntime.dll</HintPath>
</Reference>
<Reference Include="Windows">
<HintPath>..\..\..\..\..\..\..\..\Program Files (x86)\Windows Kits\8.1\References\CommonConfiguration\Neutral\Annotated\Windows.winmd</HintPath>
<IsWinMDFile>true</IsWinMDFile>
</Reference>
</ItemGroup>
<ItemGroup>
<None Update="Resources\giant.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>
App.xaml
<Application x:Class="LivetNotifyWpfTest01.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:Notification.Wpf.Controls;assembly=Notifications.Wpf"
xmlns:system="clr-namespace:System;assembly=mscorlib"
xmlns:local="clr-namespace:LivetNotifyWpfTest01"
ShutdownMode="OnMainWindowClose"
StartupUri="Views\MainWindow.xaml"
Startup="Application_Startup">
<Application.Resources>
<local:CustomTemplateSelector x:Key="CustomTemplateSelector" />
<Style TargetType="controls:Notification">
<Setter Property="ContentTemplateSelector" Value="{StaticResource CustomTemplateSelector}" />
<Style.Resources>
<DataTemplate DataType="{x:Type system:String}" x:Key="PinkStringTemplate">
<Label MinHeight="80"
Content="{Binding}"
Foreground="DeepPink" HorizontalContentAlignment="Center"
VerticalContentAlignment="Center" />
</DataTemplate>
</Style.Resources>
</Style>
</Application.Resources>
</Application>
MainWindow.xaml
ボタン2こおいて、通知エリア用にすこしウィンドウを広くしている。
一番したの controles:NotificationArea が完成系の青い通知に相当。
<Window
x:Class="LivetNotifyWpfTest01.Views.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:behaviors="http://schemas.microsoft.com/xaml/behaviors"
xmlns:l="http://schemas.livet-mvvm.net/2011/wpf"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:v="clr-namespace:LivetNotifyWpfTest01.Views"
xmlns:controls="clr-namespace:Notification.Wpf.Controls;assembly=Notifications.Wpf"
xmlns:local="clr-namespace:LivetNotifyWpfTest01"
xmlns:vm="clr-namespace:LivetNotifyWpfTest01.ViewModels"
Title="MainWindow"
Width="525"
Height="350">
<Window.DataContext>
<vm:MainWindowViewModel />
</Window.DataContext>
<behaviors:Interaction.Triggers>
<behaviors:EventTrigger EventName="ContentRendered">
<l:LivetCallMethodAction MethodName="Initialize" MethodTarget="{Binding}" />
</behaviors:EventTrigger>
<behaviors:EventTrigger EventName="Closed">
<l:DataContextDisposeAction />
</behaviors:EventTrigger>
</behaviors:Interaction.Triggers>
<Grid >
<Button x:Name="button" Content="Notification.Wpf" HorizontalAlignment="Left" Height="35" Margin="57,83,0,0" VerticalAlignment="Top" Width="138" Click="button_Click"/>
<Button x:Name="button2" Content="WinRT" HorizontalAlignment="Left" Height="35" Margin="57,150,0,0" VerticalAlignment="Top" Width="138" Click="button_Click2" />
<controls:NotificationArea x:Name="WindowArea" Position="BottomRight" MaxItems="3" Margin="0 0 10 10"/>
</Grid>
</Window>
MainWindow.xaml.cs
using System.Diagnostics;
using System.IO;
using System.Windows;
using Windows.UI.Notifications;
using Notification.Wpf;
namespace LivetNotifyWpfTest01.Views
{
public partial class MainWindow
{
private readonly NotificationManager _notificationManager = new NotificationManager();
public MainWindow()
{
InitializeComponent();
}
/// <summary>
/// Notification.Wpfでの通知
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button_Click(object sender, RoutedEventArgs e)
{
var content = new NotificationContent {Title = "ウィンドウ内の通知やでー", Message = "推して"};
var clickContent = new NotificationContent
{
Title = "推された!💛",
Message = "う、うおーーー。にっぽごーー",
Type = NotificationType.Error
};
_notificationManager.Show(content, "WindowArea", onClick: () => _notificationManager.Show(clickContent));
}
/// <summary>
/// WinRTを使った通知
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button_Click2(object sender, RoutedEventArgs e)
{
var template = ToastTemplateType.ToastImageAndText02;
var xml = ToastNotificationManager.GetTemplateContent(template);
var images = xml.GetElementsByTagName("image");
var src = images[0].Attributes.GetNamedItem("src");
if (src != null)
{
src.InnerText = "file:///" + Path.GetFullPath("Resources\\giant.png");
Debug.WriteLine(src.InnerText);
}
var texts = xml.GetElementsByTagName("text");
texts[0].AppendChild(xml.CreateTextNode("のび太~。"));
texts[1].AppendChild(xml.CreateTextNode("ジャイ子が家に来てほしいってさ~"));
var toast = new ToastNotification(xml);
ToastNotificationManager.CreateToastNotifier("🤩 通 知 🤩").Show(toast);
}
}
}
その他
Notification.WpfはCaliburn.Microに依存しているらしく、サンプルコピペでビルドしたらdependエラーとなった。
csprojにあるように、NuGetでCaliburn.Micro.Coreを追加でインストールしている。
かっこよさをとるか、標準仕様をとるかという選択肢があっていいですね。
はい/いいえ等ユーザからのフォードバックもとれそうなので、ダイアログ替わりにも使えそう。
-
.NET5からは楽になるみたい ↩
-
Notification.Wpfが求めてくるのでCaliburn.Micro.Coreもインストールした。 ↩