diff --git a/BetterGenshinImpact/App.xaml.cs b/BetterGenshinImpact/App.xaml.cs index 2765c740..d428fd6d 100644 --- a/BetterGenshinImpact/App.xaml.cs +++ b/BetterGenshinImpact/App.xaml.cs @@ -69,6 +69,7 @@ public partial class App : Application // Main window with navigation services.AddSingleton(); services.AddSingleton(); + services.AddSingleton(); // Views and ViewModels services.AddSingleton(); @@ -111,6 +112,16 @@ public partial class App : Application return _host.Services.GetService(typeof(T)) as T; } + /// + /// Gets registered service. + /// + /// Instance of the service or . + /// + public static object? GetService(Type type) + { + return _host.Services.GetService(type); + } + /// /// Occurs when the application is loading. /// diff --git a/BetterGenshinImpact/Markup/ServiceLocatorExtension.cs b/BetterGenshinImpact/Markup/ServiceLocatorExtension.cs new file mode 100644 index 00000000..473f21dd --- /dev/null +++ b/BetterGenshinImpact/Markup/ServiceLocatorExtension.cs @@ -0,0 +1,20 @@ +using System; +using System.Windows.Markup; + +namespace BetterGenshinImpact.Markup; + +[MarkupExtensionReturnType(typeof(object))] +public class ServiceLocatorExtension : MarkupExtension +{ + public Type Type { get; set; } = null!; + + public ServiceLocatorExtension() + { + } + + public override object ProvideValue(IServiceProvider serviceProvider) + { + _ = Type ?? throw new ArgumentNullException(nameof(Type)); + return App.GetService(Type)!; + } +} diff --git a/BetterGenshinImpact/View/MainWindow.xaml b/BetterGenshinImpact/View/MainWindow.xaml index 90df3335..8a69f518 100644 --- a/BetterGenshinImpact/View/MainWindow.xaml +++ b/BetterGenshinImpact/View/MainWindow.xaml @@ -18,7 +18,6 @@ ui:Design.Background="{DynamicResource ApplicationBackgroundBrush}" ui:Design.Foreground="{DynamicResource TextFillColorPrimaryBrush}" ExtendsContentIntoTitleBar="True" - Loaded="Window_Loaded" Visibility="{Binding IsVisible, Mode=TwoWay, Converter={StaticResource BooleanToVisibilityConverter}}" WindowState="{Binding WindowState, Mode=TwoWay}" mc:Ignorable="d"> @@ -29,9 +28,6 @@ - - - @@ -113,19 +109,24 @@ Background="Transparent" BorderBrush="Transparent" Command="{Binding HideCommand}" - Icon="{ui:SymbolIcon CaretDown24}" /> + Icon="{ui:SymbolIcon CaretDown24}" + ToolTip="最小化到托盘" /> - - + + + + diff --git a/BetterGenshinImpact/View/MainWindow.xaml.cs b/BetterGenshinImpact/View/MainWindow.xaml.cs index b1d91086..72d1e5ef 100644 --- a/BetterGenshinImpact/View/MainWindow.xaml.cs +++ b/BetterGenshinImpact/View/MainWindow.xaml.cs @@ -5,6 +5,7 @@ using System.Windows; using System.Windows.Media; using Wpf.Ui; using Wpf.Ui.Controls; +using Wpf.Ui.Tray.Controls; namespace BetterGenshinImpact.View; @@ -31,6 +32,11 @@ public partial class MainWindow : FluentWindow, INavigationWindow TryApplySystemBackdrop(); } + private void OnNotifyIconLeftDoubleClick(NotifyIcon sender, RoutedEventArgs e) + { + NotifyIconViewModel.ShowOrHide(); + } + public INavigationView GetNavigation() => RootNavigation; public bool Navigate(Type pageType) => RootNavigation.Navigate(pageType); @@ -65,9 +71,4 @@ public partial class MainWindow : FluentWindow, INavigationWindow return; } } - - private void Window_Loaded(object sender, RoutedEventArgs e) - { - this.TrayContextMenu.DataContext = ViewModel; - } } diff --git a/BetterGenshinImpact/ViewModel/MainWindowViewModel.cs b/BetterGenshinImpact/ViewModel/MainWindowViewModel.cs index a15b4d13..9370cb3c 100644 --- a/BetterGenshinImpact/ViewModel/MainWindowViewModel.cs +++ b/BetterGenshinImpact/ViewModel/MainWindowViewModel.cs @@ -1,156 +1,144 @@ -using System; -using BetterGenshinImpact.Core.Config; +using BetterGenshinImpact.Core.Config; using BetterGenshinImpact.Core.Recognition.OCR; using BetterGenshinImpact.Helpers; using BetterGenshinImpact.Model; using BetterGenshinImpact.Service.Interface; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; -using CommunityToolkit.Mvvm.Messaging; -using CommunityToolkit.Mvvm.Messaging.Messages; using Microsoft.Extensions.Logging; using OpenCvSharp; +using System; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Net.Http; using System.Net.Http.Json; using System.Threading.Tasks; using System.Windows; using Wpf.Ui; -namespace BetterGenshinImpact.ViewModel +namespace BetterGenshinImpact.ViewModel; + +public partial class MainWindowViewModel : ObservableObject { - public partial class MainWindowViewModel : ObservableObject + private readonly ILogger _logger; + private readonly IConfigService _configService; + public string Title { get; set; } = $"BetterGI · 更好的原神 · {Global.Version}"; + + [ObservableProperty] + public bool _isVisible = true; + + [ObservableProperty] + public WindowState _windowState = WindowState.Normal; + + public AllConfig Config { get; set; } + + public MainWindowViewModel(INavigationService navigationService, IConfigService configService) { - private readonly ILogger _logger; - private readonly IConfigService _configService; - public string Title { get; set; } = $"BetterGI · 更好的原神 · {Global.Version}"; - - [ObservableProperty] - public bool _isVisible = true; - - [ObservableProperty] - public WindowState _windowState = WindowState.Normal; - - public AllConfig Config { get; set; } - - public MainWindowViewModel(INavigationService navigationService, IConfigService configService) - { - _configService = configService; - Config = configService.Get(); - _logger = App.GetLogger(); + _configService = configService; + Config = _configService.Get(); + _logger = App.GetLogger(); #if DEBUG - Title += " · Dev"; + Title += " · Dev"; #endif - } + } - [RelayCommand] - private void OnHide() + [RelayCommand] + private void OnHide() + { + IsVisible = false; + } + + [RelayCommand] + [SuppressMessage("CommunityToolkit.Mvvm.SourceGenerators.RelayCommandGenerator", "MVVMTK0039:Async void returning method annotated with RelayCommand")] + private async void OnLoaded() + { + _logger.LogInformation("更好的原神 {Version}", Global.Version); + try { - IsVisible = false; - } - - - [RelayCommand] - private async void OnLoaded() - { - _logger.LogInformation("更好的原神 {Version}", Global.Version); - try + await Task.Run(() => { - await Task.Run(() => + try { - try - { - var s = OcrFactory.Paddle.Ocr(new Mat(Global.Absolute("Assets\\Model\\PaddleOCR\\test_ocr.png"), ImreadModes.Grayscale)); - Debug.WriteLine("PaddleOcr预热结果:" + s); - } - catch (Exception e) - { - Console.WriteLine(e); - _logger.LogError("PaddleOcr预热异常:" + e.Source + "\r\n--" + Environment.NewLine + e.StackTrace + "\r\n---" + Environment.NewLine + e.Message); - var innerException = e.InnerException; - if (innerException != null) - { - _logger.LogError("PaddleOcr预热内部异常:" + innerException.Source + "\r\n--" + Environment.NewLine + innerException.StackTrace + "\r\n---" + Environment.NewLine + innerException.Message); - throw innerException; - } - else - { - throw; - } - } - }); - } - catch (Exception e) - { - MessageBox.Show("PaddleOcr预热失败:" + e.Source + "\r\n--" + Environment.NewLine + e.StackTrace + "\r\n---" + Environment.NewLine + e.Message); - } - - - try - { - await Task.Run(GetNewestInfo); - } - catch (Exception e) - { - Debug.WriteLine("获取最新版本信息失败:" + e.Source + "\r\n--" + Environment.NewLine + e.StackTrace + "\r\n---" + Environment.NewLine + e.Message); - _logger.LogWarning("获取 BetterGI 最新版本信息失败"); - } - } - - private async void GetNewestInfo() - { - try - { - var httpClient = new HttpClient(); - var notice = await httpClient.GetFromJsonAsync(@"https://hui-config.oss-cn-hangzhou.aliyuncs.com/bgi/notice.json"); - if (notice != null && !string.IsNullOrWhiteSpace(notice.Version)) + var s = OcrFactory.Paddle.Ocr(new Mat(Global.Absolute("Assets\\Model\\PaddleOCR\\test_ocr.png"), ImreadModes.Grayscale)); + Debug.WriteLine("PaddleOcr预热结果:" + s); + } + catch (Exception e) { - if (Global.IsNewVersion(notice.Version)) + Console.WriteLine(e); + _logger.LogError("PaddleOcr预热异常:" + e.Source + "\r\n--" + Environment.NewLine + e.StackTrace + "\r\n---" + Environment.NewLine + e.Message); + var innerException = e.InnerException; + if (innerException != null) { - if (!string.IsNullOrEmpty(Config.NotShowNewVersionNoticeEndVersion) - && !Global.IsNewVersion(Config.NotShowNewVersionNoticeEndVersion, notice.Version)) - { - return; - } - - await UIDispatcherHelper.Invoke(async () => - { - var uiMessageBox = new Wpf.Ui.Controls.MessageBox - { - Title = "更新提示", - Content = $"存在最新版本 {notice.Version},点击确定前往下载页面下载最新版本", - PrimaryButtonText = "确定", - SecondaryButtonText = "不再提示", - CloseButtonText = "取消", - }; - - var result = await uiMessageBox.ShowDialogAsync(); - if (result == Wpf.Ui.Controls.MessageBoxResult.Primary) - { - Process.Start(new ProcessStartInfo("https://bgi.huiyadan.com/download.html") { UseShellExecute = true }); - } - else if (result == Wpf.Ui.Controls.MessageBoxResult.Secondary) - { - Config.NotShowNewVersionNoticeEndVersion = notice.Version; - } - }); + _logger.LogError("PaddleOcr预热内部异常:" + innerException.Source + "\r\n--" + Environment.NewLine + innerException.StackTrace + "\r\n---" + Environment.NewLine + innerException.Message); + throw innerException; + } + else + { + throw; } } - } - catch (Exception e) - { - Debug.WriteLine("获取最新版本信息失败:" + e.Source + "\r\n--" + Environment.NewLine + e.StackTrace + "\r\n---" + Environment.NewLine + e.Message); - _logger.LogWarning("获取 BetterGI 最新版本信息失败"); - } + }); + } + catch (Exception e) + { + MessageBox.Show("PaddleOcr预热失败:" + e.Source + "\r\n--" + Environment.NewLine + e.StackTrace + "\r\n---" + Environment.NewLine + e.Message); } - [RelayCommand] - private void OnClosed() + try { - _configService.Save(); - WeakReferenceMessenger.Default.Send(new PropertyChangedMessage(this, "Close", "", "")); - Debug.WriteLine("MainWindowViewModel Closed"); - Application.Current.Shutdown(); + await Task.Run(GetNewestInfo); + } + catch (Exception e) + { + Debug.WriteLine("获取最新版本信息失败:" + e.Source + "\r\n--" + Environment.NewLine + e.StackTrace + "\r\n---" + Environment.NewLine + e.Message); + _logger.LogWarning("获取 BetterGI 最新版本信息失败"); } } -} \ No newline at end of file + + private async void GetNewestInfo() + { + try + { + var httpClient = new HttpClient(); + var notice = await httpClient.GetFromJsonAsync(@"https://hui-config.oss-cn-hangzhou.aliyuncs.com/bgi/notice.json"); + if (notice != null && !string.IsNullOrWhiteSpace(notice.Version)) + { + if (Global.IsNewVersion(notice.Version)) + { + if (!string.IsNullOrEmpty(Config.NotShowNewVersionNoticeEndVersion) + && !Global.IsNewVersion(Config.NotShowNewVersionNoticeEndVersion, notice.Version)) + { + return; + } + + await UIDispatcherHelper.Invoke(async () => + { + var uiMessageBox = new Wpf.Ui.Controls.MessageBox + { + Title = "更新提示", + Content = $"存在最新版本 {notice.Version},点击确定前往下载页面下载最新版本", + PrimaryButtonText = "确定", + SecondaryButtonText = "不再提示", + CloseButtonText = "取消", + }; + + var result = await uiMessageBox.ShowDialogAsync(); + if (result == Wpf.Ui.Controls.MessageBoxResult.Primary) + { + Process.Start(new ProcessStartInfo("https://bgi.huiyadan.com/download.html") { UseShellExecute = true }); + } + else if (result == Wpf.Ui.Controls.MessageBoxResult.Secondary) + { + Config.NotShowNewVersionNoticeEndVersion = notice.Version; + } + }); + } + } + } + catch (Exception e) + { + Debug.WriteLine("获取最新版本信息失败:" + e.Source + "\r\n--" + Environment.NewLine + e.StackTrace + "\r\n---" + Environment.NewLine + e.Message); + _logger.LogWarning("获取 BetterGI 最新版本信息失败"); + } + } +} diff --git a/BetterGenshinImpact/ViewModel/NotifyIconViewModel.cs b/BetterGenshinImpact/ViewModel/NotifyIconViewModel.cs new file mode 100644 index 00000000..01f94344 --- /dev/null +++ b/BetterGenshinImpact/ViewModel/NotifyIconViewModel.cs @@ -0,0 +1,63 @@ +using BetterGenshinImpact.Service.Interface; +using CommunityToolkit.Mvvm.ComponentModel; +using CommunityToolkit.Mvvm.Input; +using System; +using System.Windows; +using System.Windows.Interop; +using Vanara.PInvoke; + +namespace BetterGenshinImpact.ViewModel; + +public partial class NotifyIconViewModel : ObservableObject +{ + [RelayCommand] + public static void ShowOrHide() + { + if (Application.Current.MainWindow.Visibility == Visibility.Visible) + { + Application.Current.MainWindow.Hide(); + } + else + { + Application.Current.MainWindow.Activate(); + Application.Current.MainWindow.Focus(); + Application.Current.MainWindow.Show(); + WindowBacktray.Show(Application.Current.MainWindow); + } + } + + [RelayCommand] + public static void Exit() + { + App.GetService()?.Save(); + Application.Current.Shutdown(); + } +} + +file static class WindowBacktray +{ + public static void Hide(Window window) + { + if (window != null) + { + window.Visibility = Visibility.Hidden; + window.WindowState = WindowState.Minimized; + } + } + + public static void Show(Window window) + { + if (window != null) + { + if (window.Visibility != Visibility.Visible) + { + window.Visibility = Visibility.Visible; + } + if (window.WindowState == WindowState.Minimized) + { + nint hWnd = new WindowInteropHelper(Application.Current.MainWindow).Handle; + _ = User32.SendMessage(hWnd, User32.WindowMessage.WM_SYSCOMMAND, User32.SysCommand.SC_RESTORE, IntPtr.Zero); + } + } + } +}