添加消息通知功能,使用Handy Control的Grow来实现的

This commit is contained in:
2025-06-14 19:34:12 +08:00
parent 1fbf8b8fa6
commit 5dfce624c4
10 changed files with 195 additions and 81 deletions

View File

@@ -11,6 +11,9 @@
<ui:ThemeResources/>
<ui:XamlControlsResources/>
<ResourceDictionary Source="Resources/DevicesItemTemplateDictionary.xaml" />
<ResourceDictionary Source="pack://application:,,,/HandyControl;component/Themes/SkinDefault.xaml"/>
<ResourceDictionary Source="pack://application:,,,/HandyControl;component/Themes/Theme.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>

View File

@@ -24,6 +24,7 @@ namespace PMSWPF
container.AddSingleton<NavgatorServices>();
container.AddSingleton<DevicesRepositories>();
container.AddSingleton<IDeviceDialogService, DeviceDialogService>();
container.AddSingleton<INotificationService, GrowlNotificationService>();
container.AddSingleton<MainViewModel>();
container.AddSingleton<HomeViewModel>();
container.AddSingleton<DevicesViewModel>();

13
Enums/NotificationType.cs Normal file
View File

@@ -0,0 +1,13 @@
namespace PMSWPF.Enums;
public enum NotificationType
{
Info,
Warning,
Error,
Fatal,
Success,
Clear,
Ask
}

10
Models/Notification.cs Normal file
View File

@@ -0,0 +1,10 @@
using PMSWPF.Enums;
namespace PMSWPF.Models;
public class Notification
{
public string Message { get; set; }
public NotificationType Type { get; set; }
public bool IsGlobal { get; set; }
}

View File

@@ -0,0 +1,77 @@
using HandyControl.Controls;
using PMSWPF.Enums;
using Notification = PMSWPF.Models.Notification;
namespace PMSWPF.Services;
public class GrowlNotificationService : INotificationService
{
public void Show(Notification notification)
{
if (notification == null )
{
return;
}
if (notification.IsGlobal)
{
switch (notification.Type)
{
case NotificationType.Info:
Growl.InfoGlobal(notification.Message);
break;
case NotificationType.Error:
Growl.ErrorGlobal(notification.Message);
break;
case NotificationType.Warning:
Growl.WarningGlobal(notification.Message);
break;
case NotificationType.Success:
Growl.SuccessGlobal(notification.Message);
break;
case NotificationType.Fatal:
Growl.FatalGlobal(notification.Message);
break;
case NotificationType.Clear:
Growl.ClearGlobal();
break;
default:
Growl.InfoGlobal(notification.Message);
break;
}
}
else
{
switch (notification.Type)
{
case NotificationType.Info:
Growl.Info(notification.Message);
break;
case NotificationType.Error:
Growl.Error(notification.Message);
break;
case NotificationType.Warning:
Growl.Warning(notification.Message);
break;
case NotificationType.Success:
Growl.Success(notification.Message);
break;
case NotificationType.Fatal:
Growl.Fatal(notification.Message);
break;
case NotificationType.Clear:
Growl.Clear();
break;
default:
Growl.Info(notification.Message);
break;
}
}
}
public void Show(string message, NotificationType type=NotificationType.Info,bool IsGlobal=true)
{
Show(new Notification(){Message = message,Type = type,IsGlobal = IsGlobal});
}
}

View File

@@ -0,0 +1,10 @@
using PMSWPF.Enums;
using PMSWPF.Models;
namespace PMSWPF.Services;
public interface INotificationService
{
void Show(Notification notification);
void Show(string message, NotificationType type, bool IsGlobal);
}

View File

@@ -1,9 +1,11 @@
using System.Collections.ObjectModel;
using System.Windows;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using HandyControl.Controls;
using HandyControl.Data;
using PMSWPF.Data.Entities;
using PMSWPF.Data.Repositories;
using PMSWPF.Enums;
using PMSWPF.Excptions;
using PMSWPF.Extensions;
using PMSWPF.Helper;
@@ -11,6 +13,8 @@ using PMSWPF.Models;
using PMSWPF.Services;
using PMSWPF.ViewModels.Dialogs;
using PMSWPF.Views.Dialogs;
using MessageBox = System.Windows.MessageBox;
using Notification = PMSWPF.Models.Notification;
namespace PMSWPF.ViewModels;
@@ -18,40 +22,56 @@ public partial class DevicesViewModel : ViewModelBase
{
private readonly IDeviceDialogService _deviceDialogService;
private readonly DevicesRepositories _devicesRepositories;
[ObservableProperty]
private ObservableCollection<Device> _devices;
public DevicesViewModel(IDeviceDialogService deviceDialogService,DevicesRepositories devicesRepositories)
private readonly INotificationService _notificationService;
[ObservableProperty] private ObservableCollection<Device> _devices = new ();
public DevicesViewModel(IDeviceDialogService deviceDialogService, DevicesRepositories devicesRepositories,INotificationService notificationService)
{
_deviceDialogService = deviceDialogService;
_devicesRepositories = devicesRepositories;
_devices = new ObservableCollection<Device>();
_notificationService = notificationService;
}
public async Task OnLoadedAsync()
{
var ds = await _devicesRepositories.GetAll();
_devices.Clear();
foreach (var dbDevice in ds)
{
var deviceExist= _devices.FirstOrDefault(d => d.Id == dbDevice.Id);
if (deviceExist == null)
{
Device device = new Device();
dbDevice.CopyTo(device);
_devices.Add(device);
}
}
}
[RelayCommand]
public void Test()
{
// GrowlInfo info = new GrowlInfo();
// info.Message = "Hello";
// info.Type = InfoType.Error;
// info.ShowCloseButton = true;
_notificationService.Show( "Hello",NotificationType.Info,true);
// Growl.Error("Hello");
// Growl.Info("Hello");
// Growl.Success("Hello");
// Growl.WarningGlobal("Hello");
// Growl.SuccessGlobal("Hello");
// Growl.FatalGlobal("Hello");
// Growl.Ask("Hello", isConfirmed =>
// {
// Growl.Info(isConfirmed.ToString());
// return true;
// });
}
[RelayCommand]
public async void AddDevice()
{
try
{
Device device = await _deviceDialogService.ShowAddDeviceDialog();
if (device != null)
{
@@ -62,15 +82,14 @@ public partial class DevicesViewModel : ViewModelBase
{
// MessageBox.Show("Device added successfully");
await OnLoadedAsync();
_notificationService.Show(new Notification(){Message = "Hello World!",Type = NotificationType.Success,IsGlobal = true});
}
}
}
catch (DbExistException e)
{
Console.WriteLine(e);
MessageBox.Show(e.Message);
}
catch (Exception e)
{
@@ -81,12 +100,6 @@ public partial class DevicesViewModel : ViewModelBase
public override void OnLoaded()
{
OnLoadedAsync().Await((e) =>
{
_deviceDialogService.ShowMessageDialog("",e.Message);
}, () =>
{
});
OnLoadedAsync().Await((e) => { _deviceDialogService.ShowMessageDialog("", e.Message); }, () => { });
}
}

View File

@@ -9,13 +9,18 @@
xmlns:dl="clr-namespace:PMSWPF.Views.Dialogs"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:vm="clr-namespace:PMSWPF.ViewModels"
xmlns:hc="https://handyorg.github.io/handycontrol"
d:DataContext="{d:DesignInstance vm:DevicesViewModel}"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<StackPanel>
<StackPanel ZIndex="1" >
<StackPanel Margin="10 5" Orientation="Horizontal">
<Button Margin="5" Command="{Binding AddDeviceCommand}" Content="添加"/>
<Button Margin="5" Command="{Binding TestCommand}" Content="测试通知"/>
</StackPanel>
<ui:GridView x:Name="BasicGridView"
Margin="10"
IsItemClickEnabled="True"

View File

@@ -10,6 +10,7 @@ public partial class DevicesView : UserControl
public DevicesView()
{
InitializeComponent();
}
private void BasicGridView_ItemClick(object sender, ItemClickEventArgs e)

View File

@@ -9,6 +9,7 @@
xmlns:local="clr-namespace:PMSWPF.Views"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="clr-namespace:PMSWPF.ViewModels"
xmlns:hc="https://handyorg.github.io/handycontrol"
Title="MainView"
Width="800"
Height="600"
@@ -51,35 +52,9 @@
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.Switch}" />
</ui:NavigationViewItem.Icon>
</ui:NavigationViewItem>
<!-- <ui:NavigationViewItemHeader Content="Actions" /> -->
<!-- <ui:NavigationViewItem -->
<!-- x:Name="SamplePage2Item" -->
<!-- Content="Menu Item2" -->
<!-- SelectsOnInvoked="True" -->
<!-- Tag="SamplePage2"> -->
<!-- <ui:NavigationViewItem.Icon> -->
<!-- <ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.Save}" /> -->
<!-- </ui:NavigationViewItem.Icon> -->
<!-- </ui:NavigationViewItem> -->
<!-- <ui:NavigationViewItem -->
<!-- x:Name="SamplePage3Item" -->
<!-- Content="Menu Item3" -->
<!-- Tag="SamplePage3"> -->
<!-- <ui:NavigationViewItem.Icon> -->
<!-- <ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.Refresh}" /> -->
<!-- </ui:NavigationViewItem.Icon> -->
<!-- </ui:NavigationViewItem> -->
</ui:NavigationView.MenuItems>
<!-- <ui:NavigationView.PaneCustomContent> -->
<!-- <ui:HyperlinkButton -->
<!-- x:Name="PaneHyperlink" -->
<!-- Margin="12,0" -->
<!-- Content="More info" -->
<!-- Visibility="Collapsed" /> -->
<!-- </ui:NavigationView.PaneCustomContent> -->
<ui:NavigationView.AutoSuggestBox>
<ui:AutoSuggestBox AutomationProperties.Name="Search">
<ui:AutoSuggestBox.QueryIcon>
@@ -94,7 +69,6 @@
<StackPanel
x:Name="FooterStackPanel"
Margin="0"
Orientation="Vertical"
Visibility="Visible">
<ui:NavigationViewItem Content="设置">
@@ -109,6 +83,7 @@
</ui:NavigationViewItem>
</StackPanel>
</ui:NavigationView.PaneFooter>
<Grid>
<ContentControl Content="{Binding CurrentViewModel}">
<ContentControl.Resources>
@@ -123,6 +98,12 @@
</DataTemplate>
</ContentControl.Resources>
</ContentControl>
<ScrollViewer VerticalScrollBarVisibility="Hidden" HorizontalAlignment="Right">
<StackPanel hc:Growl.GrowlParent="True" VerticalAlignment="Top" Margin="0,10,10,10"/>
</ScrollViewer>
</Grid>
</ui:NavigationView>
</Grid>
</Window>