Add some WinUI work

This commit is contained in:
Dan Clark
2024-11-17 21:25:27 +00:00
parent b5bf17821c
commit 796ef41e3f
25 changed files with 426 additions and 159 deletions
-1
View File
@@ -1,6 +1,5 @@
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Controls.Templates; using Avalonia.Controls.Templates;
using Avalonia.Data;
using Avalonia.Interactivity; using Avalonia.Interactivity;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Toolkit.Foundation; using Toolkit.Foundation;
@@ -7,14 +7,14 @@ public class AsyncHandlerInitialization<TMessage, TResponse, THandler>(IServiceP
IInitialization where THandler : class, IAsyncHandler<TMessage, TResponse> IInitialization where THandler : class, IAsyncHandler<TMessage, TResponse>
where TMessage : class where TMessage : class
{ {
public void Initialize() => WeakReferenceMessenger.Default.Register<IServiceProvider, AsyncResponseEventArgs<TMessage, TResponse>>(provider, public void Initialize() => StrongReferenceMessenger.Default.Register<IServiceProvider, AsyncResponseEventArgs<TMessage, TResponse>>(provider,
async (provider, args) => args.Reply(await provider.GetRequiredService<THandler>().Handle(args.Message, args.CancellationToken))); (provider, args) => args.Reply(provider.GetRequiredService<THandler>().Handle(args.Message, args.CancellationToken)));
} }
public class AsyncHandlerInitialization<TMessage, THandler>(IServiceProvider provider) : public class AsyncHandlerInitialization<TMessage, THandler>(IServiceProvider provider) :
IInitialization where THandler : class, IAsyncHandler<TMessage> IInitialization where THandler : class, IAsyncHandler<TMessage>
where TMessage : class where TMessage : class
{ {
public void Initialize() => WeakReferenceMessenger.Default.Register<IServiceProvider, AsyncResponseEventArgs<TMessage, Unit>>(provider, public void Initialize() => StrongReferenceMessenger.Default.Register<IServiceProvider, AsyncResponseEventArgs<TMessage, Unit>>(provider,
async (provider, args) => await provider.GetRequiredService<THandler>().Handle(args.Message, args.CancellationToken)); (provider, args) => provider.GetRequiredService<THandler>().Handle(args.Message, args.CancellationToken));
} }
+1 -1
View File
@@ -5,7 +5,7 @@ namespace Toolkit.Foundation;
public class AsyncResponseEventArgs<TMessage, TResponse> : public class AsyncResponseEventArgs<TMessage, TResponse> :
AsyncRequestMessage<TResponse> AsyncRequestMessage<TResponse>
{ {
public TMessage? Message { get; set; } public required TMessage Message { get; set; }
public CancellationToken CancellationToken { get; set; } public CancellationToken CancellationToken { get; set; }
} }
+4 -10
View File
@@ -20,14 +20,8 @@ public class DefaultHostBuilder :
ComponentHostCollection>(); ComponentHostCollection>();
services.AddSingleton<IDisposer, Disposer>(); services.AddSingleton<IDisposer, Disposer>();
services.AddSingleton<IMessenger, WeakReferenceMessenger>(_ => WeakReferenceMessenger.Default); services.AddSingleton<IMessenger, StrongReferenceMessenger>(_ => StrongReferenceMessenger.Default);
//services.AddScoped<SubscriptionCollection>();
//services.AddTransient<IHandlerProvider, HandlerProvider>();
//services.AddScoped<ISubscriber, Subscriber>();
//services.AddTransient<IPublisher, Publisher>();
//services.AddTransient<IMediator, Mediator>();
services.AddTransient<IValidation, Validation>(); services.AddTransient<IValidation, Validation>();
services.AddTransient<IValidatorCollection, ValidatorCollection>(); services.AddTransient<IValidatorCollection, ValidatorCollection>();
@@ -57,10 +51,10 @@ public class DefaultHostBuilder :
services.AddTransient<IComponentFactory, ComponentFactory>(); services.AddTransient<IComponentFactory, ComponentFactory>();
services.AddTransient<IComponentScopeProvider, ComponentScopeProvider>(); services.AddTransient<IComponentScopeProvider, ComponentScopeProvider>();
//services.AddHandler<NavigateHandler>(); services.AddHandler<NavigateEventArgs, NavigateHandler>();
//services.AddHandler<NavigateBackHandler>(); services.AddHandler<NavigateBackEventArgs, NavigateBackHandler>();
//services.AddInitialization<ComponentInitializer>(); services.AddInitialization<ComponentInitializer>();
services.AddHostedService<AppService>(); services.AddHostedService<AppService>();
}); });
} }
+3 -3
View File
@@ -7,7 +7,7 @@ public class HandlerInitialization<TMessage, TResponse, THandler>(IServiceProvid
IInitialization where THandler : class, IHandler<TMessage, TResponse> IInitialization where THandler : class, IHandler<TMessage, TResponse>
where TMessage : class where TMessage : class
{ {
public void Initialize() => WeakReferenceMessenger.Default.Register<IServiceProvider, ResponseEventArgs<TMessage, TResponse>>(provider, public void Initialize() => StrongReferenceMessenger.Default.Register<IServiceProvider, ResponseEventArgs<TMessage, TResponse>>(provider,
(provider, args) => args.Reply(provider.GetRequiredService<THandler>().Handle(args.Message))); (provider, args) => args.Reply(provider.GetRequiredService<THandler>().Handle(args.Message)));
} }
@@ -15,7 +15,7 @@ public class HandlerInitialization<TMessage, THandler>(IServiceProvider provider
IInitialization where THandler : class, IHandler<TMessage> IInitialization where THandler : class, IHandler<TMessage>
where TMessage : class where TMessage : class
{ {
public void Initialize() => WeakReferenceMessenger.Default.Register<IServiceProvider, TMessage>(provider, public void Initialize() => StrongReferenceMessenger.Default.Register<IServiceProvider, TMessage>(provider,
(provider, args) => provider.GetRequiredService<THandler>().Handle(args)); (provider, args) => provider.GetRequiredService<THandler>().Handle(args));
} }
@@ -23,6 +23,6 @@ public class HandlerKeyedInitialization<TMessage, THandler>(string key, IService
IInitialization where THandler : class, IHandler<TMessage> IInitialization where THandler : class, IHandler<TMessage>
where TMessage : class where TMessage : class
{ {
public void Initialize() => WeakReferenceMessenger.Default.Register<IServiceProvider, TMessage, string>(provider, key, public void Initialize() => StrongReferenceMessenger.Default.Register<IServiceProvider, TMessage, string>(provider, key,
(provider, args) => provider.GetRequiredKeyedService<THandler>(key).Handle(args)); (provider, args) => provider.GetRequiredKeyedService<THandler>(key).Handle(args));
} }
+21 -3
View File
@@ -11,9 +11,27 @@ public static class IMessengerExtensions
return args.Response; return args.Response;
} }
public static void Send<TMessage>(this IMessenger messenger, string key) public static TResponse Send<TMessage, TResponse>(this IMessenger messenger,
where TMessage : class, new() => messenger.Send(new TMessage(), key); TMessage message)
where TMessage : class
{
ResponseEventArgs<TMessage, TResponse> args = messenger.Send(new ResponseEventArgs<TMessage, TResponse> { Message = message });
return args.Response;
}
public static void Send<TMessage>(this IMessenger messenger,
TMessage message, string key) where TMessage : class =>
messenger.Send(message, key);
public static void Send<TMessage>(this IMessenger messenger,
string key) where TMessage : class, new() =>
messenger.Send(new TMessage(), key);
public static async Task<TResponse> SendAsync<TMessage, TResponse>(this IMessenger messenger) public static async Task<TResponse> SendAsync<TMessage, TResponse>(this IMessenger messenger)
where TMessage : class, new() => await messenger.Send(new AsyncResponseEventArgs<TMessage, TResponse> { Message = new TMessage() }); where TMessage : class, new() =>
await messenger.Send(new AsyncResponseEventArgs<TMessage, TResponse> { Message = new TMessage() });
public static async Task<TResponse> SendAsync<TMessage, TResponse>(this IMessenger messenger,
TMessage message) where TMessage : class =>
await messenger.Send(new AsyncResponseEventArgs<TMessage, TResponse> { Message = message });
} }
+21
View File
@@ -0,0 +1,21 @@
using System.Drawing;
using System.IO;
using Toolkit.Windows;
using Toolkit.WinUI;
namespace Toolkit.UI.WinUI;
public static class NotifyIconExtensions
{
public static void SetIcon(this INotifyIcon notifyIcon,
Stream? stream)
{
nint shellTrayHandle = WindowHelper.GetWindowHandle("Shell_TrayWnd");
uint dpi = WindowHelper.GetDpi(shellTrayHandle);
if (stream?.ConvertToIcon(dpi) is Icon icon)
{
notifyIcon.SetIcon(icon.Handle);
}
}
}
+1
View File
@@ -23,5 +23,6 @@
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Toolkit.Windows\Toolkit.Windows.csproj" /> <ProjectReference Include="..\Toolkit.Windows\Toolkit.Windows.csproj" />
<ProjectReference Include="..\Toolkit.WinUI\Toolkit.WinUI.csproj" />
</ItemGroup> </ItemGroup>
</Project> </Project>
+87 -18
View File
@@ -7,6 +7,9 @@ using Windows.Win32.Graphics.Gdi;
using System.Drawing; using System.Drawing;
using Windows.Win32.UI.Shell; using Windows.Win32.UI.Shell;
using Windows.Win32.UI.WindowsAndMessaging; using Windows.Win32.UI.WindowsAndMessaging;
using Toolkit.Windows;
using Rect = Windows.Foundation.Rect;
using WinUIEx;
namespace Toolkit.UI.WinUI; namespace Toolkit.UI.WinUI;
@@ -14,20 +17,78 @@ public static partial class WindowExtensions
{ {
private static SUBCLASSPROC? SubClassDelegate; private static SUBCLASSPROC? SubClassDelegate;
public static void SetBorderless(this Window window, public static void Hide(this Window window)
{
nint handle = WindowNative.GetWindowHandle(window);
if (handle == 0) return;
WindowHelper.HideWindow(new HWND(handle));
}
public static void IsShownInSwitchers(this Window window,
bool value) bool value)
{ {
if (window.AppWindow is AppWindow appWindow && if (window.AppWindow is AppWindow appWindow)
appWindow.Presenter is OverlappedPresenter presenter)
{ {
presenter.IsMaximizable = !value; appWindow.IsShownInSwitchers = value;
presenter.IsMinimizable = !value;
presenter.IsResizable = !value;
presenter.SetBorderAndTitleBar(!value, !value);
} }
} }
public static void SetTransparency(this Window window, public static void MoveAndResize(this Window window,
Rect rect)
{
nint handle = WindowNative.GetWindowHandle(window);
if (handle == 0) return;
WindowHelper.MoveAndResizeWindow(new HWND(handle),
(int)rect.Left,
(int)rect.Top,
(int)rect.Width,
(int)rect.Height);
}
public static void SetBorderless(this Window window,
bool value)
{
WindowStyle windowStyle = window.GetWindowStyle();
if (value)
{
windowStyle &= ~(WindowStyle.Caption |
WindowStyle.ThickFrame |
WindowStyle.Border |
WindowStyle.SysMenu);
}
else
{
windowStyle |= WindowStyle.Caption |
WindowStyle.ThickFrame |
WindowStyle.Border |
WindowStyle.SysMenu;
}
window.SetWindowStyle(windowStyle);
}
public static void SetForeground(this Window window)
{
nint handle = WindowNative.GetWindowHandle(window);
if (handle == 0) return;
WindowHelper.SetForegroundWindow(new HWND(handle));
}
public static void SetTopMost(this Window window,
bool value)
{
if (window.AppWindow is AppWindow appWindow &&
appWindow.Presenter is OverlappedPresenter presenter)
{
presenter.IsAlwaysOnTop = value;
}
}
public static void SetTransparency(this Window window,
bool value) bool value)
{ {
nint handle = WindowNative.GetWindowHandle(window); nint handle = WindowNative.GetWindowHandle(window);
@@ -44,18 +105,12 @@ public static partial class WindowExtensions
} }
} }
private static unsafe void EnableTransparency(HWND hWnd) public static void Show(this Window window)
{ {
SubClassDelegate = new SUBCLASSPROC(WindowSubClass); nint handle = WindowNative.GetWindowHandle(window);
_ = PInvoke.SetWindowSubclass(hWnd, SubClassDelegate, 0, 0); if (handle == 0) return;
WINDOW_EX_STYLE exStyle = (WINDOW_EX_STYLE)PInvoke.GetWindowLong(hWnd, WINDOW_LONG_PTR_INDEX.GWL_EXSTYLE); WindowHelper.ShowWindow(new HWND(handle));
_ = PInvoke.SetWindowLong(hWnd, WINDOW_LONG_PTR_INDEX.GWL_EXSTYLE,
(int)(exStyle | WINDOW_EX_STYLE.WS_EX_LAYERED));
COLORREF blackColor = new COLORREF((uint)ToWin32(Color.Black));
_ = PInvoke.SetLayeredWindowAttributes(hWnd, blackColor, 0, LAYERED_WINDOW_ATTRIBUTES_FLAGS.LWA_COLORKEY |
LAYERED_WINDOW_ATTRIBUTES_FLAGS.LWA_ALPHA);
} }
private static unsafe void DisableTransparency(HWND hWnd) private static unsafe void DisableTransparency(HWND hWnd)
@@ -70,6 +125,20 @@ public static partial class WindowExtensions
_ = PInvoke.SetWindowLong(hWnd, WINDOW_LONG_PTR_INDEX.GWL_EXSTYLE, (int)(exStyle & ~WINDOW_EX_STYLE.WS_EX_LAYERED)); _ = PInvoke.SetWindowLong(hWnd, WINDOW_LONG_PTR_INDEX.GWL_EXSTYLE, (int)(exStyle & ~WINDOW_EX_STYLE.WS_EX_LAYERED));
} }
private static unsafe void EnableTransparency(HWND hWnd)
{
SubClassDelegate = new SUBCLASSPROC(WindowSubClass);
_ = PInvoke.SetWindowSubclass(hWnd, SubClassDelegate, 0, 0);
WINDOW_EX_STYLE exStyle = (WINDOW_EX_STYLE)PInvoke.GetWindowLong(hWnd, WINDOW_LONG_PTR_INDEX.GWL_EXSTYLE);
_ = PInvoke.SetWindowLong(hWnd, WINDOW_LONG_PTR_INDEX.GWL_EXSTYLE,
(int)(exStyle | WINDOW_EX_STYLE.WS_EX_LAYERED));
COLORREF blackColor = new COLORREF((uint)ToWin32(Color.Black));
_ = PInvoke.SetLayeredWindowAttributes(hWnd, blackColor, 0, LAYERED_WINDOW_ATTRIBUTES_FLAGS.LWA_COLORKEY |
LAYERED_WINDOW_ATTRIBUTES_FLAGS.LWA_ALPHA);
}
private static int ToWin32(Color c) => c.B << 16 | c.G << 8 | c.R; private static int ToWin32(Color c) => c.B << 16 | c.G << 8 | c.R;
private static unsafe LRESULT WindowSubClass(HWND hWnd, private static unsafe LRESULT WindowSubClass(HWND hWnd,
+45
View File
@@ -0,0 +1,45 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Markup;
using Toolkit.Foundation;
namespace Toolkit.WinUI;
public class ContentTemplate :
DataTemplateSelector,
IContentTemplate
{
protected override DataTemplate? SelectTemplateCore(object item)
{
if (item is IObservableViewModel observableViewModel)
{
if (observableViewModel.Provider is IServiceProvider provider)
{
Type itemType = item.GetType();
if (provider.GetRequiredKeyedService<IContentTemplateDescriptor>(itemType.Name.Replace("ViewModel", ""))
is IContentTemplateDescriptor descriptor)
{
return CreateDataTemplate(descriptor);
}
}
}
return default;
}
protected override DataTemplate? SelectTemplateCore(object item,
DependencyObject container) => SelectTemplateCore(item);
private static DataTemplate CreateDataTemplate(IContentTemplateDescriptor descriptor)
{
string xamlString = @$"
<DataTemplate xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation""
xmlns:x=""http://schemas.microsoft.com/winfx/2006/xaml""
xmlns:local=""using:Toolkit.WinUI"">
<local:TemplateControl />
</DataTemplate>";
return (DataTemplate)XamlReader.Load(xamlString);
}
}
@@ -11,6 +11,8 @@ public static class IServiceCollectionExtensions
services.AddTransient<IDispatcherTimerFactory, DispatcherTimerFactory>(); services.AddTransient<IDispatcherTimerFactory, DispatcherTimerFactory>();
services.AddSingleton<IWindowRegistry, WindowRegistry>(); services.AddSingleton<IWindowRegistry, WindowRegistry>();
services.AddTransient<IContentTemplate, ContentTemplate>();
services.AddTransient((Func<IServiceProvider, IProxyServiceCollection<IComponentBuilder>>)(provider => services.AddTransient((Func<IServiceProvider, IProxyServiceCollection<IComponentBuilder>>)(provider =>
new ProxyServiceCollection<IComponentBuilder>(services => new ProxyServiceCollection<IComponentBuilder>(services =>
{ {
+79
View File
@@ -0,0 +1,79 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Data;
using Toolkit.Foundation;
namespace Toolkit.WinUI;
[Bindable]
public class TemplateControl :
ContentControl
{
public TemplateControl()
{
DefaultStyleKey = typeof(TemplateControl);
Loaded += OnLoaded;
}
private void OnLoaded(object sender,
RoutedEventArgs args)
{
Loaded -= OnLoaded;
if (DataContext is IObservableViewModel observableViewModel)
{
if (observableViewModel.Provider is IServiceProvider provider)
{
if (provider.GetRequiredKeyedService<IContentTemplateDescriptor>(DataContext.GetType().Name.Replace("ViewModel", ""))
is IContentTemplateDescriptor descriptor)
{
if (provider.GetRequiredKeyedService(descriptor.TemplateType, descriptor.Key)
is FrameworkElement control)
{
void HandleLoaded(object? sender, RoutedEventArgs args)
{
control.Loaded -= HandleLoaded;
if (control.DataContext is object content)
{
if (content is IActivation activation)
{
activation.IsActive = true;
}
}
}
void HandleDataContextChanged(FrameworkElement? sender, DataContextChangedEventArgs args)
{
if (control.DataContext is object content)
{
if (content is IActivation activation)
{
activation.IsActive = true;
}
}
}
void HandleUnloaded(object? sender, RoutedEventArgs args)
{
control.Unloaded -= HandleUnloaded;
if (control.DataContext is object content)
{
if (content is IActivation activation)
{
activation.IsActive = false;
}
}
}
control.Loaded += HandleLoaded;
control.Unloaded += HandleUnloaded;
control.DataContextChanged += HandleDataContextChanged;
Content = control;
}
}
}
}
}
}
+1 -2
View File
@@ -15,14 +15,13 @@
<UseWinUI>true</UseWinUI> <UseWinUI>true</UseWinUI>
<WindowsSdkPackageVersion>10.0.19041.41</WindowsSdkPackageVersion> <WindowsSdkPackageVersion>10.0.19041.41</WindowsSdkPackageVersion>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.26100.1742" /> <PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.26100.1742" />
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.6.240923002" /> <PackageReference Include="Microsoft.WindowsAppSDK" Version="1.6.240923002" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Toolkit.Foundation\Toolkit.Foundation.csproj" />
<ProjectReference Include="..\Toolkit.Windows\Toolkit.Windows.csproj" /> <ProjectReference Include="..\Toolkit.Windows\Toolkit.Windows.csproj" />
</ItemGroup> </ItemGroup>
@@ -2,6 +2,6 @@
namespace Toolkit.Windows; namespace Toolkit.Windows;
public interface IPointerMonitor : public interface IPointer :
IInitialization, IInitialization,
IDisposable; IDisposable;
+46 -36
View File
@@ -1,41 +1,51 @@
SetWindowsHookEx CallNextHookEx
GetModuleHandle CreateSolidBrush
CallNextHookEx
GetPhysicalCursorPos
FindWindowEx
FindWindow
GetWindowRect
DestroyWindow
DefWindowProcW
CreateWindowExW CreateWindowExW
RegisterClassW DWM_WINDOW_CORNER_PREFERENCE
GetSystemMetrics DefSubclassProc
MonitorFromWindow DefWindowProcW
RegisterWindowMessage DeleteObject
GetDpiForWindow DestroyWindow
SetWindowPos DwmSetWindowAttribute
SHCreateShellItemArrayFromDataObject FillRect
Shell_NotifyIcon FindWindow
GetSystemMetricsForDpi FindWindowEx
GetSystemMetrics GetClientRect
GetCurrentProcess GetCurrentProcess
SetProcessInformation GetDpiForWindow
PROCESS_POWER_THROTTLING_STATE GetPhysicalCursorPos
GetSystemMetrics
GetSystemMetricsForDpi
GetWindowLong
GetWindowRect
MonitorFromWindow
PROCESS_POWER_THROTTLING_CURRENT_VERSION PROCESS_POWER_THROTTLING_CURRENT_VERSION
PROCESS_POWER_THROTTLING_EXECUTION_SPEED PROCESS_POWER_THROTTLING_EXECUTION_SPEED
SetPriorityClass PROCESS_POWER_THROTTLING_STATE
WINDOW_STYLE RegisterClassW
DefSubclassProc RegisterWindowMessage
SetLayeredWindowAttributes
WM_ERASEBKGND
SetWindowSubclass
GetClientRect
CreateSolidBrush
DwmSetWindowAttribute
DWM_WINDOW_CORNER_PREFERENCE
FillRect
SetLayeredWindowAttributes
DeleteObject
RemoveWindowSubclass RemoveWindowSubclass
GetWindowLong SHCreateShellItemArrayFromDataObject
SetWindowLong SetLayeredWindowAttributes
SetPriorityClass
SetProcessInformation
SetWindowLong
SetWindowPos
SetWindowSubclass
SetWindowsHookEx
Shell_NotifyIcon
WINDOW_STYLE
WM_ERASEBKGND
SetForegroundWindow
ShowWindow
GetCursorPos
UnregisterClass
CreateWindowEx
RegisterClass
DefWindowProc
GetMessage
TranslateMessage
DispatchMessage
PostQuitMessage
DestroyWindow
GetModuleHandle
+17 -10
View File
@@ -1,11 +1,12 @@
using CommunityToolkit.Mvvm.Messaging; using CommunityToolkit.Mvvm.Messaging;
using System.Drawing;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using Toolkit.Foundation; using Toolkit.Foundation;
using Windows.Win32;
namespace Toolkit.Windows; namespace Toolkit.Windows;
public class NotifyIcon(IWndProc wndProc,
public partial class NotifyIcon(IWndProc wndProc,
IMessenger messenger) : IMessenger messenger) :
INotifyIcon, INotifyIcon,
IRecipient<WndProcEventArgs> IRecipient<WndProcEventArgs>
@@ -17,11 +18,6 @@ public partial class NotifyIcon(IWndProc wndProc,
private bool isDisposed; private bool isDisposed;
private NotifyIconData notifyIconData; private NotifyIconData notifyIconData;
~NotifyIcon()
{
Dispose(false);
}
private enum NotifyIconBalloonType private enum NotifyIconBalloonType
{ {
None = 0x00, None = 0x00,
@@ -66,6 +62,14 @@ public partial class NotifyIcon(IWndProc wndProc,
GC.SuppressFinalize(this); GC.SuppressFinalize(this);
} }
public unsafe PointerLocation GetPointerPosition()
{
Point point = new();
_ = PInvoke.GetCursorPos(&point);
return new PointerLocation(point.X, point.Y);
}
public void Initialize() public void Initialize()
{ {
messenger.RegisterAll(this); messenger.RegisterAll(this);
@@ -79,15 +83,18 @@ public partial class NotifyIcon(IWndProc wndProc,
switch (message.LParam) switch (message.LParam)
{ {
case (uint)WndProcMessages.WM_LBUTTONUP: case (uint)WndProcMessages.WM_LBUTTONUP:
messenger.Send(new NotifyIconInvokedEventArgs(PointerButton.Left)); messenger.Send(new NotifyIconInvokedEventArgs(PointerButton.Left,
GetPointerPosition()));
break; break;
case (uint)WndProcMessages.WM_MBUTTONUP: case (uint)WndProcMessages.WM_MBUTTONUP:
messenger.Send(new NotifyIconInvokedEventArgs(PointerButton.Middle)); messenger.Send(new NotifyIconInvokedEventArgs(PointerButton.Middle,
GetPointerPosition()));
break; break;
case (uint)WndProcMessages.WM_RBUTTONUP: case (uint)WndProcMessages.WM_RBUTTONUP:
messenger.Send(new NotifyIconInvokedEventArgs(PointerButton.Right)); messenger.Send(new NotifyIconInvokedEventArgs(PointerButton.Right,
GetPointerPosition()));
break; break;
} }
} }
@@ -1,3 +1,4 @@
namespace Toolkit.Windows; namespace Toolkit.Windows;
public record NotifyIconInvokedEventArgs(PointerButton PointerButton); public record NotifyIconInvokedEventArgs(PointerButton Button,
PointerLocation Location);
+5
View File
@@ -59,4 +59,9 @@ public static partial class PInvoke
public static void GetAppBarPosition(ref AppBarData appBarData) => public static void GetAppBarPosition(ref AppBarData appBarData) =>
SHAppBarMessage(AppBarMessage.GetTaskbarPos, ref appBarData); SHAppBarMessage(AppBarMessage.GetTaskbarPos, ref appBarData);
internal static bool SetWindowSubclass(HWND value1, Func<HWND, uint, WPARAM, LPARAM, nuint, nuint, LRESULT> value2, int v1, nuint v2)
{
throw new NotImplementedException();
}
} }
@@ -7,8 +7,8 @@ using Windows.Win32.UI.WindowsAndMessaging;
namespace Toolkit.Windows; namespace Toolkit.Windows;
public class PointerMonitor(IMessenger messenger) : public class Pointer(IMessenger messenger) :
IPointerMonitor IPointer
{ {
private bool isDisposed; private bool isDisposed;
private bool isPointerDrag; private bool isPointerDrag;
@@ -16,7 +16,7 @@ public class PointerMonitor(IMessenger messenger) :
private HOOKPROC? mouseEventDelegate; private HOOKPROC? mouseEventDelegate;
private UnhookWindowsHookExSafeHandle? mouseHandle; private UnhookWindowsHookExSafeHandle? mouseHandle;
~PointerMonitor() ~Pointer()
{ {
Dispose(false); Dispose(false);
} }
@@ -126,9 +126,9 @@ public class PointerMonitor(IMessenger messenger) :
} }
} }
private unsafe bool TryGetPointer(out Point point) private unsafe bool TryGetPointer(out System.Drawing.Point point)
{ {
fixed (Point* lpPointLocal = &point) fixed (System.Drawing.Point* lpPointLocal = &point)
{ {
return PInvoke.GetPhysicalCursorPos(lpPointLocal); return PInvoke.GetPhysicalCursorPos(lpPointLocal);
} }
@@ -136,7 +136,7 @@ public class PointerMonitor(IMessenger messenger) :
private bool TryGetPointerLocation([MaybeNullWhen(false)] out PointerLocation location) private bool TryGetPointerLocation([MaybeNullWhen(false)] out PointerLocation location)
{ {
if (TryGetPointer(out Point point)) if (TryGetPointer(out System.Drawing.Point point))
{ {
location = new PointerLocation(point.X, point.Y); location = new PointerLocation(point.X, point.Y);
return true; return true;
+3
View File
@@ -16,7 +16,10 @@ public record Rect
} }
public int X { get; } public int X { get; }
public int Y { get; } public int Y { get; }
public int Width { get; } public int Width { get; }
public int Height { get; } public int Height { get; }
} }
+2 -2
View File
@@ -40,7 +40,7 @@ public class Taskbar(IMessenger messenger,
return state; return state;
} }
public IntPtr GetHandle() => WindowHelper.Find("Shell_TrayWnd"); public IntPtr GetHandle() => WindowHelper.FindWindow("Shell_TrayWnd");
public void Receive(WndProcEventArgs args) public void Receive(WndProcEventArgs args)
{ {
@@ -63,7 +63,7 @@ public class Taskbar(IMessenger messenger,
public void Receive(PointerMovedEventArgs args) public void Receive(PointerMovedEventArgs args)
{ {
nint taskbarHandle = GetHandle(); nint taskbarHandle = GetHandle();
if (WindowHelper.TryGetBounds(taskbarHandle, out Rect? rect)) if (WindowHelper.TryGetWindowBounds(taskbarHandle, out Rect? rect))
{ {
if (args.Location.IsWithinBounds(rect)) if (args.Location.IsWithinBounds(rect))
{ {
+2 -2
View File
@@ -49,7 +49,7 @@ public class TaskbarButtonMonitor :
taskListHandle = taskbarList.GetHandle(); taskListHandle = taskbarList.GetHandle();
taskListElement = clientUIAutomation.ElementFromHandle(taskListHandle); taskListElement = clientUIAutomation.ElementFromHandle(taskListHandle);
if (WindowHelper.TryGetBounds(taskListHandle, out Rect? rect)) if (WindowHelper.TryGetWindowBounds(taskListHandle, out Rect? rect))
{ {
taskbarRectCache = rect; taskbarRectCache = rect;
} }
@@ -60,7 +60,7 @@ public class TaskbarButtonMonitor :
private bool CheckDirtyTaskbarRegion() private bool CheckDirtyTaskbarRegion()
{ {
if (WindowHelper.TryGetBounds(taskListHandle, out Rect? rect)) if (WindowHelper.TryGetWindowBounds(taskListHandle, out Rect? rect))
{ {
if (taskbarRectCache?.Width != rect.Width || if (taskbarRectCache?.Width != rect.Width ||
taskbarRectCache?.Height != rect.Height) taskbarRectCache?.Height != rect.Height)
+3 -3
View File
@@ -5,8 +5,8 @@ public class TaskbarList(ITaskbar taskbar) :
{ {
public IntPtr GetHandle() public IntPtr GetHandle()
{ {
nint rebarHandle = WindowHelper.Find("ReBarWindow32", taskbar.GetHandle()); nint rebarHandle = WindowHelper.FindWindow("ReBarWindow32", taskbar.GetHandle());
nint taskHandle = WindowHelper.Find("MSTaskSwWClass", rebarHandle); nint taskHandle = WindowHelper.FindWindow("MSTaskSwWClass", rebarHandle);
return WindowHelper.Find("MSTaskListWClass", taskHandle); return WindowHelper.FindWindow("MSTaskListWClass", taskHandle);
} }
} }
+28 -19
View File
@@ -7,35 +7,44 @@ namespace Toolkit.Windows;
public class WindowHelper public class WindowHelper
{ {
public static IntPtr GetHandle(string windowName) => PInvoke.FindWindow(windowName, null); public static IntPtr FindWindow(string name) =>
PInvoke.FindWindow(name, null);
public static uint GetDpi(IntPtr handle) => PInvoke.GetDpiForWindow((HWND)handle); public static IntPtr FindWindow(string name, IntPtr handle) =>
PInvoke.FindWindowEx(new HWND(handle), new HWND(), name, null);
public static void BringToForeground(HWND handle) public static uint GetDpi(IntPtr handle) =>
{ PInvoke.GetDpiForWindow((HWND)handle);
if (TryGetBoundsUnsafe(handle, out RECT bounds))
{
PInvoke.SetWindowPos(handle, new HWND(), bounds.left, bounds.top, bounds.right -
bounds.left, bounds.bottom - bounds.top, SET_WINDOW_POS_FLAGS.SWP_SHOWWINDOW | SET_WINDOW_POS_FLAGS.SWP_NOACTIVATE);
}
}
public static IntPtr Find(string windowName) => public static IntPtr GetWindowHandle(string name) =>
PInvoke.FindWindow(windowName, null); PInvoke.FindWindow(name, null);
public static IntPtr Find(string windowName, IntPtr parentHandle) => public static bool HideWindow(IntPtr hWnd) =>
PInvoke.FindWindowEx(new HWND(parentHandle), new HWND(), windowName, null); PInvoke.ShowWindow(new HWND(hWnd), SHOW_WINDOW_CMD.SW_HIDE);
public static void MoveAndResize(HWND handle, int x, int y, int width, int height) => public static void MoveAndResizeWindow(HWND handle, int x, int y, int width, int height) =>
PInvoke.SetWindowPos(handle, new HWND(), x, y, width, height, 0); PInvoke.SetWindowPos(handle, new HWND(), x, y, width, height, 0);
public static bool TryGetBounds(IntPtr handle, public static bool SetForegroundWindow(HWND handle)
{
if (TryGetWindowBoundsUnsafe(handle, out RECT bounds))
{
return PInvoke.SetWindowPos(handle, new HWND(), bounds.left, bounds.top, bounds.right -
bounds.left, bounds.bottom - bounds.top, SET_WINDOW_POS_FLAGS.SWP_SHOWWINDOW | SET_WINDOW_POS_FLAGS.SWP_NOACTIVATE) == 1;
}
return false;
}
public static bool ShowWindow(IntPtr hWnd) =>
PInvoke.ShowWindow(new HWND(hWnd), SHOW_WINDOW_CMD.SW_SHOW);
public static bool TryGetWindowBounds(IntPtr handle,
[MaybeNullWhen(false)] out Rect rect) [MaybeNullWhen(false)] out Rect rect)
{ {
if (TryGetBoundsUnsafe(handle, out RECT unsafeRect)) if (TryGetWindowBoundsUnsafe(handle, out RECT unsafeRect))
{ {
rect = new Rect(unsafeRect.left, unsafeRect.top, unsafeRect.right - unsafeRect.left, unsafeRect.bottom - unsafeRect.top); rect = new Rect(unsafeRect.left, unsafeRect.top, unsafeRect.right - unsafeRect.left, unsafeRect.bottom - unsafeRect.top);
return true; return true;
} }
@@ -43,7 +52,7 @@ public class WindowHelper
return false; return false;
} }
private static unsafe bool TryGetBoundsUnsafe(IntPtr handle, out RECT rect) private static unsafe bool TryGetWindowBoundsUnsafe(IntPtr handle, out RECT rect)
{ {
fixed (RECT* lpRectLocal = &rect) fixed (RECT* lpRectLocal = &rect)
{ {
+42 -37
View File
@@ -1,7 +1,7 @@
using CommunityToolkit.Mvvm.Messaging; using CommunityToolkit.Mvvm.Messaging;
using Toolkit.Foundation;
using Windows.Win32; using Windows.Win32;
using Windows.Win32.Foundation; using Windows.Win32.Foundation;
using Windows.Win32.Graphics.Gdi;
using Windows.Win32.UI.WindowsAndMessaging; using Windows.Win32.UI.WindowsAndMessaging;
namespace Toolkit.Windows; namespace Toolkit.Windows;
@@ -10,56 +10,61 @@ public class WndProc(IMessenger messenger) :
IWndProc IWndProc
{ {
private WNDPROC? handler; private WNDPROC? handler;
private bool isDisposed;
~WndProc()
{
Dispose(false);
}
public IntPtr Handle { get; private set; } public IntPtr Handle { get; private set; }
public void Dispose() public void Dispose()
{ {
Dispose(true);
GC.SuppressFinalize(this); GC.SuppressFinalize(this);
}
public void Initialize() => InitializeWndProc();
protected virtual void Dispose(bool disposing)
{
if (isDisposed)
{
return;
}
isDisposed = true;
PInvoke.DestroyWindow((HWND)Handle); PInvoke.DestroyWindow((HWND)Handle);
} }
private LRESULT HandleWndProc(HWND hWnd, uint msg,
WPARAM wParam, LPARAM lParam)
{
messenger.Send(new WndProcEventArgs(msg,
(uint)wParam.Value,
(uint)lParam.Value));
return PInvoke.DefWindowProc(hWnd, msg, wParam, lParam);
}
private unsafe void InitializeWndProc() private unsafe void InitializeWndProc()
{ {
string windowName = Guid.NewGuid().ToString(); string windowId = $"WndProc_Handler_{Guid.NewGuid()}";
handler = Wndproc; handler = HandleWndProc;
WNDCLASSW wndProcWindow; fixed (char* className = windowId)
wndProcWindow.style = 0;
wndProcWindow.lpfnWndProc = handler;
wndProcWindow.cbClsExtra = 0;
wndProcWindow.cbWndExtra = 0;
wndProcWindow.hInstance = new HINSTANCE();
wndProcWindow.hIcon = new HICON();
wndProcWindow.hCursor = new HCURSOR();
wndProcWindow.hbrBackground = new HBRUSH();
fixed (char* menuName = "")
{ {
wndProcWindow.lpszMenuName = new PCWSTR(menuName); WNDCLASSW wndCLass = new()
{
lpfnWndProc = handler,
lpszClassName = className,
};
_ = PInvoke.RegisterClass(wndCLass);
} }
fixed (char* className = windowName) Handle = PInvoke.CreateWindowEx(0, windowId, windowId, 0, 0, 0, 0, 0,
{ new HWND(IntPtr.Zero), null, null, null);
wndProcWindow.lpszClassName = new PCWSTR(className);
}
PInvoke.RegisterClass(wndProcWindow);
Handle = PInvoke.CreateWindowEx(0, wndProcWindow.lpszClassName,
new PCWSTR(), 0, 0, 0, 0, 0, new HWND(),
new HMENU(),
new HINSTANCE());
}
private LRESULT Wndproc(HWND param0, uint param1, WPARAM param2, LPARAM param3)
{
messenger.Send(new WndProcEventArgs(param1, (uint)param2.Value, (uint)param3.Value));
return PInvoke.DefWindowProc(param0, param1, param2, param3);
}
public void Initialize()
{
InitializeWndProc();
} }
} }