11using System . Runtime . InteropServices ;
2+ using System . Threading ;
23using CodingWithCalvin . VSToolbox . Services ;
34using Microsoft . UI ;
45using Microsoft . UI . Windowing ;
78
89namespace CodingWithCalvin . VSToolbox ;
910
11+ // Windows API for setting window corner preference
12+ internal static class NativeMethods
13+ {
14+ [ DllImport ( "dwmapi.dll" ) ]
15+ internal static extern int DwmSetWindowAttribute ( IntPtr hwnd , int attr , ref int attrValue , int attrSize ) ;
16+
17+ [ DllImport ( "user32.dll" ) ]
18+ internal static extern int GetWindowLong ( IntPtr hwnd , int nIndex ) ;
19+
20+ [ DllImport ( "user32.dll" ) ]
21+ internal static extern int SetWindowLong ( IntPtr hwnd , int nIndex , int dwNewLong ) ;
22+
23+ [ DllImport ( "user32.dll" ) ]
24+ internal static extern bool SetWindowPos ( IntPtr hwnd , IntPtr hwndInsertAfter , int x , int y , int cx , int cy , uint uFlags ) ;
25+
26+ internal const int DWMWA_WINDOW_CORNER_PREFERENCE = 33 ;
27+ internal const int DWMWCP_DONOTROUND = 1 ;
28+ internal const int DWMWA_CAPTION_COLOR = 35 ;
29+ internal const int DWMWA_BORDER_COLOR = 34 ;
30+
31+ internal const int GWL_STYLE = - 16 ;
32+ internal const int WS_CAPTION = 0x00C00000 ;
33+ internal const int WS_THICKFRAME = 0x00040000 ;
34+ internal const uint SWP_FRAMECHANGED = 0x0020 ;
35+ internal const uint SWP_NOMOVE = 0x0002 ;
36+ internal const uint SWP_NOSIZE = 0x0001 ;
37+ internal const uint SWP_NOZORDER = 0x0004 ;
38+
39+ // For finding and showing existing window
40+ internal delegate bool EnumWindowsProc ( IntPtr hWnd , IntPtr lParam ) ;
41+
42+ [ DllImport ( "user32.dll" ) ]
43+ internal static extern bool EnumWindows ( EnumWindowsProc lpEnumFunc , IntPtr lParam ) ;
44+
45+ [ DllImport ( "user32.dll" , CharSet = CharSet . Unicode ) ]
46+ internal static extern int GetWindowText ( IntPtr hWnd , System . Text . StringBuilder lpString , int nMaxCount ) ;
47+
48+ [ DllImport ( "user32.dll" ) ]
49+ internal static extern bool IsWindowVisible ( IntPtr hWnd ) ;
50+
51+ [ DllImport ( "user32.dll" ) ]
52+ internal static extern bool ShowWindow ( IntPtr hWnd , int nCmdShow ) ;
53+
54+ [ DllImport ( "user32.dll" ) ]
55+ internal static extern bool SetForegroundWindow ( IntPtr hWnd ) ;
56+
57+ internal const int SW_RESTORE = 9 ;
58+ internal const int SW_SHOW = 5 ;
59+ }
60+
1061public partial class App : Application
1162{
63+ private const string MutexName = "CodingWithCalvin.VSToolbox.SingleInstance" ;
64+ private static Mutex ? _mutex ;
1265 private Window ? _window ;
1366 private AppWindow ? _appWindow ;
1467 private TrayIconService ? _trayIconService ;
1568
69+ public Window ? MainWindow => _window ;
70+
1671 public App ( )
1772 {
1873 InitializeComponent ( ) ;
1974 }
2075
2176 protected override void OnLaunched ( LaunchActivatedEventArgs e )
2277 {
78+ // Check for single instance
79+ _mutex = new Mutex ( true , MutexName , out var createdNew ) ;
80+ if ( ! createdNew )
81+ {
82+ // Another instance is already running - try to bring it to front
83+ BringExistingInstanceToFront ( ) ;
84+ Environment . Exit ( 0 ) ;
85+ return ;
86+ }
87+
2388 _window = new Window
2489 {
2590 Title = "Visual Studio Toolbox"
@@ -37,6 +102,9 @@ protected override void OnLaunched(LaunchActivatedEventArgs e)
37102 _appWindow . SetIcon ( iconPath ) ;
38103 }
39104
105+ // Configure custom title bar with square corners
106+ ConfigureCustomTitleBar ( ) ;
107+
40108 // Set up the main content
41109 var rootFrame = new Frame ( ) ;
42110 rootFrame . NavigationFailed += OnNavigationFailed ;
@@ -57,6 +125,41 @@ protected override void OnLaunched(LaunchActivatedEventArgs e)
57125 _window . Activate ( ) ;
58126 }
59127
128+ private void ConfigureCustomTitleBar ( )
129+ {
130+ if ( _appWindow is null || _window is null ) return ;
131+
132+ // Get the window handle for native API calls
133+ var hwnd = WinRT . Interop . WindowNative . GetWindowHandle ( _window ) ;
134+
135+ // Set square corners using DWM API
136+ var cornerPreference = NativeMethods . DWMWCP_DONOTROUND ;
137+ NativeMethods . DwmSetWindowAttribute ( hwnd , NativeMethods . DWMWA_WINDOW_CORNER_PREFERENCE ,
138+ ref cornerPreference , sizeof ( int ) ) ;
139+
140+ // Set caption and border color to purple (#68217A = 0x007A2168 in COLORREF BGR format)
141+ var purpleColor = 0x007A2168 ; // BGR format for #68217A
142+ NativeMethods . DwmSetWindowAttribute ( hwnd , NativeMethods . DWMWA_CAPTION_COLOR ,
143+ ref purpleColor , sizeof ( int ) ) ;
144+ NativeMethods . DwmSetWindowAttribute ( hwnd , NativeMethods . DWMWA_BORDER_COLOR ,
145+ ref purpleColor , sizeof ( int ) ) ;
146+
147+ // Remove the caption from window style to eliminate the title bar area
148+ var style = NativeMethods . GetWindowLong ( hwnd , NativeMethods . GWL_STYLE ) ;
149+ style &= ~ NativeMethods . WS_CAPTION ; // Remove caption
150+ NativeMethods . SetWindowLong ( hwnd , NativeMethods . GWL_STYLE , style ) ;
151+ NativeMethods . SetWindowPos ( hwnd , IntPtr . Zero , 0 , 0 , 0 , 0 ,
152+ NativeMethods . SWP_FRAMECHANGED | NativeMethods . SWP_NOMOVE | NativeMethods . SWP_NOSIZE | NativeMethods . SWP_NOZORDER ) ;
153+
154+ // Make window borderless (no system title bar at all)
155+ if ( _appWindow . Presenter is OverlappedPresenter presenter )
156+ {
157+ presenter . SetBorderAndTitleBar ( false , false ) ;
158+ presenter . IsResizable = true ;
159+ presenter . IsMaximizable = false ;
160+ }
161+ }
162+
60163 private void PositionWindowBottomRight ( int width , int height )
61164 {
62165 if ( _appWindow is null ) return ;
@@ -86,4 +189,29 @@ private void OnNavigationFailed(object sender, NavigationFailedEventArgs e)
86189 {
87190 throw new InvalidOperationException ( $ "Failed to load Page { e . SourcePageType . FullName } ") ;
88191 }
192+
193+ private static void BringExistingInstanceToFront ( )
194+ {
195+ const string windowTitle = "Visual Studio Toolbox" ;
196+ IntPtr foundWindow = IntPtr . Zero ;
197+
198+ NativeMethods . EnumWindows ( ( hWnd , lParam ) =>
199+ {
200+ var sb = new System . Text . StringBuilder ( 256 ) ;
201+ NativeMethods . GetWindowText ( hWnd , sb , sb . Capacity ) ;
202+ if ( sb . ToString ( ) == windowTitle )
203+ {
204+ foundWindow = hWnd ;
205+ return false ; // Stop enumeration
206+ }
207+ return true ; // Continue enumeration
208+ } , IntPtr . Zero ) ;
209+
210+ if ( foundWindow != IntPtr . Zero )
211+ {
212+ // Show and bring the window to front
213+ NativeMethods . ShowWindow ( foundWindow , NativeMethods . SW_RESTORE ) ;
214+ NativeMethods . SetForegroundWindow ( foundWindow ) ;
215+ }
216+ }
89217}
0 commit comments