Merge pull request #54 from HandyOrg/add-NotifyIcon

Add notify icon
This commit is contained in:
NaBian authored and GitHub committed 2019-03-31 15:44:59 +08:00
commit c51936691a
47 files changed
+2055 -26

No files matched your search

+451
View File
@@ -0,0 +1,451 @@
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Threading;
using HandyControl.Data;
using HandyControl.Tools;
using HandyControl.Tools.Interop;
namespace HandyControl.Controls
{
public class NotifyIcon : FrameworkElement, IDisposable
{
private bool _added;
private readonly object _syncObj = new object();
private readonly int _id;
private static int NextId;
private ImageSource _icon;
private IconHandle _defaultLargeIconHandle;
private IconHandle _defaultSmallIconHandle;
private IconHandle _currentLargeIconHandle;
private IconHandle _currentSmallIconHandle;
private const int WmTrayMouseMessage = NativeMethods.WM_USER + 1024;
private string _windowClassName;
private int _wmTaskbarCreated;
private IntPtr _messageWindowHandle;
private readonly WndProc _callback;
private Popup _contextContent;
private bool _doubleClick;
private DispatcherTimer _dispatcherTimer;
private bool _isTransparent;
private bool _isDisposed;
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;
_callback = Callback;
Loaded += (s, e) =>
{
RegisterClass();
if (Visibility == Visibility.Visible)
{
OnIconChanged();
UpdateIcon(true);
}
};
if (Application.Current != null) Application.Current.Exit += (s, e) => Dispose();
}
~NotifyIcon()
{
Dispose(false);
}
public static readonly DependencyProperty TextProperty = DependencyProperty.Register(
"Text", typeof(string), typeof(NotifyIcon), new PropertyMetadata(default(string)));
public string Text
{
get => (string) GetValue(TextProperty);
set => SetValue(TextProperty, value);
}
public static readonly DependencyProperty IconProperty = DependencyProperty.Register(
"Icon", typeof(ImageSource), typeof(NotifyIcon), new PropertyMetadata(default(ImageSource), OnIconChanged));
private static void OnIconChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var ctl = (NotifyIcon)d;
ctl._icon = (ImageSource)e.NewValue;
ctl.OnIconChanged();
}
public ImageSource Icon
{
get => (ImageSource)GetValue(IconProperty);
set => SetValue(IconProperty, value);
}
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 BlinkIntervalProperty = DependencyProperty.Register(
"BlinkInterval", typeof(TimeSpan), typeof(NotifyIcon), new PropertyMetadata(TimeSpan.FromMilliseconds(500), OnBlinkIntervalChanged));
private static void OnBlinkIntervalChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var ctl = (NotifyIcon)d;
if (ctl._dispatcherTimer != null)
{
ctl._dispatcherTimer.Interval = (TimeSpan) e.NewValue;
}
}
public TimeSpan BlinkInterval
{
get => (TimeSpan) GetValue(BlinkIntervalProperty);
set => SetValue(BlinkIntervalProperty, value);
}
public static readonly DependencyProperty IsBlinkProperty = DependencyProperty.Register(
"IsBlink", typeof(bool), typeof(NotifyIcon), new PropertyMetadata(ValueBoxes.FalseBox, OnIsBlinkChanged));
private static void OnIsBlinkChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var ctl = (NotifyIcon)d;
if (ctl.Visibility != Visibility.Visible) return;
if ((bool) e.NewValue)
{
if (ctl._dispatcherTimer == null)
{
ctl._dispatcherTimer = new DispatcherTimer
{
Interval = ctl.BlinkInterval
};
ctl._dispatcherTimer.Tick += ctl.DispatcherTimer_Tick;
}
ctl._dispatcherTimer.Start();
}
else
{
ctl._dispatcherTimer?.Stop();
ctl._dispatcherTimer = null;
ctl.UpdateIcon(true);
}
}
private void DispatcherTimer_Tick(object sender, EventArgs e)
{
if (Visibility != Visibility.Visible || _currentSmallIconHandle == null) return;
UpdateIcon(true, !_isTransparent);
}
public bool IsBlink
{
get => (bool) GetValue(IsBlinkProperty);
set => SetValue(IsBlinkProperty, value);
}
private void OnIconChanged()
{
IconHandle largeIconHandle;
IconHandle smallIconHandle;
if (_icon != null)
{
IconHelper.GetIconHandlesFromImageSource(_icon, out largeIconHandle, out smallIconHandle);
}
else
{
if (_defaultLargeIconHandle == null && _defaultSmallIconHandle == null)
{
IconHelper.GetDefaultIconHandles(out largeIconHandle, out smallIconHandle);
_defaultLargeIconHandle = largeIconHandle;
_defaultSmallIconHandle = smallIconHandle;
}
else
{
largeIconHandle = _defaultLargeIconHandle;
smallIconHandle = _defaultSmallIconHandle;
}
}
if (_currentLargeIconHandle != null && _currentLargeIconHandle != _defaultLargeIconHandle)
{
_currentLargeIconHandle.Dispose();
}
if (_currentSmallIconHandle != null && _currentSmallIconHandle != _defaultSmallIconHandle)
{
_currentSmallIconHandle.Dispose();
}
_currentLargeIconHandle = largeIconHandle;
_currentSmallIconHandle = smallIconHandle;
}
private void UpdateIcon(bool showIconInTray, bool isTransparent = false)
{
lock (_syncObj)
{
if (_currentSmallIconHandle == null || DesignerHelper.IsInDesignMode) return;
_isTransparent = isTransparent;
var data = new NOTIFYICONDATA
{
uCallbackMessage = WmTrayMouseMessage,
uFlags = NativeMethods.NIF_MESSAGE | NativeMethods.NIF_ICON | NativeMethods.NIF_TIP,
hWnd = _messageWindowHandle,
uID = _id,
dwInfoFlags = NativeMethods.NIF_TIP,
hIcon = isTransparent ? IntPtr.Zero : _currentSmallIconHandle.CriticalGetHandle(),
szTip = Text
};
if (showIconInTray)
{
if (!_added)
{
UnsafeNativeMethods.Shell_NotifyIcon(NativeMethods.NIM_ADD, data);
_added = true;
}
else
{
UnsafeNativeMethods.Shell_NotifyIcon(NativeMethods.NIM_MODIFY, data);
}
}
else if (_added)
{
UnsafeNativeMethods.Shell_NotifyIcon(NativeMethods.NIM_DELETE, data);
_added = false;
}
}
}
private void RegisterClass()
{
_windowClassName = $"HandyControl.Controls.NotifyIcon{Guid.NewGuid()}";
var wndclass = new WNDCLASS
{
style = 0,
lpfnWndProc = _callback,
cbClsExtra = 0,
cbWndExtra = 0,
hInstance = IntPtr.Zero,
hIcon = IntPtr.Zero,
hCursor = IntPtr.Zero,
hbrBackground = IntPtr.Zero,
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);
}
private IntPtr Callback(IntPtr hWnd, int msg, IntPtr wparam, IntPtr lparam)
{
if (IsLoaded)
{
if (msg == _wmTaskbarCreated)
{
UpdateIcon(true);
}
else
{
switch (lparam.ToInt32())
{
case NativeMethods.WM_LBUTTONDBLCLK:
WmMouseDown(MouseButton.Left, 2);
break;
case NativeMethods.WM_LBUTTONUP:
WmMouseUp(MouseButton.Left);
break;
case NativeMethods.WM_RBUTTONUP:
ShowContextMenu();
WmMouseUp(MouseButton.Right);
break;
}
}
}
return UnsafeNativeMethods.DefWindowProc(hWnd, msg, wparam, lparam);
}
private void WmMouseDown(MouseButton button, int clicks)
{
if (clicks == 2)
{
RaiseEvent(new MouseButtonEventArgs(Mouse.PrimaryDevice, Environment.TickCount, button)
{
RoutedEvent = MouseDoubleClickEvent
});
_doubleClick = true;
}
}
private void WmMouseUp(MouseButton button)
{
if (!_doubleClick && button == MouseButton.Left)
{
RaiseEvent(new MouseButtonEventArgs(Mouse.PrimaryDevice, Environment.TickCount, button)
{
RoutedEvent = ClickEvent
});
}
_doubleClick = false;
}
private void ShowContextMenu()
{
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;
ContextMenu.IsOpen = true;
var handle = IntPtr.Zero;
var hwndSource = (HwndSource)PresentationSource.FromVisual(ContextMenu);
if (hwndSource != null)
{
handle = hwndSource.Handle;
}
UnsafeNativeMethods.SetForegroundWindow(handle);
}
}
public static readonly RoutedEvent ClickEvent =
EventManager.RegisterRoutedEvent("Click", RoutingStrategy.Bubble,
typeof(RoutedEventHandler), typeof(NotifyIcon));
public event RoutedEventHandler Click
{
add => AddHandler(ClickEvent, value);
remove => RemoveHandler(ClickEvent, value);
}
public static readonly RoutedEvent MouseDoubleClickEvent =
EventManager.RegisterRoutedEvent("MouseDoubleClick", RoutingStrategy.Bubble,
typeof(RoutedEventHandler), typeof(NotifyIcon));
public event RoutedEventHandler MouseDoubleClick
{
add => AddHandler(MouseDoubleClickEvent, value);
remove => RemoveHandler(MouseDoubleClickEvent, value);
}
private void Dispose(bool disposing)
{
if (_isDisposed) return;
if (disposing)
{
if (_dispatcherTimer != null && IsBlink)
{
_dispatcherTimer.Stop();
}
UpdateIcon(false);
_defaultLargeIconHandle?.Dispose();
_defaultSmallIconHandle?.Dispose();
_currentLargeIconHandle?.Dispose();
_currentSmallIconHandle?.Dispose();
}
_isDisposed = true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
public void CloseContextControl()
{
if (_contextContent != null)
{
_contextContent.IsOpen = false;
}
else if (ContextMenu != null)
{
ContextMenu.IsOpen = false;
}
}
}
}
+20
View File
@@ -115,6 +115,7 @@
<Compile Include="Controls\Other\ChatBubble.cs" />
<Compile Include="Controls\Other\Dialog.cs" />
<Compile Include="Controls\Other\GotoTop.cs" />
<Compile Include="Controls\Other\NotifyIcon.cs" />
<Compile Include="Controls\Other\Gravatar.cs" />
<Compile Include="Controls\Panel\SimplePanel.cs" />
<Compile Include="Controls\ProgressBar\WaveProgressBar.cs" />
@@ -179,6 +180,8 @@
<Compile Include="Data\MessageBoxInfo.cs" />
<Compile Include="Data\ResourceToken.cs" />
<Compile Include="Data\SystemVersionInfo.cs" />
<Compile Include="Interactivity\PushMainWindow2TopCommand.cs" />
<Compile Include="Interactivity\ShutdownAppCommand.cs" />
<Compile Include="Tools\Converter\Boolean2StrConverter.cs" />
<Compile Include="Tools\Converter\BorderClipConverter.cs" />
<Compile Include="Tools\Converter\BorderCircularConverter.cs" />
@@ -223,8 +226,12 @@
<Compile Include="Expression\Media\UnitType.cs" />
<Compile Include="Expression\Shapes\Arc.cs" />
<Compile Include="Expression\Shapes\PrimitiveShape.cs" />
<Compile Include="Tools\Helper\CreateParams.cs" />
<Compile Include="Tools\Helper\DesignerHelper.cs" />
<Compile Include="Tools\Helper\ExternDllHelper.cs" />
<Compile Include="Tools\Helper\IconHelper.cs" />
<Compile Include="Tools\Interop\NOTIFYICONDATA.cs" />
<Compile Include="Tools\Helper\SecurityHelper.cs" />
<Compile Include="Tools\Helper\SingleOpenHelper.cs" />
<Compile Include="Controls\TabControl\TabControl.cs" />
<Compile Include="Controls\TabControl\TabItem.cs" />
@@ -304,6 +311,19 @@
<Compile Include="Tools\Converter\TreeViewItemMarginConverter.cs" />
<Compile Include="Tools\Extension\ColorExtension.cs" />
<Compile Include="Tools\Extension\UIElementExtension.cs" />
<Compile Include="Tools\Interop\BitmapHandle.cs" />
<Compile Include="Tools\Interop\BITMAPINFO.cs" />
<Compile Include="Tools\Interop\CommonHandles.cs" />
<Compile Include="Tools\Interop\ExternDll.cs" />
<Compile Include="Tools\Interop\HandleCollector.cs" />
<Compile Include="Tools\Interop\IconHandle.cs" />
<Compile Include="Tools\Interop\ICONINFO.cs" />
<Compile Include="Tools\Interop\NativeMethods.cs" />
<Compile Include="Tools\Interop\NativeValues.cs" />
<Compile Include="Tools\Interop\SafeFileMappingHandle.cs" />
<Compile Include="Tools\Interop\UnsafeNativeMethods.cs" />
<Compile Include="Tools\Interop\WNDCLASS.cs" />
<Compile Include="Tools\Interop\WpfSafeHandle.cs" />
<Compile Include="Tools\MouseDragElementBehaviorEx.cs" />
<Compile Include="Tools\StyleSelector\RadioGroupItemStyleSelector.cs" />
<Compile Include="Tools\RegularJudgment.cs" />
@@ -146,5 +146,15 @@ namespace HandyControl.Interactivity
/// 打开链接
/// </summary>
public static RoutedCommand OpenLink { get; } = new RoutedCommand(nameof(OpenLink), typeof(ControlCommands));
/// <summary>
/// 关闭程序
/// </summary>
public static ShutdownAppCommand ShutdownApp { get; } = new ShutdownAppCommand();
/// <summary>
/// 前置主窗口
/// </summary>
public static PushMainWindow2TopCommand PushMainWindow2Top { get; } = new PushMainWindow2TopCommand();
}
}
+1 -1
View File
@@ -1,4 +1,4 @@
/*refrence from https://github.com/lbugnion/mvvmlight*/
// reference from https://github.com/lbugnion/mvvmlight
//The MIT License(MIT)
@@ -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.Visibility != Visibility.Visible)
{
Application.Current.MainWindow.Show();
var hwndSource = (HwndSource)PresentationSource.FromDependencyObject(Application.Current.MainWindow);
if (hwndSource != null)
{
UnsafeNativeMethods.SetForegroundWindow(hwndSource.Handle);
}
}
}
public event EventHandler CanExecuteChanged;
}
}
@@ -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;
}
}
@@ -123,22 +123,22 @@
</Grid.ColumnDefinitions>
<Border Height="22" Width="36" x:Name="backBorder" Background="{DynamicResource BorderBrush}" CornerRadius="11"/>
<Border Grid.Column="0" Height="22" Width="36" Opacity="0" x:Name="lightBorder" Background="{DynamicResource PrimaryBrush}" CornerRadius="11" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}"/>
<Border Grid.Column="0" Margin="1" Background="{TemplateBinding Background}" CornerRadius="11" Name="innerBorder" RenderTransformOrigin=".8,0.5">
<Border Grid.Column="0" Height="20" Margin="1" Background="{TemplateBinding Background}" CornerRadius="11" Name="innerBorder" RenderTransformOrigin=".8,0.5">
<Border.RenderTransform>
<TransformGroup>
<ScaleTransform/>
</TransformGroup>
</Border.RenderTransform>
</Border>
<Canvas Grid.Column="0" Margin="1">
<Border CornerRadius="10" Name="thumb" Effect="{StaticResource EffectShadow2}" Background="White" Width="20" Height="20" RenderTransformOrigin="0.5,0.5">
<controls:SimplePanel Grid.Column="0" Margin="1">
<Border HorizontalAlignment="Left" CornerRadius="10" Name="thumb" Effect="{StaticResource EffectShadow2}" Background="White" Width="20" Height="20" RenderTransformOrigin="0.5,0.5">
<Border.RenderTransform>
<TransformGroup>
<TranslateTransform/>
</TransformGroup>
</Border.RenderTransform>
</Border>
</Canvas>
</controls:SimplePanel>
<ContentPresenter x:Name="contentPresenter" Visibility="Collapsed" Grid.Column="1" Focusable="False" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
</Grid>
<ControlTemplate.Triggers>
+54
View File
@@ -0,0 +1,54 @@
using System;
using System.Text;
namespace HandyControl.Tools
{
internal class CreateParams
{
public string ClassName { get; set; }
public string Caption { get; set; }
public int Style { get; set; }
public int ExStyle{ get; set; }
public int ClassStyle { get; set; }
public int X { get; set; }
public int Y { get; set; }
public int Width { get; set; }
public int Height { get; set; }
public IntPtr Parent { get; set; }
public object Param { get; set; }
public override string ToString()
{
var sb = new StringBuilder(64);
sb.Append("CreateParams {'");
sb.Append(ClassName);
sb.Append("', '");
sb.Append(Caption);
sb.Append("', 0x");
sb.Append(Convert.ToString(Style, 16));
sb.Append(", 0x");
sb.Append(Convert.ToString(ExStyle, 16));
sb.Append(", {");
sb.Append(X);
sb.Append(", ");
sb.Append(Y);
sb.Append(", ");
sb.Append(Width);
sb.Append(", ");
sb.Append(Height);
sb.Append("}");
sb.Append("}");
return sb.ToString();
}
}
}
+1 -8
View File
@@ -10,13 +10,13 @@ using System.Threading;
namespace HandyControl.Tools
{
[SuppressMessage("ReSharper", "InconsistentNaming")]
internal class ExternDllHelper
{
private const string Gdi32 = "gdi32.dll";
private const string User32 = "user32.dll";
// ReSharper disable once InconsistentNaming
public const int E_FAIL = unchecked((int) 0x80004005);
[StructLayout(LayoutKind.Sequential)]
@@ -70,7 +70,6 @@ namespace HandyControl.Tools
[In]
int len);
int Write(
[In]
IntPtr buf,
@@ -84,7 +83,6 @@ namespace HandyControl.Tools
[In]
int dwOrigin);
void SetSize(
[In, MarshalAs(UnmanagedType.I8)]
long libNewSize);
@@ -98,15 +96,12 @@ namespace HandyControl.Tools
[Out, MarshalAs(UnmanagedType.LPArray)]
long[] pcbRead);
void Commit(
[In]
int grfCommitFlags);
void Revert();
void LockRegion(
[In, MarshalAs(UnmanagedType.I8)]
long libOffset,
@@ -115,7 +110,6 @@ namespace HandyControl.Tools
[In]
int dwLockType);
void UnlockRegion(
[In, MarshalAs(UnmanagedType.I8)]
long libOffset,
@@ -124,7 +118,6 @@ namespace HandyControl.Tools
[In]
int dwLockType);
void Stat(
[In]
IntPtr pStatstg,
+294
View File
@@ -0,0 +1,294 @@
using System;
using System.Collections.ObjectModel;
using System.Runtime.InteropServices;
using System.Security;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using HandyControl.Tools.Interop;
namespace HandyControl.Tools
{
internal static class IconHelper
{
private static Size SmallIconSize;
private static Size IconSize;
private static int SystemBitDepth;
[SecurityCritical, SecuritySafeCritical]
public static void GetIconHandlesFromImageSource(ImageSource image, out IconHandle largeIconHandle, out IconHandle smallIconHandle)
{
EnsureSystemMetrics();
largeIconHandle = CreateIconHandleFromImageSource(image, IconSize);
smallIconHandle = CreateIconHandleFromImageSource(image, SmallIconSize);
}
[SecurityCritical]
public static IconHandle CreateIconHandleFromImageSource(ImageSource image, Size size)
{
EnsureSystemMetrics();
var asGoodAsItGets = false;
var bf = image as BitmapFrame;
if (bf?.Decoder?.Frames != null)
{
bf = GetBestMatch(bf.Decoder.Frames, size);
asGoodAsItGets = bf.Decoder is IconBitmapDecoder || bf.PixelWidth == (int)size.Width && bf.PixelHeight == (int)size.Height;
image = bf;
}
if (!asGoodAsItGets)
{
bf = BitmapFrame.Create(GenerateBitmapSource(image, size));
}
return CreateIconHandleFromBitmapFrame(bf);
}
[SecurityCritical]
private static IconHandle CreateIconHandleFromBitmapFrame(BitmapFrame sourceBitmapFrame)
{
BitmapSource bitmapSource = sourceBitmapFrame;
if (bitmapSource.Format != PixelFormats.Bgra32 && bitmapSource.Format != PixelFormats.Pbgra32)
{
bitmapSource = new FormatConvertedBitmap(bitmapSource, PixelFormats.Bgra32, null, 0.0);
}
var w = bitmapSource.PixelWidth;
var h = bitmapSource.PixelHeight;
var bpp = bitmapSource.Format.BitsPerPixel;
var stride = (bpp * w + 31) / 32 * 4;
var sizeCopyPixels = stride * h;
var xor = new byte[sizeCopyPixels];
bitmapSource.CopyPixels(xor, stride, 0);
return CreateIconCursor(xor, w, h, 0, 0, true);
}
[SecurityCritical]
internal static IconHandle CreateIconCursor(byte[] colorArray, int width, int height, int xHotspot, int yHotspot, bool isIcon)
{
BitmapHandle colorBitmap = null;
BitmapHandle maskBitmap = null;
try
{
var bi = new BITMAPINFO(width, -height, 32)
{
bmiHeader_biCompression = NativeMethods.BI_RGB
};
var bits = IntPtr.Zero;
colorBitmap = UnsafeNativeMethods.CreateDIBSection(new HandleRef(null, IntPtr.Zero), ref bi, NativeMethods.DIB_RGB_COLORS, ref bits, null, 0);
if (colorBitmap.IsInvalid || bits == IntPtr.Zero)
{
return IconHandle.GetInvalidIcon();
}
Marshal.Copy(colorArray, 0, bits, colorArray.Length);
var maskArray = GenerateMaskArray(width, height, colorArray);
maskBitmap = UnsafeNativeMethods.CreateBitmap(width, height, 1, 1, maskArray);
if (maskBitmap.IsInvalid)
{
return IconHandle.GetInvalidIcon();
}
var iconInfo = new ICONINFO
{
fIcon = isIcon,
xHotspot = xHotspot,
yHotspot = yHotspot,
hbmMask = maskBitmap,
hbmColor = colorBitmap
};
return UnsafeNativeMethods.CreateIconIndirect(iconInfo);
}
finally
{
colorBitmap?.Dispose();
maskBitmap?.Dispose();
}
}
private static byte[] GenerateMaskArray(int width, int height, byte[] colorArray)
{
var nCount = width * height;
var bytesPerScanLine = AlignToBytes(width, 2) / 8;
var bitsMask = new byte[bytesPerScanLine * height];
for (var i = 0; i < nCount; i++)
{
var hPos = i % width;
var vPos = i / width;
var byteIndex = hPos / 8;
var offsetBit = (byte)(0x80 >> (hPos % 8));
if (colorArray[i * 4 + 3] == 0x00)
{
bitsMask[byteIndex + bytesPerScanLine * vPos] |= offsetBit;
}
else
{
bitsMask[byteIndex + bytesPerScanLine * vPos] &= (byte)~offsetBit;
}
if (hPos == width - 1 && width == 8)
{
bitsMask[1 + bytesPerScanLine * vPos] = 0xff;
}
}
return bitsMask;
}
internal static int AlignToBytes(double original, int nBytesCount)
{
var nBitsCount = 8 << (nBytesCount - 1);
return ((int)Math.Ceiling(original) + (nBitsCount - 1)) / nBitsCount * nBitsCount;
}
private static BitmapSource GenerateBitmapSource(ImageSource img, Size renderSize)
{
var drawingDimensions = new Rect(0, 0, renderSize.Width, renderSize.Height);
var renderRatio = renderSize.Width / renderSize.Height;
var aspectRatio = img.Width / img.Height;
if (img.Width <= renderSize.Width && img.Height <= renderSize.Height)
{
drawingDimensions = new Rect((renderSize.Width - img.Width) / 2, (renderSize.Height - img.Height) / 2, img.Width, img.Height);
}
else if (renderRatio > aspectRatio)
{
var scaledRenderWidth = (img.Width / img.Height) * renderSize.Width;
drawingDimensions = new Rect((renderSize.Width - scaledRenderWidth) / 2, 0, scaledRenderWidth, renderSize.Height);
}
else if (renderRatio < aspectRatio)
{
var scaledRenderHeight = img.Height / img.Width * renderSize.Height;
drawingDimensions = new Rect(0, (renderSize.Height - scaledRenderHeight) / 2, renderSize.Width, scaledRenderHeight);
}
var dv = new DrawingVisual();
var dc = dv.RenderOpen();
dc.DrawImage(img, drawingDimensions);
dc.Close();
var bmp = new RenderTargetBitmap((int)renderSize.Width, (int)renderSize.Height, 96, 96, PixelFormats.Pbgra32);
bmp.Render(dv);
return bmp;
}
private static BitmapFrame GetBestMatch(ReadOnlyCollection<BitmapFrame> frames, Size size)
{
var bestScore = int.MaxValue;
var bestBpp = 0;
var bestIndex = 0;
var isBitmapIconDecoder = frames[0].Decoder is IconBitmapDecoder;
for (int i = 0; i < frames.Count && bestScore != 0; ++i)
{
var currentIconBitDepth = isBitmapIconDecoder ? frames[i].Thumbnail.Format.BitsPerPixel : frames[i].Format.BitsPerPixel;
if (currentIconBitDepth == 0)
{
currentIconBitDepth = 8;
}
var score = MatchImage(frames[i], size, currentIconBitDepth);
if (score < bestScore)
{
bestIndex = i;
bestBpp = currentIconBitDepth;
bestScore = score;
}
else if (score == bestScore)
{
if (bestBpp < currentIconBitDepth)
{
bestIndex = i;
bestBpp = currentIconBitDepth;
}
}
}
return frames[bestIndex];
}
private static int MatchImage(BitmapFrame frame, Size size, int bpp)
{
var score = 2 * MyAbs(bpp, SystemBitDepth, false) +
MyAbs(frame.PixelWidth, (int)size.Width, true) +
MyAbs(frame.PixelHeight, (int)size.Height, true);
return score;
}
private static int MyAbs(int valueHave, int valueWant, bool fPunish)
{
var diff = (valueHave - valueWant);
if (diff < 0)
{
diff = (fPunish ? -2 : -1) * diff;
}
return diff;
}
[SecurityCritical, SecuritySafeCritical]
private static void EnsureSystemMetrics()
{
if (SystemBitDepth == 0)
{
var hdcDesktop = new HandleRef(null, UnsafeNativeMethods.GetDC(new HandleRef()));
try
{
var sysBitDepth = UnsafeNativeMethods.GetDeviceCaps(hdcDesktop, NativeMethods.BITSPIXEL);
sysBitDepth *= UnsafeNativeMethods.GetDeviceCaps(hdcDesktop, NativeMethods.PLANES);
if (sysBitDepth == 8)
{
sysBitDepth = 4;
}
var cxSmallIcon = UnsafeNativeMethods.GetSystemMetrics(SM.CXSMICON);
var cySmallIcon = UnsafeNativeMethods.GetSystemMetrics(SM.CYSMICON);
var cxIcon = UnsafeNativeMethods.GetSystemMetrics(SM.CXICON);
var cyIcon = UnsafeNativeMethods.GetSystemMetrics(SM.CYICON);
SmallIconSize = new Size(cxSmallIcon, cySmallIcon);
IconSize = new Size(cxIcon, cyIcon);
SystemBitDepth = sysBitDepth;
}
finally
{
UnsafeNativeMethods.ReleaseDC(new HandleRef(), hdcDesktop);
}
}
}
[SecurityCritical, SecuritySafeCritical]
public static void GetDefaultIconHandles(out IconHandle largeIconHandle, out IconHandle smallIconHandle)
{
largeIconHandle = null;
smallIconHandle = null;
SecurityHelper.DemandUIWindowPermission();
var iconModuleFile = UnsafeNativeMethods.GetModuleFileName(new HandleRef());
UnsafeNativeMethods.ExtractIconEx(iconModuleFile, 0, out largeIconHandle, out smallIconHandle, 1);
}
}
}
@@ -0,0 +1,22 @@
using System.Diagnostics.CodeAnalysis;
using System.Security;
using System.Security.Permissions;
namespace HandyControl.Tools
{
[SuppressMessage("ReSharper", "InconsistentNaming")]
internal class SecurityHelper
{
private static UIPermission _allWindowsUIPermission;
[SecurityCritical]
internal static void DemandUIWindowPermission()
{
if (_allWindowsUIPermission == null)
{
_allWindowsUIPermission = new UIPermission(UIPermissionWindow.AllWindows);
}
_allWindowsUIPermission.Demand();
}
}
}
+54
View File
@@ -0,0 +1,54 @@
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
using System.Security;
namespace HandyControl.Tools.Interop
{
[StructLayout(LayoutKind.Sequential, Pack = 2)]
[SuppressMessage("ReSharper", "InconsistentNaming")]
internal struct BITMAPINFO
{
public int bmiHeader_biSize;
public int bmiHeader_biWidth;
public int bmiHeader_biHeight;
public short bmiHeader_biPlanes;
public short bmiHeader_biBitCount;
public int bmiHeader_biCompression;
public int bmiHeader_biSizeImage;
public int bmiHeader_biXPelsPerMeter;
public int bmiHeader_biYPelsPerMeter;
public int bmiHeader_biClrUsed;
public int bmiHeader_biClrImportant;
public BITMAPINFO(int width, int height, short bpp)
{
bmiHeader_biSize = SizeOf();
bmiHeader_biWidth = width;
bmiHeader_biHeight = height;
bmiHeader_biPlanes = 1;
bmiHeader_biBitCount = bpp;
bmiHeader_biCompression = 0;
bmiHeader_biSizeImage = 0;
bmiHeader_biXPelsPerMeter = 0;
bmiHeader_biYPelsPerMeter = 0;
bmiHeader_biClrUsed = 0;
bmiHeader_biClrImportant = 0;
}
[SecuritySafeCritical]
private static int SizeOf()
{
return Marshal.SizeOf(typeof(BITMAPINFO));
}
}
}
@@ -0,0 +1,45 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.ConstrainedExecution;
using System.Runtime.InteropServices;
using System.Security;
namespace HandyControl.Tools.Interop
{
[SuppressMessage("ReSharper", "UnusedMember.Local")]
internal sealed class BitmapHandle : WpfSafeHandle
{
[SecurityCritical]
private BitmapHandle() : this(true)
{
//请不要删除此构造函数,否则当使用自定义ico文件时会报错
}
[SecurityCritical]
private BitmapHandle(bool ownsHandle) : base(ownsHandle, CommonHandles.GDI)
{
}
[SecurityCritical]
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
protected override bool ReleaseHandle()
{
return UnsafeNativeMethods.DeleteObject(handle);
}
[SecurityCritical]
internal HandleRef MakeHandleRef(object obj)
{
return new HandleRef(obj, handle);
}
[SecurityCritical]
internal static BitmapHandle CreateFromHandle(IntPtr hbitmap, bool ownsHandle = true)
{
return new BitmapHandle(ownsHandle)
{
handle = hbitmap,
};
}
}
}
@@ -0,0 +1,16 @@
using System.Diagnostics.CodeAnalysis;
namespace HandyControl.Tools.Interop
{
[SuppressMessage("ReSharper", "InconsistentNaming")]
internal static class CommonHandles
{
public static readonly int Icon = HandleCollector.RegisterType(nameof(Icon), 20, 500);
public static readonly int HDC = HandleCollector.RegisterType(nameof(HDC), 100, 2);
public static readonly int GDI = HandleCollector.RegisterType(nameof(GDI), 50, 500);
public static readonly int Kernel = HandleCollector.RegisterType(nameof(Kernel), 0, 1000);
}
}
+11
View File
@@ -0,0 +1,11 @@
namespace HandyControl.Tools.Interop
{
internal static class ExternDll
{
public const string
User32 = "user32.dll",
Gdi32 = "gdi32.dll",
Kernel32 = "kernel32.dll",
Shell32 = "shell32.dll";
}
}
@@ -0,0 +1,132 @@
// reference from https://referencesource.microsoft.com/#WindowsBase/Shared/MS/Win32/HandleCollector.cs,d0f99220d8e1b708
using System;
using System.Runtime.InteropServices;
namespace HandyControl.Tools.Interop
{
internal static class HandleCollector
{
private static HandleType[] HandleTypes;
private static int HandleTypeCount;
private static readonly object HandleMutex = new object();
internal static IntPtr Add(IntPtr handle, int type)
{
HandleTypes[type - 1].Add();
return handle;
}
[System.Security.SecuritySafeCritical]
internal static SafeHandle Add(SafeHandle handle, int type)
{
HandleTypes[type - 1].Add();
return handle;
}
internal static void Add(int type)
{
HandleTypes[type - 1].Add();
}
internal static int RegisterType(string typeName, int expense, int initialThreshold)
{
lock (HandleMutex)
{
if (HandleTypeCount == 0 || HandleTypeCount == HandleTypes.Length)
{
HandleType[] newTypes = new HandleType[HandleTypeCount + 10];
if (HandleTypes != null)
{
Array.Copy(HandleTypes, 0, newTypes, 0, HandleTypeCount);
}
HandleTypes = newTypes;
}
HandleTypes[HandleTypeCount++] = new HandleType(expense, initialThreshold);
return HandleTypeCount;
}
}
internal static IntPtr Remove(IntPtr handle, int type)
{
HandleTypes[type - 1].Remove();
return handle;
}
[System.Security.SecuritySafeCritical]
internal static SafeHandle Remove(SafeHandle handle, int type)
{
HandleTypes[type - 1].Remove();
return handle;
}
internal static void Remove(int type)
{
HandleTypes[type - 1].Remove();
}
private class HandleType
{
private readonly int _initialThreshHold;
private int _threshHold;
private int _handleCount;
private readonly int _deltaPercent;
internal HandleType(int expense, int initialThreshHold)
{
_initialThreshHold = initialThreshHold;
_threshHold = initialThreshHold;
_deltaPercent = 100 - expense;
}
internal void Add()
{
lock (this)
{
_handleCount++;
var performCollect = NeedCollection();
if (!performCollect)
{
return;
}
}
GC.Collect();
var sleep = (100 - _deltaPercent) / 4;
System.Threading.Thread.Sleep(sleep);
}
private bool NeedCollection()
{
if (_handleCount > _threshHold)
{
_threshHold = _handleCount + _handleCount * _deltaPercent / 100;
return true;
}
var oldThreshHold = 100 * _threshHold / (100 + _deltaPercent);
if (oldThreshHold >= _initialThreshHold && _handleCount < (int)(oldThreshHold * .9F))
{
_threshHold = oldThreshHold;
}
return false;
}
internal void Remove()
{
lock (this)
{
_handleCount--;
_handleCount = Math.Max(0, _handleCount);
}
}
}
}
}
+16
View File
@@ -0,0 +1,16 @@
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
namespace HandyControl.Tools.Interop
{
[StructLayout(LayoutKind.Sequential)]
[SuppressMessage("ReSharper", "InconsistentNaming")]
internal class ICONINFO
{
public bool fIcon = false;
public int xHotspot = 0;
public int yHotspot = 0;
public BitmapHandle hbmMask = null;
public BitmapHandle hbmColor = null;
}
}
+33
View File
@@ -0,0 +1,33 @@
using System;
using System.Runtime.ConstrainedExecution;
using System.Security;
namespace HandyControl.Tools.Interop
{
internal sealed class IconHandle : WpfSafeHandle
{
[SecurityCritical]
private IconHandle() : base(true, CommonHandles.Icon)
{
}
[SecurityCritical]
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
protected override bool ReleaseHandle()
{
return UnsafeNativeMethods.DestroyIcon(handle);
}
[SecurityCritical, SecuritySafeCritical]
internal static IconHandle GetInvalidIcon()
{
return new IconHandle();
}
[SecurityCritical]
internal IntPtr CriticalGetHandle()
{
return handle;
}
}
}
@@ -0,0 +1,28 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
namespace HandyControl.Tools.Interop
{
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
[SuppressMessage("ReSharper", "InconsistentNaming")]
public class NOTIFYICONDATA
{
public int cbSize = Marshal.SizeOf(typeof(NOTIFYICONDATA));
public IntPtr hWnd;
public int uID;
public int uFlags;
public int uCallbackMessage;
public IntPtr hIcon;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
public string szTip = string.Empty;
public int dwState = 0x01;
public int dwStateMask = 0x01;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
public string szInfo = string.Empty;
public int uTimeoutOrVersion;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]
public string szInfoTitle = string.Empty;
public int dwInfoFlags;
}
}
@@ -0,0 +1,50 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
namespace HandyControl.Tools.Interop
{
public delegate IntPtr WndProc(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);
[SuppressMessage("ReSharper", "InconsistentNaming")]
[SuppressMessage("ReSharper", "IdentifierTypo")]
internal class NativeMethods
{
public const int
WS_CHILD = 0x40000000,
BITSPIXEL = 12,
PLANES = 14,
BI_RGB = 0,
DIB_RGB_COLORS = 0,
NIF_MESSAGE = 0x00000001,
NIF_ICON = 0x00000002,
NIF_TIP = 0x00000004,
NIF_INFO = 0x00000010,
NIN_BALLOONSHOW = WM_USER + 2,
NIN_BALLOONHIDE = WM_USER + 3,
NIN_BALLOONTIMEOUT = WM_USER + 4,
NIM_ADD = 0x00000000,
NIM_MODIFY = 0x00000001,
NIM_DELETE = 0x00000002,
NIIF_NONE = 0x00000000,
NIIF_INFO = 0x00000001,
NIIF_WARNING = 0x00000002,
NIIF_ERROR = 0x00000003,
WM_MOUSEMOVE = 0x0200,
WM_LBUTTONDOWN = 0x0201,
WM_LBUTTONUP = 0x0202,
WM_LBUTTONDBLCLK = 0x0203,
WM_RBUTTONDOWN = 0x0204,
WM_RBUTTONUP = 0x0205,
WM_RBUTTONDBLCLK = 0x0206,
WM_MBUTTONDOWN = 0x0207,
WM_MBUTTONUP = 0x0208,
WM_MBUTTONDBLCLK = 0x0209,
WM_USER = 0x0400;
[DllImport(ExternDll.User32, CharSet = CharSet.Auto)]
[ResourceExposure(ResourceScope.None)]
public static extern int RegisterWindowMessage(string msg);
}
}
+119
View File
@@ -0,0 +1,119 @@
// ReSharper disable InconsistentNaming
using System.Runtime.InteropServices;
namespace HandyControl.Tools.Interop
{
internal enum SM
{
CXSCREEN = 0,
CYSCREEN = 1,
CXVSCROLL = 2,
CYHSCROLL = 3,
CYCAPTION = 4,
CXBORDER = 5,
CYBORDER = 6,
CXFIXEDFRAME = 7,
CYFIXEDFRAME = 8,
CYVTHUMB = 9,
CXHTHUMB = 10,
CXICON = 11,
CYICON = 12,
CXCURSOR = 13,
CYCURSOR = 14,
CYMENU = 15,
CXFULLSCREEN = 16,
CYFULLSCREEN = 17,
CYKANJIWINDOW = 18,
MOUSEPRESENT = 19,
CYVSCROLL = 20,
CXHSCROLL = 21,
DEBUG = 22,
SWAPBUTTON = 23,
CXMIN = 28,
CYMIN = 29,
CXSIZE = 30,
CYSIZE = 31,
CXFRAME = 32,
CXSIZEFRAME = CXFRAME,
CYFRAME = 33,
CYSIZEFRAME = CYFRAME,
CXMINTRACK = 34,
CYMINTRACK = 35,
CXDOUBLECLK = 36,
CYDOUBLECLK = 37,
CXICONSPACING = 38,
CYICONSPACING = 39,
MENUDROPALIGNMENT = 40,
PENWINDOWS = 41,
DBCSENABLED = 42,
CMOUSEBUTTONS = 43,
SECURE = 44,
CXEDGE = 45,
CYEDGE = 46,
CXMINSPACING = 47,
CYMINSPACING = 48,
CXSMICON = 49,
CYSMICON = 50,
CYSMCAPTION = 51,
CXSMSIZE = 52,
CYSMSIZE = 53,
CXMENUSIZE = 54,
CYMENUSIZE = 55,
ARRANGE = 56,
CXMINIMIZED = 57,
CYMINIMIZED = 58,
CXMAXTRACK = 59,
CYMAXTRACK = 60,
CXMAXIMIZED = 61,
CYMAXIMIZED = 62,
NETWORK = 63,
CLEANBOOT = 67,
CXDRAG = 68,
CYDRAG = 69,
SHOWSOUNDS = 70,
CXMENUCHECK = 71,
CYMENUCHECK = 72,
SLOWMACHINE = 73,
MIDEASTENABLED = 74,
MOUSEWHEELPRESENT = 75,
XVIRTUALSCREEN = 76,
YVIRTUALSCREEN = 77,
CXVIRTUALSCREEN = 78,
CYVIRTUALSCREEN = 79,
CMONITORS = 80,
SAMEDISPLAYFORMAT = 81,
IMMENABLED = 82,
CXFOCUSBORDER = 83,
CYFOCUSBORDER = 84,
TABLETPC = 86,
MEDIACENTER = 87,
REMOTESESSION = 0x1000,
REMOTECONTROL = 0x2001,
}
internal static class Win32Constant
{
internal const int MAX_PATH = 260;
internal const int INFOTIPSIZE = 1024;
internal const int TRUE = 1;
internal const int FALSE = 0;
}
[StructLayout(LayoutKind.Sequential)]
public class POINT
{
public int x;
public int y;
public POINT()
{
}
public POINT(int x, int y)
{
this.x = x;
this.y = y;
}
}
}
@@ -0,0 +1,50 @@
using System;
using System.Runtime.InteropServices;
using System.Security;
using System.Security.Permissions;
using Microsoft.Win32.SafeHandles;
namespace HandyControl.Tools.Interop
{
internal sealed class SafeFileMappingHandle : SafeHandleZeroOrMinusOneIsInvalid
{
[SecurityCritical]
internal SafeFileMappingHandle(IntPtr handle) : base(false)
{
SetHandle(handle);
}
[SecurityCritical, SecuritySafeCritical]
internal SafeFileMappingHandle() : base(true)
{
}
public override bool IsInvalid
{
[SecurityCritical, SecuritySafeCritical]
get => handle == IntPtr.Zero;
}
[SecurityCritical, SecuritySafeCritical]
protected override bool ReleaseHandle()
{
new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Assert();
try
{
return CloseHandleNoThrow(new HandleRef(null, handle));
}
finally
{
CodeAccessPermission.RevertAssert();
}
}
[SecurityCritical]
public static bool CloseHandleNoThrow(HandleRef handle)
{
HandleCollector.Remove((IntPtr)handle, CommonHandles.Kernel);
var result = UnsafeNativeMethods.IntCloseHandle(handle);
return result;
}
}
}
@@ -0,0 +1,182 @@
using System;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Text;
namespace HandyControl.Tools.Interop
{
[SuppressMessage("ReSharper", "InconsistentNaming")]
internal class UnsafeNativeMethods
{
[SecurityCritical]
[SuppressUnmanagedCodeSecurity]
[DllImport(ExternDll.User32, SetLastError = true, ExactSpelling = true, EntryPoint = nameof(GetDC), CharSet = CharSet.Auto)]
public static extern IntPtr IntGetDC(HandleRef hWnd);
[SecurityCritical]
public static IntPtr GetDC(HandleRef hWnd)
{
var hDc = IntGetDC(hWnd);
if (hDc == IntPtr.Zero)
{
throw new Win32Exception();
}
return HandleCollector.Add(hDc, CommonHandles.HDC);
}
[SecurityCritical]
[SuppressUnmanagedCodeSecurity]
[DllImport(ExternDll.User32, ExactSpelling = true, EntryPoint = nameof(ReleaseDC), CharSet = CharSet.Auto)]
public static extern int IntReleaseDC(HandleRef hWnd, HandleRef hDC);
[SecurityCritical]
public static int ReleaseDC(HandleRef hWnd, HandleRef hDC)
{
HandleCollector.Remove((IntPtr)hDC, CommonHandles.HDC);
return IntReleaseDC(hWnd, hDC);
}
[SecurityCritical]
[SuppressUnmanagedCodeSecurity]
[DllImport(ExternDll.Gdi32, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Auto)]
public static extern int GetDeviceCaps(HandleRef hDC, int nIndex);
[SecurityCritical]
[SuppressUnmanagedCodeSecurity]
[DllImport(ExternDll.User32)]
public static extern int GetSystemMetrics(SM nIndex);
[SecurityCritical]
[SuppressUnmanagedCodeSecurity]
[DllImport(ExternDll.User32, EntryPoint = nameof(DestroyIcon), CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool IntDestroyIcon(IntPtr hIcon);
[SecurityCritical]
public static bool DestroyIcon(IntPtr hIcon)
{
var result = IntDestroyIcon(hIcon);
return result;
}
[SecurityCritical]
[SuppressUnmanagedCodeSecurity]
[DllImport(ExternDll.Gdi32, EntryPoint = nameof(DeleteObject), CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool IntDeleteObject(IntPtr hObject);
[SecurityCritical]
public static bool DeleteObject(IntPtr hObject)
{
var result = IntDeleteObject(hObject);
return result;
}
[SecurityCritical]
public static BitmapHandle CreateDIBSection(HandleRef hdc, ref BITMAPINFO bitmapInfo, int iUsage, ref IntPtr ppvBits, SafeFileMappingHandle hSection, int dwOffset)
{
if (hSection == null)
{
hSection = new SafeFileMappingHandle(IntPtr.Zero);
}
var hBitmap = PrivateCreateDIBSection(hdc, ref bitmapInfo, iUsage, ref ppvBits, hSection, dwOffset);
return hBitmap;
}
[DllImport(ExternDll.Kernel32, EntryPoint = "CloseHandle", CharSet = CharSet.Auto, SetLastError = true)]
public static extern bool IntCloseHandle(HandleRef handle);
[SecurityCritical, SuppressUnmanagedCodeSecurity]
[DllImport(ExternDll.Gdi32, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Auto, EntryPoint = nameof(CreateDIBSection))]
private static extern BitmapHandle PrivateCreateDIBSection(HandleRef hdc, ref BITMAPINFO bitmapInfo, int iUsage, ref IntPtr ppvBits, SafeFileMappingHandle hSection, int dwOffset);
[SecurityCritical, SuppressUnmanagedCodeSecurity]
[DllImport(ExternDll.User32, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Auto, EntryPoint = nameof(CreateIconIndirect))]
private static extern IconHandle PrivateCreateIconIndirect([In, MarshalAs(UnmanagedType.LPStruct)]ICONINFO iconInfo);
[SecurityCritical]
public static IconHandle CreateIconIndirect([In, MarshalAs(UnmanagedType.LPStruct)]ICONINFO iconInfo)
{
var hIcon = PrivateCreateIconIndirect(iconInfo);
return hIcon;
}
[SecurityCritical, SuppressUnmanagedCodeSecurity]
[DllImport(ExternDll.Gdi32, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Auto, EntryPoint = nameof(CreateBitmap))]
private static extern BitmapHandle PrivateCreateBitmap(int width, int height, int planes, int bitsPerPixel, byte[] lpvBits);
[SecurityCritical]
internal static BitmapHandle CreateBitmap(int width, int height, int planes, int bitsPerPixel, byte[] lpvBits)
{
var hBitmap = PrivateCreateBitmap(width, height, planes, bitsPerPixel, lpvBits);
return hBitmap;
}
[SecurityCritical, SuppressUnmanagedCodeSecurity]
[DllImport(ExternDll.Kernel32, EntryPoint = "GetModuleFileName", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern int IntGetModuleFileName(HandleRef hModule, StringBuilder buffer, int length);
[SecurityCritical]
internal static string GetModuleFileName(HandleRef hModule)
{
var buffer = new StringBuilder(Win32Constant.MAX_PATH);
while (true)
{
var size = IntGetModuleFileName(hModule, buffer, buffer.Capacity);
if (size == 0)
{
throw new Win32Exception();
}
if (size == buffer.Capacity)
{
buffer.EnsureCapacity(buffer.Capacity * 2);
continue;
}
return buffer.ToString();
}
}
[SecurityCritical, SuppressUnmanagedCodeSecurity]
[DllImport(ExternDll.Shell32, CharSet = CharSet.Auto, BestFitMapping = false, ThrowOnUnmappableChar = true)]
internal static extern int ExtractIconEx(string szExeFileName, int nIconIndex, out IconHandle phiconLarge, out IconHandle phiconSmall, int nIcons);
[DllImport(ExternDll.Shell32, CharSet = CharSet.Auto)]
public static extern int Shell_NotifyIcon(int message, NOTIFYICONDATA pnid);
[SecurityCritical]
[SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
[DllImport(ExternDll.User32, SetLastError = true, CharSet = CharSet.Unicode, EntryPoint = "CreateWindowExW")]
public static extern IntPtr CreateWindowEx(
int dwExStyle,
[MarshalAs(UnmanagedType.LPWStr)] string lpClassName,
[MarshalAs(UnmanagedType.LPWStr)] string lpWindowName,
int dwStyle,
int x,
int y,
int nWidth,
int nHeight,
IntPtr hWndParent,
IntPtr hMenu,
IntPtr hInstance,
IntPtr lpParam);
[SecurityCritical]
[SuppressUnmanagedCodeSecurity]
[DllImport(ExternDll.User32, CharSet = CharSet.Unicode, SetLastError = true, BestFitMapping = false)]
public static extern short RegisterClass(WNDCLASS wc);
[DllImport(ExternDll.User32, CharSet = CharSet.Auto)]
public static extern IntPtr DefWindowProc(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);
[DllImport(ExternDll.User32, ExactSpelling = true, CharSet = CharSet.Auto)]
public static extern bool GetCursorPos([In, Out] POINT pt);
[DllImport(ExternDll.User32, ExactSpelling = true, CharSet = CharSet.Auto)]
public static extern bool SetForegroundWindow(IntPtr hWnd);
}
}
+23
View File
@@ -0,0 +1,23 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
namespace HandyControl.Tools.Interop
{
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
[SuppressMessage("ReSharper", "InconsistentNaming")]
[SuppressMessage("ReSharper", "IdentifierTypo")]
internal class WNDCLASS
{
public int style;
public WndProc lpfnWndProc;
public int cbClsExtra;
public int cbWndExtra;
public IntPtr hInstance;
public IntPtr hIcon;
public IntPtr hCursor;
public IntPtr hbrBackground;
public string lpszMenuName;
public string lpszClassName;
}
}
@@ -0,0 +1,24 @@
using System.Security;
using Microsoft.Win32.SafeHandles;
namespace HandyControl.Tools.Interop
{
internal abstract class WpfSafeHandle : SafeHandleZeroOrMinusOneIsInvalid
{
private readonly int _collectorId;
[SecurityCritical]
protected WpfSafeHandle(bool ownsHandle, int collectorId) : base(ownsHandle)
{
HandleCollector.Add(collectorId);
_collectorId = collectorId;
}
[SecurityCritical, SecuritySafeCritical]
protected override void Dispose(bool disposing)
{
HandleCollector.Remove(_collectorId);
base.Dispose(disposing);
}
}
}
+2
View File
@@ -32,5 +32,7 @@ namespace HandyControlDemo.Data
}
public static AppConfig Config { get; set; }
public static bool NotifyIconIsShow { get; set; }
}
}
+2
View File
@@ -84,6 +84,8 @@
public static readonly string GotoTopDemoCtl = nameof(GotoTopDemoCtl);
public static readonly string NotifyIconDemoCtl = nameof(NotifyIconDemoCtl);
public static readonly string BadgeDemoCtl = nameof(BadgeDemoCtl);
public static readonly string GravatarDemoCtl = nameof(GravatarDemoCtl);
+1
View File
@@ -45,6 +45,7 @@ var controlList = new List<string>
"TransferDemoCtl",
"ChatBubbleDemoCtl",
"GotoTopDemoCtl",
"NotifyIconDemoCtl"
"BadgeDemoCtl",
"GravatarDemoCtl",
"DialogDemoCtl"
+10
View File
@@ -187,6 +187,9 @@
<Compile Include="UserControl\Controls\GotoTopDemoCtl.xaml.cs">
<DependentUpon>GotoTopDemoCtl.xaml</DependentUpon>
</Compile>
<Compile Include="UserControl\Controls\NotifyIconDemoCtl.xaml.cs">
<DependentUpon>NotifyIconDemoCtl.xaml</DependentUpon>
</Compile>
<Compile Include="UserControl\Controls\GravatarDemoCtl.xaml.cs">
<DependentUpon>GravatarDemoCtl.xaml</DependentUpon>
</Compile>
@@ -359,6 +362,7 @@
<Compile Include="ViewModel\Common\ComboBoxDemoViewModel.cs" />
<Compile Include="ViewModel\Controls\CoverViewModel.cs" />
<Compile Include="ViewModel\Controls\DialogDemoViewModel.cs" />
<Compile Include="ViewModel\Controls\NotifyIconDemoViewModel.cs" />
<Compile Include="ViewModel\Controls\SearchBarDemoViewModel.cs" />
<Compile Include="ViewModel\DemoViewModelBase!1.cs" />
<Compile Include="ViewModel\Main\ContributorsViewModel.cs" />
@@ -527,6 +531,10 @@
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="UserControl\Controls\NotifyIconDemoCtl.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="UserControl\Controls\GravatarDemoCtl.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
@@ -797,6 +805,8 @@
<Generator>TextTemplatingFileGenerator</Generator>
<LastGenOutput>MessageToken.cs</LastGenOutput>
</Content>
<Resource Include="Resources\Img\icon-white.ico" />
<Resource Include="Resources\Img\LeftMainContent\NotificationHub_16x.png" />
<Resource Include="Resources\Img\LeftMainContent\Dialog_16x.png" />
<Resource Include="Resources\Img\under_construction.gif" />
<Resource Include="Resources\Img\LeftMainContent\User_16x.png" />
+16
View File
@@ -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);
}
}
}
}
+72
View File
@@ -78,6 +78,15 @@ namespace HandyControlDemo.Properties.Langs {
}
}
/// <summary>
/// 查找类似 托盘图标已打开,将隐藏窗口而不是关闭程序 的本地化字符串。
/// </summary>
public static string AppClosingTip {
get {
return ResourceManager.GetString("AppClosingTip", resourceCulture);
}
}
/// <summary>
/// 查找类似 询问 的本地化字符串。
/// </summary>
@@ -105,6 +114,15 @@ namespace HandyControlDemo.Properties.Langs {
}
}
/// <summary>
/// 查找类似 闪烁 的本地化字符串。
/// </summary>
public static string Blink {
get {
return ResourceManager.GetString("Blink", resourceCulture);
}
}
/// <summary>
/// 查找类似 博客 的本地化字符串。
/// </summary>
@@ -429,6 +447,15 @@ namespace HandyControlDemo.Properties.Langs {
}
}
/// <summary>
/// 查找类似 退出 的本地化字符串。
/// </summary>
public static string Exit {
get {
return ResourceManager.GetString("Exit", resourceCulture);
}
}
/// <summary>
/// 查找类似 展开框 的本地化字符串。
/// </summary>
@@ -699,6 +726,15 @@ namespace HandyControlDemo.Properties.Langs {
}
}
/// <summary>
/// 查找类似 托盘图标 的本地化字符串。
/// </summary>
public static string NotifyIcon {
get {
return ResourceManager.GetString("NotifyIcon", resourceCulture);
}
}
/// <summary>
/// 查找类似 数值选择控件 的本地化字符串。
/// </summary>
@@ -789,6 +825,15 @@ namespace HandyControlDemo.Properties.Langs {
}
}
/// <summary>
/// 查找类似 打开面板 的本地化字符串。
/// </summary>
public static string OpenPanel {
get {
return ResourceManager.GetString("OpenPanel", resourceCulture);
}
}
/// <summary>
/// 查找类似 轮廓文本 的本地化字符串。
/// </summary>
@@ -1167,6 +1212,15 @@ namespace HandyControlDemo.Properties.Langs {
}
}
/// <summary>
/// 查找类似 提示 的本地化字符串。
/// </summary>
public static string Tip {
get {
return ResourceManager.GetString("Tip", resourceCulture);
}
}
/// <summary>
/// 查找类似 标题 的本地化字符串。
/// </summary>
@@ -1239,6 +1293,15 @@ namespace HandyControlDemo.Properties.Langs {
}
}
/// <summary>
/// 查找类似 试试关闭程序吧? 的本地化字符串。
/// </summary>
public static string Try2CloseApp {
get {
return ResourceManager.GetString("Try2CloseApp", resourceCulture);
}
}
/// <summary>
/// 查找类似 类型 的本地化字符串。
/// </summary>
@@ -1266,6 +1329,15 @@ namespace HandyControlDemo.Properties.Langs {
}
}
/// <summary>
/// 查找类似 可见性 的本地化字符串。
/// </summary>
public static string Visibility {
get {
return ResourceManager.GetString("Visibility", resourceCulture);
}
}
/// <summary>
/// 查找类似 警告 的本地化字符串。
/// </summary>
@@ -498,6 +498,9 @@
<data name="GotoTop" xml:space="preserve">
<value>GotoTop</value>
</data>
<data name="NotifyIcon" xml:space="preserve">
<value>NotifyIcon</value>
</data>
<data name="Badge" xml:space="preserve">
<value>Badge</value>
</data>
@@ -528,4 +531,25 @@
<data name="NewWindow" xml:space="preserve">
<value>New window</value>
</data>
<data name="Visibility" xml:space="preserve">
<value>Visibility</value>
</data>
<data name="Blink" xml:space="preserve">
<value>Blink</value>
</data>
<data name="Exit" xml:space="preserve">
<value>Exit</value>
</data>
<data name="OpenPanel" xml:space="preserve">
<value>Open Panel</value>
</data>
<data name="AppClosingTip" xml:space="preserve">
<value>The tray icon is open and will hide the window instead of closing the program</value>
</data>
<data name="Tip" xml:space="preserve">
<value>Prompt</value>
</data>
<data name="Try2CloseApp" xml:space="preserve">
<value>Try to close app?</value>
</data>
</root>
@@ -498,6 +498,9 @@
<data name="GotoTop" xml:space="preserve">
<value>回到顶部</value>
</data>
<data name="NotifyIcon" xml:space="preserve">
<value>托盘图标</value>
</data>
<data name="Badge" xml:space="preserve">
<value>标记</value>
</data>
@@ -528,4 +531,25 @@
<data name="NewWindow" xml:space="preserve">
<value>新建窗口</value>
</data>
<data name="Visibility" xml:space="preserve">
<value>可见性</value>
</data>
<data name="Blink" xml:space="preserve">
<value>闪烁</value>
</data>
<data name="Exit" xml:space="preserve">
<value>退出</value>
</data>
<data name="OpenPanel" xml:space="preserve">
<value>打开面板</value>
</data>
<data name="AppClosingTip" xml:space="preserve">
<value>托盘图标已打开,将隐藏窗口而不是关闭程序</value>
</data>
<data name="Tip" xml:space="preserve">
<value>提示</value>
</data>
<data name="Try2CloseApp" xml:space="preserve">
<value>试试关闭程序吧?</value>
</data>
</root>
Binary file not shown.

After

Width:  |  Height:  |  Size: 335 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

@@ -0,0 +1,76 @@
<UserControl x:Class="HandyControlDemo.UserControl.NotifyIconDemoCtl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:langs="clr-namespace:HandyControlDemo.Properties.Langs"
xmlns:controls="clr-namespace:HandyControl.Controls;assembly=HandyControl"
xmlns:interactivity="clr-namespace:HandyControl.Interactivity;assembly=HandyControl"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:command="http://www.galasoft.ch/mvvmlight"
DataContext="{Binding NotifyIconDemo,Source={StaticResource Locator}}"
Background="{DynamicResource RegionBrush}">
<controls:TransitioningContentControl>
<controls:SimplePanel Margin="32">
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
<GroupBox Header="ContextMenu" Padding="10">
<StackPanel>
<GroupBox controls:TitleElement.TitleAlignment="Left" Style="{StaticResource GroupBoxOriginal}" Header="{x:Static langs:Lang.Visibility}">
<ToggleButton IsChecked="{Binding ContextMenuIsShow}" Style="{StaticResource ToggleButtonSwitch}"/>
</GroupBox>
<TextBlock Foreground="{DynamicResource InfoBrush}" Visibility="{Binding ContextMenuIsShow,Converter={StaticResource Boolean2VisibilityConverter}}" Text="{x:Static langs:Lang.Try2CloseApp}" Margin="0,6,0,0"/>
<GroupBox Margin="0,10,0,0" controls:TitleElement.TitleAlignment="Left" Style="{StaticResource GroupBoxOriginal}" Header="{x:Static langs:Lang.Blink}">
<ToggleButton IsChecked="{Binding ContextMenuIsBlink}" IsEnabled="{Binding ContextMenuIsShow}" Style="{StaticResource ToggleButtonSwitch}"/>
</GroupBox>
</StackPanel>
</GroupBox>
<GroupBox Header="ContextContent" Margin="16,0,0,0" Padding="10">
<StackPanel>
<GroupBox controls:TitleElement.TitleAlignment="Left" Style="{StaticResource GroupBoxOriginal}" Header="{x:Static langs:Lang.Visibility}">
<ToggleButton IsChecked="{Binding ContextContentIsShow}" Style="{StaticResource ToggleButtonSwitch}"/>
</GroupBox>
<TextBlock Foreground="{DynamicResource InfoBrush}" Visibility="{Binding ContextContentIsShow,Converter={StaticResource Boolean2VisibilityConverter}}" Text="{x:Static langs:Lang.Try2CloseApp}" Margin="0,6,0,0"/>
<GroupBox Margin="0,10,0,0" controls:TitleElement.TitleAlignment="Left" Style="{StaticResource GroupBoxOriginal}" Header="{x:Static langs:Lang.Blink}">
<ToggleButton IsChecked="{Binding ContextContentIsBlink}" IsEnabled="{Binding ContextContentIsShow}" Style="{StaticResource ToggleButtonSwitch}"/>
</GroupBox>
</StackPanel>
</GroupBox>
</StackPanel>
<controls:NotifyIcon Text="HandyControl" IsBlink="{Binding ContextMenuIsBlink}" Visibility="{Binding ContextMenuIsShow,Converter={StaticResource Boolean2VisibilityConverter}}">
<controls:NotifyIcon.ContextMenu>
<ContextMenu>
<MenuItem Command="interactivity:ControlCommands.PushMainWindow2Top" Header="{x:Static langs:Lang.OpenPanel}"/>
<MenuItem Command="interactivity:ControlCommands.ShutdownApp" Header="{x:Static langs:Lang.Exit}"/>
</ContextMenu>
</controls:NotifyIcon.ContextMenu>
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<command:EventToCommand Command="interactivity:ControlCommands.PushMainWindow2Top"/>
</i:EventTrigger>
<i:EventTrigger EventName="MouseDoubleClick">
<command:EventToCommand Command="{Binding MouseCmd}" CommandParameter="MouseDoubleClick"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</controls:NotifyIcon>
<controls:NotifyIcon x:Name="NotifyIconContextContent" Text="HandyControl" IsBlink="{Binding ContextContentIsBlink}" Visibility="{Binding ContextContentIsShow,Converter={StaticResource Boolean2VisibilityConverter}}" Icon="/HandyControlDemo;component/Resources/Img/icon-white.ico">
<controls:NotifyIcon.ContextContent>
<Border CornerRadius="4" Margin="10" Background="White" Effect="{StaticResource EffectShadow2}">
<StackPanel VerticalAlignment="Center" Margin="16">
<Path Width="100" Height="100" Fill="#f06632" Data="{StaticResource LogoGeometry}"/>
<StackPanel Margin="0,16,0,0" HorizontalAlignment="Center" Orientation="Horizontal">
<Button Click="ButtonPush_OnClick" Command="interactivity:ControlCommands.PushMainWindow2Top" MinWidth="100" Content="{x:Static langs:Lang.OpenPanel}"/>
<Button Command="interactivity:ControlCommands.ShutdownApp" Margin="16,0,0,0" MinWidth="100" Style="{StaticResource ButtonPrimary}" Content="{x:Static langs:Lang.Exit}"/>
</StackPanel>
</StackPanel>
</Border>
</controls:NotifyIcon.ContextContent>
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<command:EventToCommand Command="interactivity:ControlCommands.PushMainWindow2Top"/>
</i:EventTrigger>
<i:EventTrigger EventName="MouseDoubleClick">
<command:EventToCommand Command="{Binding MouseCmd}" CommandParameter="MouseDoubleClick"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</controls:NotifyIcon>
</controls:SimplePanel>
</controls:TransitioningContentControl>
</UserControl>
@@ -0,0 +1,14 @@
using System.Windows;
namespace HandyControlDemo.UserControl
{
public partial class NotifyIconDemoCtl
{
public NotifyIconDemoCtl()
{
InitializeComponent();
}
private void ButtonPush_OnClick(object sender, RoutedEventArgs e) => NotifyIconContextContent.CloseContextControl();
}
}
@@ -213,6 +213,11 @@
<Image Source="../../Resources/Img/LeftMainContent/Bubble_16xLG.png"/>
</controls:EdgeElement.LeftContent>
</ListBoxItem>
<ListBoxItem Style="{StaticResource ListBoxItemNew}" Tag="{x:Static data:MessageToken.NotifyIconDemoCtl}" Content="{x:Static langs:Lang.NotifyIcon}">
<controls:EdgeElement.LeftContent>
<Image Source="../../Resources/Img/LeftMainContent/NotificationHub_16x.png"/>
</controls:EdgeElement.LeftContent>
</ListBoxItem>
<ListBoxItem Tag="{x:Static data:MessageToken.NumericUpDownDemoCtl}" Content="{x:Static langs:Lang.NumericUpDown}">
<controls:EdgeElement.LeftContent>
<Image Source="../../Resources/Img/LeftMainContent/NumericListBox_16x.png"/>
@@ -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;
});
}
@@ -0,0 +1,54 @@
using System;
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.CommandWpf;
using HandyControl.Controls;
using HandyControlDemo.Data;
namespace HandyControlDemo.ViewModel
{
public class NotifyIconDemoViewModel : ViewModelBase
{
private bool _contextMenuIsShow;
public bool ContextMenuIsShow
{
get => _contextMenuIsShow;
set
{
Set(ref _contextMenuIsShow, value);
GlobalData.NotifyIconIsShow = ContextMenuIsShow || ContextContentIsShow;
}
}
private bool _contextMenuIsBlink;
public bool ContextMenuIsBlink
{
get => _contextMenuIsBlink;
set => Set(ref _contextMenuIsBlink, value);
}
private bool _contextContentIsShow;
public bool ContextContentIsShow
{
get => _contextContentIsShow;
set
{
Set(ref _contextContentIsShow, value);
GlobalData.NotifyIconIsShow = ContextMenuIsShow || ContextContentIsShow;
}
}
private bool _contextContentIsBlink;
public bool ContextContentIsBlink
{
get => _contextContentIsBlink;
set => Set(ref _contextContentIsBlink, value);
}
public RelayCommand<object> MouseCmd => new Lazy<RelayCommand<object>>(() =>
new RelayCommand<object>(str=> Growl.Info(str.ToString()))).Value;
}
}
@@ -26,6 +26,7 @@ namespace HandyControlDemo.ViewModel
SimpleIoc.Default.Register<CoverViewModel>();
SimpleIoc.Default.Register<DialogDemoViewModel>();
SimpleIoc.Default.Register<SearchBarDemoViewModel>();
SimpleIoc.Default.Register<NotifyIconDemoViewModel>();
}
public static ViewModelLocator Instance => new Lazy<ViewModelLocator>(() =>
@@ -57,6 +58,8 @@ namespace HandyControlDemo.ViewModel
public SearchBarDemoViewModel SearchBarDemo => ServiceLocator.Current.GetInstance<SearchBarDemoViewModel>();
public NotifyIconDemoViewModel NotifyIconDemo => ServiceLocator.Current.GetInstance<NotifyIconDemoViewModel>();
#endregion
}
}
+17 -10
View File
@@ -9,14 +9,9 @@
![qq-group](https://raw.githubusercontent.com/NaBian/HandyControl/master/Resources/qq-group.png)
## 捐赠
如果您觉得HandyControl还不错,那么就以HandyControl的名义,花一顿饭的钱来为慈善事业做一份贡献吧(捐赠留言写:HandyControl),谢谢大家。
## Special thanks to
捐赠链接:
[中华慈善总会](http://www.chinacharityfederation.org/ConfirmDonation/0.html?zhijie=3)
也可以扫下面的二维码:
![中华慈善总会](https://raw.githubusercontent.com/NaBian/HandyControl/master/Resources/chinacharityfederation.png)
[![JetBrains](https://raw.githubusercontent.com/NaBian/HandyControl/master/Resources/resharper_logo.svg)](https://www.jetbrains.com/?from=HandyControl)
## Usage
@@ -39,6 +34,10 @@ Step 3:enjoy coding
## Latest examples
### NotifyIcon
![NotifyIcon](https://raw.githubusercontent.com/NaBian/HandyControl/master/Resources/NotifyIcon.png)
### Dialog
![Dialog](https://raw.githubusercontent.com/NaBian/HandyControl/master/Resources/Dialog.png)
@@ -55,12 +54,12 @@ Step 3:enjoy coding
![Gravatar](https://raw.githubusercontent.com/NaBian/HandyControl/master/Resources/Gravatar.gif)
## History publication
### ChatBubble
![ChatBubble](https://raw.githubusercontent.com/NaBian/HandyControl/master/Resources/ChatBubble.png)
## History publication
### Transfer
![Transfer](https://raw.githubusercontent.com/NaBian/HandyControl/master/Resources/Transfer.png)
@@ -287,6 +286,14 @@ Step 3:enjoy coding
![BorderStyle](https://raw.githubusercontent.com/NaBian/HandyControl/master/Resources/Border.png)
# Switching configuration
## Switching configuration
![Switching configuration](https://raw.githubusercontent.com/NaBian/HandyControl/master/Resources/SwitchConfig.png)
# 捐赠(面向中国)
如果您觉得HandyControl还不错,并且刚好有些闲钱,那么你可以选择以下两种方式来捐赠:
* [以HandyControl的名义为慈善事业做贡献](http://www.chinacharityfederation.org/ConfirmDonation/0.html?zhijie=3)
* 为我们购买防脱洗发水
![qrcode](https://raw.githubusercontent.com/NaBian/HandyControl/master/Resources/qrcode.png)
Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

+50
View File
@@ -0,0 +1,50 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="70px" height="70px" viewBox="0 0 70 70" style="enable-background:new 0 0 70 70;" xml:space="preserve">
<g>
<g>
<g>
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="22.9451" y1="75.7869" x2="74.7868" y2="20.6415">
<stop offset="1.612903e-002" style="stop-color:#B35BA3"/>
<stop offset="0.4044" style="stop-color:#C41E57"/>
<stop offset="0.4677" style="stop-color:#C41E57"/>
<stop offset="0.6505" style="stop-color:#EB8523"/>
<stop offset="0.9516" style="stop-color:#FEBD11"/>
</linearGradient>
<polygon style="fill:url(#SVGID_1_);" points="49.8,15.2 36,36.7 58.4,70 70,23.1 "/>
<linearGradient id="SVGID_2_" gradientUnits="userSpaceOnUse" x1="17.7187" y1="73.2922" x2="69.5556" y2="18.1519">
<stop offset="1.612903e-002" style="stop-color:#B35BA3"/>
<stop offset="0.4044" style="stop-color:#C41E57"/>
<stop offset="0.4677" style="stop-color:#C41E57"/>
<stop offset="0.7043" style="stop-color:#EB8523"/>
</linearGradient>
<polygon style="fill:url(#SVGID_2_);" points="51.1,15.7 49,0 18.8,33.6 27.6,42.3 20.8,70 58.4,70 "/>
</g>
<linearGradient id="SVGID_3_" gradientUnits="userSpaceOnUse" x1="1.8281" y1="53.4275" x2="48.8245" y2="9.2255">
<stop offset="1.612903e-002" style="stop-color:#B35BA3"/>
<stop offset="0.6613" style="stop-color:#C41E57"/>
</linearGradient>
<polygon style="fill:url(#SVGID_3_);" points="49,0 11.6,0 0,47.1 55.6,47.1 "/>
<linearGradient id="SVGID_4_" gradientUnits="userSpaceOnUse" x1="49.8935" y1="-11.5569" x2="48.8588" y2="24.0352">
<stop offset="0.5" style="stop-color:#C41E57"/>
<stop offset="0.6668" style="stop-color:#D13F48"/>
<stop offset="0.7952" style="stop-color:#D94F39"/>
<stop offset="0.8656" style="stop-color:#DD5433"/>
</linearGradient>
<polygon style="fill:url(#SVGID_4_);" points="55.3,47.1 51.1,15.7 49,0 41.7,23 "/>
</g>
<g>
<rect x="13.4" y="13.5" transform="matrix(-1 2.577289e-003 -2.577289e-003 -1 70.0288 70.081)" style="fill:#000000;" width="43.2" height="43.2"/>
<rect x="17.6" y="48.6" transform="matrix(1 -2.577289e-003 2.577289e-003 1 -0.1287 6.634109e-002)" style="fill:#FFFFFF;" width="16.2" height="2.7"/>
<path style="fill:#FFFFFF;" d="M17.4,19.1l8.2,0c2.3,0,4,0.6,5.2,1.8c1,1,1.5,2.4,1.5,4.1l0,0.1c0,1.5-0.3,2.6-1.1,3.5
c-0.7,0.9-1.6,1.6-2.8,2l4.4,6.4l-4.6,0l-3.7-5.5l-3.3,0l0,5.5l-3.9,0L17.4,19.1z M25.3,27.8c1,0,1.7-0.2,2.2-0.7
c0.5-0.5,0.8-1.1,0.8-1.8l0-0.1c0-0.9-0.3-1.5-0.8-1.9c-0.5-0.4-1.3-0.6-2.3-0.6l-3.9,0l0,5.1L25.3,27.8z"/>
<path style="fill:#FFFFFF;" d="M36,33.2l-1.9,0l0-3.3l2.5,0l0.6-3.8l-2.3,0l0-3.3l2.8,0l0.6-3.7l3.4,0l-0.6,3.7l3.7,0l0.6-3.7
l3.4,0l-0.6,3.7l1.9,0l0,3.3l-2.5,0L47,29.9l2.3,0l0,3.3l-2.8,0L45.8,37l-3.4,0l0.7-3.8l-3.7,0L38.7,37l-3.4,0L36,33.2z
M43.7,29.9l0.6-3.8l-3.7,0L40,29.9L43.7,29.9z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.1 KiB