diff --git a/HandyControl/Controls/NotifyIcon/NotifyIcon.cs b/HandyControl/Controls/Other/NotifyIcon.cs similarity index 79% rename from HandyControl/Controls/NotifyIcon/NotifyIcon.cs rename to HandyControl/Controls/Other/NotifyIcon.cs index b0493987..eb263959 100644 --- a/HandyControl/Controls/NotifyIcon/NotifyIcon.cs +++ b/HandyControl/Controls/Other/NotifyIcon.cs @@ -2,10 +2,10 @@ using System.Windows; using System.Windows.Controls; using System.Windows.Controls.Primitives; -using System.Windows.Data; using System.Windows.Input; using System.Windows.Interop; using System.Windows.Media; +using HandyControl.Data; using HandyControl.Tools; using HandyControl.Tools.Interop; @@ -35,18 +35,40 @@ namespace HandyControl.Controls private string _windowClassName; - private static readonly int WmTaskbarcreated = NativeMethods.RegisterWindowMessage("TaskbarCreated"); + private int _wmTaskbarCreated; private IntPtr _messageWindowHandle; private readonly WndProc _callback; - private ToolTip _toolTip; - - private Popup _contextMenu; + private Popup _contextContent; private bool _doubleClick; + static NotifyIcon() + { + VisibilityProperty.OverrideMetadata(typeof(NotifyIcon), new PropertyMetadata(Visibility.Visible, OnVisibilityChanged)); + } + + private static void OnVisibilityChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var ctl = (NotifyIcon) d; + var v = (Visibility)e.NewValue; + + if (v == Visibility.Visible) + { + if (ctl._currentSmallIconHandle == null) + { + ctl.OnIconChanged(); + } + ctl.UpdateIcon(true); + } + else if(ctl._currentSmallIconHandle !=null) + { + ctl.UpdateIcon(false); + } + } + public NotifyIcon() { _id = ++NextId; @@ -54,10 +76,12 @@ namespace HandyControl.Controls Loaded += (s, e) => { - CreateToolTip(); RegisterClass(); - OnIconChanged(); - UpdateIcon(true); + if (Visibility == Visibility.Visible) + { + OnIconChanged(); + UpdateIcon(true); + } }; if (Application.Current != null) Application.Current.Exit += (s, e) => Dispose(); @@ -87,6 +111,24 @@ namespace HandyControl.Controls ctl.OnIconChanged(); } + public static readonly DependencyProperty ContextContentProperty = DependencyProperty.Register( + "ContextContent", typeof(object), typeof(NotifyIcon), new PropertyMetadata(default(object))); + + public object ContextContent + { + get => GetValue(ContextContentProperty); + set => SetValue(ContextContentProperty, value); + } + + public static readonly DependencyProperty IsBlinkProperty = DependencyProperty.Register( + "IsBlink", typeof(bool), typeof(NotifyIcon), new PropertyMetadata(ValueBoxes.FalseBox)); + + public bool IsBlink + { + get => (bool) GetValue(IsBlinkProperty); + set => SetValue(IsBlinkProperty, value); + } + public ImageSource Icon { get => (ImageSource)GetValue(IconProperty); @@ -145,7 +187,7 @@ namespace HandyControl.Controls uTimeoutOrVersion = 10, uID = _id, dwInfoFlags = NativeMethods.NIF_TIP, - hIcon = _currentSmallIconHandle.DangerousGetHandle(), + hIcon = _currentSmallIconHandle.CriticalGetHandle(), szTip = Text }; @@ -182,11 +224,12 @@ namespace HandyControl.Controls hIcon = IntPtr.Zero, hCursor = IntPtr.Zero, hbrBackground = IntPtr.Zero, - lpszMenuName = string.Empty, + lpszMenuName = "", lpszClassName = _windowClassName }; UnsafeNativeMethods.RegisterClass(wndclass); + _wmTaskbarCreated = NativeMethods.RegisterWindowMessage("TaskbarCreated"); _messageWindowHandle = UnsafeNativeMethods.CreateWindowEx(0, _windowClassName, "", 0, 0, 0, 1, 1, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero); } @@ -194,13 +237,12 @@ namespace HandyControl.Controls { if (IsLoaded) { - if (msg == WmTaskbarcreated) + if (msg == _wmTaskbarCreated) { UpdateIcon(true); } else { - Console.WriteLine(lparam); switch (lparam.ToInt32()) { case NativeMethods.WM_LBUTTONDBLCLK: @@ -234,15 +276,6 @@ namespace HandyControl.Controls ShowContextMenu(); WmMouseUp(MouseButton.Right); break; - case 0x406: - OnBalloonTipShown(); - break; - case 0x407: - OnBalloonTipClosed(); - break; - case NativeMethods.NIN_BALLOONTIMEOUT: - OnBalloonTipClosed(); - break; } } } @@ -250,15 +283,6 @@ namespace HandyControl.Controls return UnsafeNativeMethods.DefWindowProc(hWnd, msg, wparam, lparam); } - private void OnBalloonTipShown() - { - var point = new POINT(); - UnsafeNativeMethods.GetCursorPos(point); - ShowToolTip(point); - } - - private void OnBalloonTipClosed() => _toolTip.IsOpen = false; - private void WmMouseDown(MouseButton button, int clicks) { if (clicks == 2) @@ -302,11 +326,38 @@ namespace HandyControl.Controls private void ShowContextMenu() { - if (ContextMenu != null) - { - var point = new POINT(); - UnsafeNativeMethods.GetCursorPos(point); + var point = new POINT(); + UnsafeNativeMethods.GetCursorPos(point); + if (ContextContent != null) + { + if (_contextContent == null) + { + _contextContent = new Popup + { + Placement = PlacementMode.AbsolutePoint, + AllowsTransparency = true, + StaysOpen = false + }; + } + + _contextContent.HorizontalOffset = point.x; + _contextContent.VerticalOffset = point.y; + _contextContent.Child = new ContentControl + { + Content = ContextContent + }; + _contextContent.IsOpen = true; + var handle = IntPtr.Zero; + var hwndSource = (HwndSource)PresentationSource.FromVisual(_contextContent.Child); + if (hwndSource != null) + { + handle = hwndSource.Handle; + } + UnsafeNativeMethods.SetForegroundWindow(handle); + } + else if (ContextMenu != null) + { ContextMenu.Placement = PlacementMode.AbsolutePoint; ContextMenu.HorizontalOffset = point.x; ContextMenu.VerticalOffset = point.y; @@ -322,28 +373,6 @@ namespace HandyControl.Controls } } - private void ShowToolTip(POINT point) - { - if (_toolTip != null) - { - _toolTip.HorizontalOffset = point.x; - _toolTip.VerticalOffset = point.x; - _toolTip.IsOpen = true; - } - } - - private void CreateToolTip() - { - _toolTip = new ToolTip - { - Placement = PlacementMode.AbsolutePoint, - Style = null - }; - - _toolTip.SetBinding(ContentControl.ContentProperty, new Binding(ToolTipProperty.Name) { Source = this }); - _toolTip.SetBinding(DataContextProperty, new Binding(DataContextProperty.Name) { Source = this }); - } - public static readonly RoutedEvent ClickEvent = EventManager.RegisterRoutedEvent("Click", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(NotifyIcon)); @@ -362,27 +391,7 @@ namespace HandyControl.Controls { add => AddHandler(MouseDoubleClickEvent, value); remove => RemoveHandler(MouseDoubleClickEvent, value); - } - - //public static readonly RoutedEvent BalloonTipShownEvent = - // EventManager.RegisterRoutedEvent("BalloonTipShown", RoutingStrategy.Bubble, - // typeof(RoutedEventHandler), typeof(NotifyIcon)); - - //public event RoutedEventHandler BalloonTipShown - //{ - // add => AddHandler(BalloonTipShownEvent, value); - // remove => RemoveHandler(BalloonTipShownEvent, value); - //} - - //public static readonly RoutedEvent BalloonTipClosedEvent = - // EventManager.RegisterRoutedEvent("BalloonTipClosed", RoutingStrategy.Bubble, - // typeof(RoutedEventHandler), typeof(NotifyIcon)); - - //public event RoutedEventHandler BalloonTipClosed - //{ - // add => AddHandler(BalloonTipClosedEvent, value); - // remove => RemoveHandler(BalloonTipClosedEvent, value); - //} + } private void Dispose(bool disposing) { diff --git a/HandyControl/HandyControl.csproj b/HandyControl/HandyControl.csproj index 31a32fbf..8a67d368 100644 --- a/HandyControl/HandyControl.csproj +++ b/HandyControl/HandyControl.csproj @@ -115,7 +115,7 @@ - + @@ -182,6 +182,8 @@ + + diff --git a/HandyControl/Interactivity/ControlCommands.cs b/HandyControl/Interactivity/ControlCommands.cs index 5a1471f8..efee5e51 100644 --- a/HandyControl/Interactivity/ControlCommands.cs +++ b/HandyControl/Interactivity/ControlCommands.cs @@ -146,5 +146,15 @@ namespace HandyControl.Interactivity /// 打开链接 /// public static RoutedCommand OpenLink { get; } = new RoutedCommand(nameof(OpenLink), typeof(ControlCommands)); + + /// + /// 关闭程序 + /// + public static ShutdownAppCommand ShutdownApp { get; } = new ShutdownAppCommand(); + + /// + /// 前置主窗口 + /// + public static PushMainWindow2TopCommand PushMainWindow2Top { get; } = new PushMainWindow2TopCommand(); } } \ No newline at end of file diff --git a/HandyControl/Interactivity/PushMainWindow2TopCommand.cs b/HandyControl/Interactivity/PushMainWindow2TopCommand.cs new file mode 100644 index 00000000..3bc1ca18 --- /dev/null +++ b/HandyControl/Interactivity/PushMainWindow2TopCommand.cs @@ -0,0 +1,28 @@ +using System; +using System.Windows; +using System.Windows.Input; +using System.Windows.Interop; +using HandyControl.Tools.Interop; + +namespace HandyControl.Interactivity +{ + public class PushMainWindow2TopCommand : ICommand + { + public bool CanExecute(object parameter) => true; + + public void Execute(object parameter) + { + if (Application.Current.MainWindow != null) + { + Application.Current.MainWindow.Show(); + var hwndSource = (HwndSource)PresentationSource.FromDependencyObject(Application.Current.MainWindow); + if (hwndSource != null) + { + UnsafeNativeMethods.SetForegroundWindow(hwndSource.Handle); + } + } + } + + public event EventHandler CanExecuteChanged; + } +} \ No newline at end of file diff --git a/HandyControl/Interactivity/ShutdownAppCommand.cs b/HandyControl/Interactivity/ShutdownAppCommand.cs new file mode 100644 index 00000000..85bc706c --- /dev/null +++ b/HandyControl/Interactivity/ShutdownAppCommand.cs @@ -0,0 +1,15 @@ +using System; +using System.Windows; +using System.Windows.Input; + +namespace HandyControl.Interactivity +{ + public class ShutdownAppCommand : ICommand + { + public bool CanExecute(object parameter) => true; + + public void Execute(object parameter) => Application.Current.Shutdown(); + + public event EventHandler CanExecuteChanged; + } +} \ No newline at end of file diff --git a/HandyControl/Themes/Styles/Base/ToggleButtonBaseStyle.xaml b/HandyControl/Themes/Styles/Base/ToggleButtonBaseStyle.xaml index 86a687e0..dc041caa 100644 --- a/HandyControl/Themes/Styles/Base/ToggleButtonBaseStyle.xaml +++ b/HandyControl/Themes/Styles/Base/ToggleButtonBaseStyle.xaml @@ -123,22 +123,22 @@ - + - - + + - + diff --git a/HandyControl/Tools/Interop/WNDCLASS.cs b/HandyControl/Tools/Interop/WNDCLASS.cs index 1fef93ed..33dd3903 100644 --- a/HandyControl/Tools/Interop/WNDCLASS.cs +++ b/HandyControl/Tools/Interop/WNDCLASS.cs @@ -11,13 +11,13 @@ namespace HandyControl.Tools.Interop { public int style; public WndProc lpfnWndProc; - public int cbClsExtra = 0; - public int cbWndExtra = 0; - public IntPtr hInstance = IntPtr.Zero; - public IntPtr hIcon = IntPtr.Zero; - public IntPtr hCursor = IntPtr.Zero; - public IntPtr hbrBackground = IntPtr.Zero; - public string lpszMenuName = null; - public string lpszClassName = null; + public int cbClsExtra; + public int cbWndExtra; + public IntPtr hInstance; + public IntPtr hIcon; + public IntPtr hCursor; + public IntPtr hbrBackground; + public string lpszMenuName; + public string lpszClassName; } } \ No newline at end of file diff --git a/HandyControlDemo/Data/GlobalData.cs b/HandyControlDemo/Data/GlobalData.cs index 9c0f9f69..92e46551 100644 --- a/HandyControlDemo/Data/GlobalData.cs +++ b/HandyControlDemo/Data/GlobalData.cs @@ -32,5 +32,7 @@ namespace HandyControlDemo.Data } public static AppConfig Config { get; set; } + + public static bool NotifyIconIsShow { get; set; } } } \ No newline at end of file diff --git a/HandyControlDemo/HandyControlDemo.csproj b/HandyControlDemo/HandyControlDemo.csproj index 6a8273dd..53deb9f4 100644 --- a/HandyControlDemo/HandyControlDemo.csproj +++ b/HandyControlDemo/HandyControlDemo.csproj @@ -362,6 +362,7 @@ + diff --git a/HandyControlDemo/MainWindow.xaml.cs b/HandyControlDemo/MainWindow.xaml.cs index bbd4e185..f4ff06dc 100644 --- a/HandyControlDemo/MainWindow.xaml.cs +++ b/HandyControlDemo/MainWindow.xaml.cs @@ -1,5 +1,7 @@ using System; +using System.ComponentModel; using HandyControl.Controls; +using HandyControlDemo.Data; namespace HandyControlDemo { @@ -23,5 +25,19 @@ namespace HandyControlDemo Growl.SetGrowlParent(GrowlPanel, false); } + + protected override void OnClosing(CancelEventArgs e) + { + if (GlobalData.NotifyIconIsShow) + { + MessageBox.Info(Properties.Langs.Lang.AppClosingTip, Properties.Langs.Lang.Tip); + Hide(); + e.Cancel = true; + } + else + { + base.OnClosing(e); + } + } } } \ No newline at end of file diff --git a/HandyControlDemo/Properties/Langs/Lang.Designer.cs b/HandyControlDemo/Properties/Langs/Lang.Designer.cs index 4c80a9c8..57bd671d 100644 --- a/HandyControlDemo/Properties/Langs/Lang.Designer.cs +++ b/HandyControlDemo/Properties/Langs/Lang.Designer.cs @@ -78,6 +78,15 @@ namespace HandyControlDemo.Properties.Langs { } } + /// + /// 查找类似 托盘图标已打开,将隐藏窗口而不是关闭程序 的本地化字符串。 + /// + public static string AppClosingTip { + get { + return ResourceManager.GetString("AppClosingTip", resourceCulture); + } + } + /// /// 查找类似 询问 的本地化字符串。 /// @@ -105,6 +114,15 @@ namespace HandyControlDemo.Properties.Langs { } } + /// + /// 查找类似 闪烁 的本地化字符串。 + /// + public static string Blink { + get { + return ResourceManager.GetString("Blink", resourceCulture); + } + } + /// /// 查找类似 博客 的本地化字符串。 /// @@ -429,6 +447,15 @@ namespace HandyControlDemo.Properties.Langs { } } + /// + /// 查找类似 退出 的本地化字符串。 + /// + public static string Exit { + get { + return ResourceManager.GetString("Exit", resourceCulture); + } + } + /// /// 查找类似 展开框 的本地化字符串。 /// @@ -798,6 +825,15 @@ namespace HandyControlDemo.Properties.Langs { } } + /// + /// 查找类似 打开面板 的本地化字符串。 + /// + public static string OpenPanel { + get { + return ResourceManager.GetString("OpenPanel", resourceCulture); + } + } + /// /// 查找类似 轮廓文本 的本地化字符串。 /// @@ -1176,6 +1212,15 @@ namespace HandyControlDemo.Properties.Langs { } } + /// + /// 查找类似 提示 的本地化字符串。 + /// + public static string Tip { + get { + return ResourceManager.GetString("Tip", resourceCulture); + } + } + /// /// 查找类似 标题 的本地化字符串。 /// @@ -1248,6 +1293,15 @@ namespace HandyControlDemo.Properties.Langs { } } + /// + /// 查找类似 试试关闭程序吧? 的本地化字符串。 + /// + public static string Try2CloseApp { + get { + return ResourceManager.GetString("Try2CloseApp", resourceCulture); + } + } + /// /// 查找类似 类型 的本地化字符串。 /// @@ -1275,6 +1329,15 @@ namespace HandyControlDemo.Properties.Langs { } } + /// + /// 查找类似 可见性 的本地化字符串。 + /// + public static string Visibility { + get { + return ResourceManager.GetString("Visibility", resourceCulture); + } + } + /// /// 查找类似 警告 的本地化字符串。 /// diff --git a/HandyControlDemo/Properties/Langs/Lang.en.resx b/HandyControlDemo/Properties/Langs/Lang.en.resx index 27a87236..ea35ef98 100644 --- a/HandyControlDemo/Properties/Langs/Lang.en.resx +++ b/HandyControlDemo/Properties/Langs/Lang.en.resx @@ -531,4 +531,25 @@ New window + + Visibility + + + Blink + + + Exit + + + Open Panel + + + The tray icon is open and will hide the window instead of closing the program + + + Prompt + + + Try to close app? + \ No newline at end of file diff --git a/HandyControlDemo/Properties/Langs/Lang.resx b/HandyControlDemo/Properties/Langs/Lang.resx index dc7baedd..f6cf4a2e 100644 --- a/HandyControlDemo/Properties/Langs/Lang.resx +++ b/HandyControlDemo/Properties/Langs/Lang.resx @@ -531,4 +531,25 @@ 新建窗口 + + 可见性 + + + 闪烁 + + + 退出 + + + 打开面板 + + + 托盘图标已打开,将隐藏窗口而不是关闭程序 + + + 提示 + + + 试试关闭程序吧? + \ No newline at end of file diff --git a/HandyControlDemo/UserControl/Controls/NotifyIconDemoCtl.xaml b/HandyControlDemo/UserControl/Controls/NotifyIconDemoCtl.xaml index daa6433f..3b701d5d 100644 --- a/HandyControlDemo/UserControl/Controls/NotifyIconDemoCtl.xaml +++ b/HandyControlDemo/UserControl/Controls/NotifyIconDemoCtl.xaml @@ -1,15 +1,29 @@  - + + + + + + + + + + + - - - + + diff --git a/HandyControlDemo/UserControl/Main/NoUserContent.xaml.cs b/HandyControlDemo/UserControl/Main/NoUserContent.xaml.cs index f765a014..acb4e8b4 100644 --- a/HandyControlDemo/UserControl/Main/NoUserContent.xaml.cs +++ b/HandyControlDemo/UserControl/Main/NoUserContent.xaml.cs @@ -1,5 +1,4 @@ -using System; -using System.Diagnostics; +using System.Diagnostics; using System.Reflection; using System.Windows; using System.Windows.Controls; @@ -32,7 +31,7 @@ namespace HandyControlDemo.UserControl GlobalData.Config.Lang = tag; GlobalData.Save(); Process.Start(Assembly.GetExecutingAssembly().Location); - Environment.Exit(0); + Application.Current.Shutdown(); return true; }); } diff --git a/HandyControlDemo/ViewModel/Controls/NotifyIconDemoViewModel.cs b/HandyControlDemo/ViewModel/Controls/NotifyIconDemoViewModel.cs new file mode 100644 index 00000000..d2e48bc5 --- /dev/null +++ b/HandyControlDemo/ViewModel/Controls/NotifyIconDemoViewModel.cs @@ -0,0 +1,20 @@ +using GalaSoft.MvvmLight; +using HandyControlDemo.Data; + +namespace HandyControlDemo.ViewModel +{ + public class NotifyIconDemoViewModel : ViewModelBase + { + private bool _isShow; + + public bool IsShow + { + get => _isShow; + set + { + Set(ref _isShow, value); + GlobalData.NotifyIconIsShow = value; + } + } + } +} \ No newline at end of file diff --git a/HandyControlDemo/ViewModel/ViewModelLocator.cs b/HandyControlDemo/ViewModel/ViewModelLocator.cs index 800ce4fe..1627d565 100644 --- a/HandyControlDemo/ViewModel/ViewModelLocator.cs +++ b/HandyControlDemo/ViewModel/ViewModelLocator.cs @@ -26,6 +26,7 @@ namespace HandyControlDemo.ViewModel SimpleIoc.Default.Register(); SimpleIoc.Default.Register(); SimpleIoc.Default.Register(); + SimpleIoc.Default.Register(); } public static ViewModelLocator Instance => new Lazy(() => @@ -57,6 +58,8 @@ namespace HandyControlDemo.ViewModel public SearchBarDemoViewModel SearchBarDemo => ServiceLocator.Current.GetInstance(); + public NotifyIconDemoViewModel NotifyIconDemo => ServiceLocator.Current.GetInstance(); + #endregion } } \ No newline at end of file