1+ using Microsoft . Win32 ;
2+ using System ;
3+ using System . Drawing ;
4+ using System . Drawing . Drawing2D ;
5+ using System . Runtime . InteropServices ;
6+ using System . Threading ;
7+ using System . Windows . Forms ;
8+ using ContextMenuManager . BluePointLilac . Controls ;
9+
10+ namespace BluePointLilac . Controls
11+ {
12+ public static class DarkModeHelper
13+ {
14+ [ DllImport ( "UXTheme.dll" , SetLastError = true , EntryPoint = "#138" ) ]
15+ public static extern bool CheckSystemDarkModeStatus ( ) ;
16+
17+ [ DllImport ( "DwmApi" ) ]
18+ private static extern int DwmSetWindowAttribute ( IntPtr hwnd , int attr , int [ ] attrValue , int attrSize ) ;
19+
20+ public static event EventHandler ThemeChanged ;
21+ private static SynchronizationContext uiContext ;
22+ public static Color MainColor = Color . FromArgb ( 255 , 143 , 31 ) ;
23+
24+ // 颜色属性
25+ public static Color TitleArea { get ; private set ; }
26+ public static Color FormBack { get ; private set ; }
27+ public static Color FormFore { get ; private set ; }
28+ public static Color FormBorder { get ; private set ; }
29+ public static Color ButtonMain { get ; private set ; }
30+ public static Color ButtonSecond { get ; private set ; }
31+ public static Color SideBarBackground { get ; private set ; }
32+ public static Color SideBarSeparator { get ; private set ; }
33+ public static Color SideBarHovered { get ; private set ; }
34+ public static Color ToolBarGradientTop { get ; private set ; }
35+ public static Color ToolBarGradientMiddle { get ; private set ; }
36+ public static Color ToolBarGradientBottom { get ; private set ; }
37+ public static Color StatusBarGradientTop { get ; private set ; }
38+ public static Color StatusBarGradientMiddle { get ; private set ; }
39+ public static Color StatusBarGradientBottom { get ; private set ; }
40+ public static Color SearchBoxBack { get ; private set ; }
41+ public static Color SearchBoxBorder { get ; private set ; }
42+ public static Color SearchBoxPlaceholder { get ; private set ; }
43+ public static Color ComboBoxBack { get ; private set ; }
44+ public static Color ComboBoxFore { get ; private set ; }
45+ public static Color ComboBoxBorder { get ; private set ; }
46+ public static Color ComboBoxArrow { get ; private set ; }
47+ public static Color ComboBoxHover { get ; private set ; }
48+ public static Color ComboBoxSelected { get ; private set ; }
49+
50+ private static bool _isDarkTheme = false ;
51+ public static bool IsDarkTheme => _isDarkTheme ;
52+ private static bool _listeningForThemeChanges = false ;
53+
54+ public static void Initialize ( )
55+ {
56+ uiContext = SynchronizationContext . Current ;
57+ if ( uiContext == null && Application . MessageLoop )
58+ {
59+ uiContext = new WindowsFormsSynchronizationContext ( ) ;
60+ }
61+
62+ UpdateTheme ( ) ;
63+ StartListeningForThemeChanges ( ) ;
64+ }
65+
66+ private static void StartListeningForThemeChanges ( )
67+ {
68+ if ( ! _listeningForThemeChanges )
69+ {
70+ SystemEvents . UserPreferenceChanged += OnSystemPreferencesChanged ;
71+ _listeningForThemeChanges = true ;
72+ }
73+ }
74+
75+ public static void StopListening ( )
76+ {
77+ if ( _listeningForThemeChanges )
78+ {
79+ SystemEvents . UserPreferenceChanged -= OnSystemPreferencesChanged ;
80+ _listeningForThemeChanges = false ;
81+ }
82+ }
83+
84+ public static bool IsDarkThemeEnabled ( )
85+ {
86+ try
87+ {
88+ using ( var key = Registry . CurrentUser . OpenSubKey (
89+ @"Software\Microsoft\Windows\CurrentVersion\Themes\Personalize" ) )
90+ {
91+ var value = key ? . GetValue ( "AppsUseLightTheme" ) ;
92+ return value != null && ( int ) value == 0 ;
93+ }
94+ }
95+ catch
96+ {
97+ try { return CheckSystemDarkModeStatus ( ) ; }
98+ catch { return false ; }
99+ }
100+ }
101+
102+ public static bool UpdateTheme ( )
103+ {
104+ bool newDarkTheme = IsDarkThemeEnabled ( ) ;
105+ bool changed = _isDarkTheme != newDarkTheme ;
106+ _isDarkTheme = newDarkTheme ;
107+
108+ UpdateAllColors ( _isDarkTheme ) ;
109+
110+ if ( changed )
111+ {
112+ if ( uiContext != null )
113+ {
114+ uiContext . Post ( _ => SafeInvokeThemeChanged ( ) , null ) ;
115+ }
116+ else
117+ {
118+ SafeInvokeThemeChanged ( ) ;
119+ }
120+ }
121+
122+ return changed ;
123+ }
124+
125+ public static void ApplyDarkModeToForm ( Form form )
126+ {
127+ if ( _isDarkTheme && form . IsHandleCreated )
128+ {
129+ try { DwmSetWindowAttribute ( form . Handle , 20 , new [ ] { 1 } , 4 ) ; }
130+ catch { /* 忽略API错误 */ }
131+ }
132+ }
133+
134+ private static void UpdateAllColors ( bool isDark )
135+ {
136+ if ( isDark ) SetDarkModeColors ( ) ;
137+ else SetLightModeColors ( ) ;
138+ }
139+
140+ private static void SetDarkModeColors ( )
141+ {
142+ TitleArea = Color . FromArgb ( 255 , 32 , 32 , 32 ) ;
143+ FormBack = Color . FromArgb ( 255 , 28 , 28 , 28 ) ;
144+ FormFore = Color . FromArgb ( 255 , 240 , 240 , 240 ) ;
145+ FormBorder = Color . FromArgb ( 255 , 50 , 50 , 50 ) ;
146+ ButtonMain = Color . FromArgb ( 255 , 55 , 55 , 55 ) ;
147+ ButtonSecond = Color . FromArgb ( 255 , 38 , 38 , 38 ) ;
148+ SideBarBackground = Color . FromArgb ( 255 , 26 , 26 , 26 ) ;
149+ SideBarSeparator = Color . FromArgb ( 255 , 64 , 64 , 64 ) ;
150+ SideBarHovered = Color . FromArgb ( 255 , 51 , 51 , 51 ) ;
151+ ToolBarGradientTop = Color . FromArgb ( 255 , 128 , 128 , 128 ) ;
152+ ToolBarGradientMiddle = Color . FromArgb ( 255 , 56 , 56 , 56 ) ;
153+ ToolBarGradientBottom = Color . FromArgb ( 255 , 128 , 128 , 128 ) ;
154+ StatusBarGradientTop = Color . FromArgb ( 255 , 128 , 128 , 128 ) ;
155+ StatusBarGradientMiddle = Color . FromArgb ( 255 , 56 , 56 , 56 ) ;
156+ StatusBarGradientBottom = Color . FromArgb ( 255 , 128 , 128 , 128 ) ;
157+ SearchBoxBack = Color . FromArgb ( 255 , 45 , 45 , 45 ) ;
158+ SearchBoxBorder = Color . FromArgb ( 255 , 80 , 80 , 80 ) ;
159+ SearchBoxPlaceholder = Color . FromArgb ( 255 , 150 , 150 , 150 ) ;
160+ ComboBoxBack = Color . FromArgb ( 255 , 45 , 45 , 48 ) ;
161+ ComboBoxFore = Color . FromArgb ( 255 , 245 , 245 , 245 ) ;
162+ ComboBoxBorder = Color . FromArgb ( 255 , 70 , 70 , 75 ) ;
163+ ComboBoxArrow = Color . FromArgb ( 255 , 200 , 200 , 200 ) ;
164+ ComboBoxHover = SideBarHovered ;
165+ ComboBoxSelected = MainColor ;
166+ }
167+
168+ private static void SetLightModeColors ( )
169+ {
170+ TitleArea = Color . FromArgb ( 255 , 243 , 243 , 243 ) ;
171+ FormBack = SystemColors . Control ;
172+ FormFore = SystemColors . ControlText ;
173+ FormBorder = Color . LightGray ;
174+ ButtonMain = SystemColors . ControlLightLight ;
175+ ButtonSecond = SystemColors . ControlLight ;
176+ SideBarBackground = SystemColors . Control ;
177+ SideBarSeparator = Color . FromArgb ( 255 , 200 , 200 , 200 ) ;
178+ SideBarHovered = Color . FromArgb ( 255 , 230 , 230 , 230 ) ;
179+ ToolBarGradientTop = Color . FromArgb ( 255 , 255 , 255 , 255 ) ;
180+ ToolBarGradientMiddle = Color . FromArgb ( 255 , 230 , 230 , 230 ) ;
181+ ToolBarGradientBottom = Color . FromArgb ( 255 , 255 , 255 , 255 ) ;
182+ StatusBarGradientTop = Color . FromArgb ( 255 , 255 , 255 , 255 ) ;
183+ StatusBarGradientMiddle = Color . FromArgb ( 255 , 230 , 230 , 230 ) ;
184+ StatusBarGradientBottom = Color . FromArgb ( 255 , 255 , 255 , 255 ) ;
185+ SearchBoxBack = Color . White ;
186+ SearchBoxBorder = Color . FromArgb ( 255 , 200 , 200 , 200 ) ;
187+ SearchBoxPlaceholder = Color . FromArgb ( 255 , 120 , 120 , 120 ) ;
188+ ComboBoxBack = Color . FromArgb ( 255 , 250 , 250 , 252 ) ;
189+ ComboBoxFore = Color . FromArgb ( 255 , 25 , 25 , 25 ) ;
190+ ComboBoxBorder = Color . FromArgb ( 255 , 210 , 210 , 215 ) ;
191+ ComboBoxArrow = Color . FromArgb ( 255 , 100 , 100 , 100 ) ;
192+ ComboBoxHover = SideBarHovered ;
193+ ComboBoxSelected = MainColor ;
194+ }
195+
196+ public static Color GetBorderColor ( bool isFocused = false )
197+ {
198+ return isFocused ? MainColor : ( IsDarkTheme ?
199+ Color . FromArgb ( 255 , 80 , 80 , 80 ) :
200+ Color . FromArgb ( 255 , 200 , 200 , 200 ) ) ;
201+ }
202+
203+ public static Color GetPlaceholderColor ( )
204+ {
205+ return IsDarkTheme ?
206+ Color . FromArgb ( 255 , 150 , 150 , 150 ) :
207+ Color . FromArgb ( 255 , 120 , 120 , 120 ) ;
208+ }
209+
210+ private static void OnSystemPreferencesChanged ( object sender , UserPreferenceChangedEventArgs e )
211+ {
212+ if ( e . Category == UserPreferenceCategory . General )
213+ {
214+ if ( uiContext != null )
215+ {
216+ uiContext . Post ( _ => UpdateTheme ( ) , null ) ;
217+ }
218+ else
219+ {
220+ UpdateTheme ( ) ;
221+ }
222+ }
223+ }
224+
225+ public static void AdjustControlColors ( Control control )
226+ {
227+ if ( control == null || control . IsDisposed ) return ;
228+
229+ if ( control . InvokeRequired )
230+ {
231+ try { control . Invoke ( new Action ( ( ) => AdjustControlColors ( control ) ) ) ; }
232+ catch { return ; }
233+ return ;
234+ }
235+
236+ if ( control . IsDisposed ) return ;
237+
238+ foreach ( Control child in control . Controls )
239+ AdjustControlColors ( child ) ;
240+
241+ try
242+ {
243+ string typeName = control . GetType ( ) . FullName ;
244+
245+ if ( typeName == "BluePointLilac.Controls.MyListBox" ||
246+ typeName == "BluePointLilac.Controls.MyListItem" )
247+ {
248+ control . BackColor = FormBack ;
249+ control . ForeColor = FormFore ;
250+ }
251+ else if ( typeName == "BluePointLilac.Controls.MyToolBar" )
252+ {
253+ control . BackColor = TitleArea ;
254+ control . ForeColor = FormFore ;
255+ }
256+ else if ( typeName == "BluePointLilac.Controls.MyToolBarButton" )
257+ {
258+ control . ForeColor = FormFore ;
259+ }
260+ else if ( typeName == "BluePointLilac.Controls.MySideBar" )
261+ {
262+ control . BackColor = ButtonSecond ;
263+ control . ForeColor = FormFore ;
264+ }
265+ else if ( typeName == "BluePointLilac.Controls.MyStatusBar" )
266+ {
267+ control . BackColor = ButtonMain ;
268+ control . ForeColor = FormFore ;
269+ }
270+ else if ( control is RComboBox combo )
271+ {
272+ if ( combo . InvokeRequired )
273+ combo . Invoke ( new Action ( ( ) => combo . UpdateColors ( ) ) ) ;
274+ else
275+ combo . UpdateColors ( ) ;
276+ }
277+ else if ( control is SearchBox searchBox )
278+ {
279+ searchBox . Invoke ( new Action ( ( ) =>
280+ {
281+ var method = searchBox . GetType ( ) . GetMethod ( "UpdateThemeColors" ) ;
282+ method ? . Invoke ( searchBox , null ) ;
283+ } ) ) ;
284+ }
285+ }
286+ catch { /* 忽略错误 */ }
287+ }
288+
289+ public static GraphicsPath CreateRoundedRectanglePath ( Rectangle rect , int radius )
290+ {
291+ var path = new GraphicsPath ( ) ;
292+ if ( radius <= 0 )
293+ {
294+ path . AddRectangle ( rect ) ;
295+ return path ;
296+ }
297+
298+ int diameter = radius * 2 ;
299+ var arc = new Rectangle ( rect . Location , new Size ( diameter , diameter ) ) ;
300+
301+ path . AddArc ( arc , 180 , 90 ) ;
302+ arc . X = rect . Right - diameter ;
303+ path . AddArc ( arc , 270 , 90 ) ;
304+ arc . Y = rect . Bottom - diameter ;
305+ path . AddArc ( arc , 0 , 90 ) ;
306+ arc . X = rect . Left ;
307+ path . AddArc ( arc , 90 , 90 ) ;
308+
309+ path . CloseFigure ( ) ;
310+ return path ;
311+ }
312+
313+ private static void SafeInvokeThemeChanged ( )
314+ {
315+ try { ThemeChanged ? . Invoke ( null , EventArgs . Empty ) ; }
316+ catch { /* 忽略异常 */ }
317+ }
318+ }
319+ }
0 commit comments