1 //
2 // VisualStyleRenderer.cs
3 //
4 // Permission is hereby granted, free of charge, to any person obtaining
5 // a copy of this software and associated documentation files (the
6 // "Software"), to deal in the Software without restriction, including
7 // without limitation the rights to use, copy, modify, merge, publish,
8 // distribute, sublicense, and/or sell copies of the Software, and to
9 // permit persons to whom the Software is furnished to do so, subject to
10 // the following conditions:
11 //
12 // The above copyright notice and this permission notice shall be
13 // included in all copies or substantial portions of the Software.
14 //
15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 //
23 // Copyright (c) 2006 Novell, Inc.
24 //
25 // Authors:
26 //	Jonathan Pobst (monkey@jpobst.com)
27 //
28 
29 using System.Drawing;
30 
31 namespace System.Windows.Forms.VisualStyles
32 {
33 	public sealed class VisualStyleRenderer
34 	{
35 		private string class_name;
36 		private int part;
37 		private int state;
38 		private IntPtr theme;
39 		private int last_hresult = 0;
40 		private ThemeHandleManager theme_handle_manager = new ThemeHandleManager ();
41 
42 		#region Public Constructors
VisualStyleRenderer(string className, int part, int state)43 		public VisualStyleRenderer (string className, int part, int state)
44 		{
45 			theme_handle_manager.VisualStyleRenderer = this;
46 			this.SetParameters (className, part, state);
47 		}
48 
VisualStyleRenderer(VisualStyleElement element)49 		public VisualStyleRenderer (VisualStyleElement element)
50 			: this (element.ClassName, element.Part, element.State) {
51 		}
52 		#endregion
53 
54 		#region Public Properties
55 		public String Class { get { return this.class_name; } }
56 		public IntPtr Handle { get { return this.theme; } }
57 		public int LastHResult { get { return this.last_hresult; } }
58 		public int Part { get { return this.part; } }
59 		public int State { get { return this.state; } }
60 
61 		public static bool IsSupported {
62 			get {
63 				if (!VisualStyleInformation.IsEnabledByUser)
64 					return false;
65 
66 				if (Application.VisualStyleState == VisualStyleState.ClientAndNonClientAreasEnabled ||
67 					Application.VisualStyleState == VisualStyleState.ClientAreaEnabled)
68 						return true;
69 
70 				return false;
71 			}
72 		}
73 		#endregion
74 
75 		#region Public Static Methods
IsElementDefined(VisualStyleElement element)76 		public static bool IsElementDefined (VisualStyleElement element)
77 		{
78 			if (!IsSupported)
79 				throw new InvalidOperationException ("Visual Styles are not enabled.");
80 
81 			if (IsElementKnownToBeSupported (element.ClassName, element.Part, element.State))
82 				return true;
83 
84 			IntPtr theme = VisualStyles.UxThemeOpenThemeData (IntPtr.Zero, element.ClassName);
85 			if (theme == IntPtr.Zero)
86 				return false;
87 			bool retval = VisualStyles.UxThemeIsThemePartDefined (theme, element.Part);
88 			VisualStyles.UxThemeCloseThemeData (theme);
89 
90 			return retval;
91 		}
92 		#endregion
93 
94 		#region Public Instance Methods
DrawBackground(IDeviceContext dc, Rectangle bounds)95 		public void DrawBackground (IDeviceContext dc, Rectangle bounds)
96 		{
97 			if (dc == null)
98 				throw new ArgumentNullException ("dc");
99 
100 			last_hresult = VisualStyles.UxThemeDrawThemeBackground (theme, dc, this.part, this.state, bounds);
101 		}
102 
DrawBackground(IDeviceContext dc, Rectangle bounds, Rectangle clipRectangle)103 		public void DrawBackground (IDeviceContext dc, Rectangle bounds, Rectangle clipRectangle)
104 		{
105 			if (dc == null)
106 				throw new ArgumentNullException ("dc");
107 
108 			last_hresult = VisualStyles.UxThemeDrawThemeBackground (theme, dc, this.part, this.state, bounds, clipRectangle);
109 		}
110 
DrawEdge(IDeviceContext dc, Rectangle bounds, Edges edges, EdgeStyle style, EdgeEffects effects)111 		public Rectangle DrawEdge (IDeviceContext dc, Rectangle bounds, Edges edges, EdgeStyle style, EdgeEffects effects)
112 		{
113 			if (dc == null)
114 				throw new ArgumentNullException ("dc");
115 
116 			Rectangle result;
117 			last_hresult = VisualStyles.UxThemeDrawThemeEdge (theme, dc, this.part, this.state, bounds, edges, style, effects, out result);
118 			return result;
119 		}
120 
DrawImage(Graphics g, Rectangle bounds, ImageList imageList, int imageIndex)121 		public void DrawImage (Graphics g, Rectangle bounds, ImageList imageList, int imageIndex)
122 		{
123 			if (g == null)
124 				throw new ArgumentNullException ("g");
125 			if (imageIndex < 0 || imageIndex > imageList.Images.Count - 1)
126 				throw new ArgumentOutOfRangeException ("imageIndex");
127 			if (imageList.Images[imageIndex] == null)
128 				throw new ArgumentNullException ("imageIndex");
129 
130 			g.DrawImage (imageList.Images[imageIndex], bounds);
131 		}
132 
DrawImage(Graphics g, Rectangle bounds, Image image)133 		public void DrawImage (Graphics g, Rectangle bounds, Image image)
134 		{
135 			if (g == null)
136 				throw new ArgumentNullException ("g");
137 			if (image == null)
138 				throw new ArgumentNullException ("image");
139 
140 			g.DrawImage (image, bounds);
141 		}
142 
DrawParentBackground(IDeviceContext dc, Rectangle bounds, Control childControl)143 		public void DrawParentBackground (IDeviceContext dc, Rectangle bounds, Control childControl)
144 		{
145 			if (dc == null)
146 				throw new ArgumentNullException ("dc");
147 
148 			last_hresult = VisualStyles.UxThemeDrawThemeParentBackground (dc, bounds, childControl);
149 		}
150 
DrawText(IDeviceContext dc, Rectangle bounds, string textToDraw, bool drawDisabled, TextFormatFlags flags)151 		public void DrawText (IDeviceContext dc, Rectangle bounds, string textToDraw, bool drawDisabled, TextFormatFlags flags)
152 		{
153 			if (dc == null)
154 				throw new ArgumentNullException ("dc");
155 
156 			last_hresult = VisualStyles.UxThemeDrawThemeText (theme, dc, this.part, this.state, textToDraw, flags, bounds);
157 		}
158 
DrawText(IDeviceContext dc, Rectangle bounds, string textToDraw, bool drawDisabled)159 		public void DrawText (IDeviceContext dc, Rectangle bounds, string textToDraw, bool drawDisabled)
160 		{
161 			this.DrawText (dc, bounds, textToDraw, drawDisabled, TextFormatFlags.Default);
162 		}
163 
DrawText(IDeviceContext dc, Rectangle bounds, string textToDraw)164 		public void DrawText (IDeviceContext dc, Rectangle bounds, string textToDraw)
165 		{
166 			this.DrawText (dc, bounds, textToDraw, false, TextFormatFlags.Default);
167 		}
168 
GetBackgroundContentRectangle(IDeviceContext dc, Rectangle bounds)169 		public Rectangle GetBackgroundContentRectangle (IDeviceContext dc, Rectangle bounds)
170 		{
171 			if (dc == null)
172 				throw new ArgumentNullException ("dc");
173 
174 			Rectangle result;
175 			last_hresult = VisualStyles.UxThemeGetThemeBackgroundContentRect (theme, dc, this.part, this.state, bounds, out result);
176 			return result;
177 		}
178 
GetBackgroundExtent(IDeviceContext dc, Rectangle contentBounds)179 		public Rectangle GetBackgroundExtent (IDeviceContext dc, Rectangle contentBounds)
180 		{
181 			if (dc == null)
182 				throw new ArgumentNullException ("dc");
183 
184 			Rectangle result;
185 			last_hresult = VisualStyles.UxThemeGetThemeBackgroundExtent (theme, dc, this.part, this.state, contentBounds, out result);
186 			return result;
187 		}
188 
189 		[System.Security.SuppressUnmanagedCodeSecurity]
GetBackgroundRegion(IDeviceContext dc, Rectangle bounds)190 		public Region GetBackgroundRegion (IDeviceContext dc, Rectangle bounds)
191 		{
192 			if (dc == null)
193 				throw new ArgumentNullException ("dc");
194 
195 			Region result;
196 			last_hresult = VisualStyles.UxThemeGetThemeBackgroundRegion (theme, dc, this.part, this.state, bounds, out result);
197 			return result;
198 		}
199 
GetBoolean(BooleanProperty prop)200 		public bool GetBoolean (BooleanProperty prop)
201 		{
202 			if (!Enum.IsDefined (typeof (BooleanProperty), prop))
203 				throw new System.ComponentModel.InvalidEnumArgumentException ("prop", (int)prop, typeof (BooleanProperty));
204 
205 			bool result;
206 			last_hresult = VisualStyles.UxThemeGetThemeBool (theme, this.part, this.state, prop, out result);
207 			return result;
208 		}
209 
GetColor(ColorProperty prop)210 		public Color GetColor (ColorProperty prop)
211 		{
212 			if (!Enum.IsDefined (typeof (ColorProperty), prop))
213 				throw new System.ComponentModel.InvalidEnumArgumentException ("prop", (int)prop, typeof (ColorProperty));
214 
215 			Color result;
216 			last_hresult = VisualStyles.UxThemeGetThemeColor (theme, this.part, this.state, prop, out result);
217 			return result;
218 		}
219 
GetEnumValue(EnumProperty prop)220 		public int GetEnumValue (EnumProperty prop)
221 		{
222 			if (!Enum.IsDefined (typeof (EnumProperty), prop))
223 				throw new System.ComponentModel.InvalidEnumArgumentException ("prop", (int)prop, typeof (EnumProperty));
224 
225 			int result;
226 			last_hresult = VisualStyles.UxThemeGetThemeEnumValue (theme, this.part, this.state, prop, out result);
227 			return result;
228 		}
229 
GetFilename(FilenameProperty prop)230 		public string GetFilename (FilenameProperty prop)
231 		{
232 			if (!Enum.IsDefined (typeof (FilenameProperty), prop))
233 				throw new System.ComponentModel.InvalidEnumArgumentException ("prop", (int)prop, typeof (FilenameProperty));
234 
235 			string result;
236 			last_hresult = VisualStyles.UxThemeGetThemeFilename (theme, this.part, this.state, prop, out result);
237 			return result;
238 
239 		}
240 
241 		[MonoTODO(@"I can't get MS's to return anything but null, so I can't really get this one right")]
GetFont(IDeviceContext dc, FontProperty prop)242 		public Font GetFont (IDeviceContext dc, FontProperty prop)
243 		{
244 			throw new NotImplementedException();
245 			//if (dc == null)
246 			//        throw new ArgumentNullException ("dc");
247 			//if (!Enum.IsDefined (typeof (FontProperty), prop))
248 			//        throw new System.ComponentModel.InvalidEnumArgumentException ("prop", (int)prop, typeof (FontProperty));
249 
250 			//UXTheme.LOGFONT lf = new UXTheme.LOGFONT();
251 
252 			//UXTheme.GetThemeFont (theme, dc.GetHdc (), this.part, this.state, (int)prop, out lf);
253 			//IntPtr fontPtr = UXTheme.CreateFontIndirect(lf);
254 			//dc.ReleaseHdc();
255 
256 			//return Font.FromLogFont(lf);
257 			//return null;
258 		}
259 
GetInteger(IntegerProperty prop)260 		public int GetInteger (IntegerProperty prop)
261 		{
262 			if (!Enum.IsDefined (typeof (IntegerProperty), prop))
263 				throw new System.ComponentModel.InvalidEnumArgumentException ("prop", (int)prop, typeof (IntegerProperty));
264 
265 			int result;
266 			last_hresult = VisualStyles.UxThemeGetThemeInt (theme, this.part, this.state, prop, out result);
267 			return result;
268 		}
269 
270 		[MonoTODO(@"MS's causes a PInvokeStackUnbalance on me, so this is not verified against MS.")]
GetMargins(IDeviceContext dc, MarginProperty prop)271 		public Padding GetMargins (IDeviceContext dc, MarginProperty prop)
272 		{
273 			if (dc == null)
274 				throw new ArgumentNullException ("dc");
275 			if (!Enum.IsDefined (typeof (MarginProperty), prop))
276 				throw new System.ComponentModel.InvalidEnumArgumentException ("prop", (int)prop, typeof (MarginProperty));
277 
278 			Padding result;
279 			last_hresult = VisualStyles.UxThemeGetThemeMargins (theme, dc, this.part, this.state, prop, out result);
280 			return result;
281 		}
282 
GetPartSize(IDeviceContext dc, Rectangle bounds, ThemeSizeType type)283 		public Size GetPartSize (IDeviceContext dc, Rectangle bounds, ThemeSizeType type)
284 		{
285 			if (dc == null)
286 				throw new ArgumentNullException ("dc");
287 			if (!Enum.IsDefined (typeof (ThemeSizeType), type))
288 				throw new System.ComponentModel.InvalidEnumArgumentException ("prop", (int)type, typeof (ThemeSizeType));
289 
290 			Size result;
291 			last_hresult = VisualStyles.UxThemeGetThemePartSize (theme, dc, this.part, this.state, bounds, type, out result);
292 			return result;
293 		}
294 
GetPartSize(IDeviceContext dc, ThemeSizeType type)295 		public Size GetPartSize (IDeviceContext dc, ThemeSizeType type)
296 		{
297 			if (dc == null)
298 				throw new ArgumentNullException ("dc");
299 			if (!Enum.IsDefined (typeof (ThemeSizeType), type))
300 				throw new System.ComponentModel.InvalidEnumArgumentException ("prop", (int)type, typeof (ThemeSizeType));
301 
302 			Size result;
303 			last_hresult = VisualStyles.UxThemeGetThemePartSize (theme, dc, this.part, this.state, type, out result);
304 			return result;
305 		}
306 
GetPoint(PointProperty prop)307 		public Point GetPoint (PointProperty prop)
308 		{
309 			if (!Enum.IsDefined (typeof (PointProperty), prop))
310 				throw new System.ComponentModel.InvalidEnumArgumentException ("prop", (int)prop, typeof (PointProperty));
311 
312 			Point result;
313 			last_hresult = VisualStyles.UxThemeGetThemePosition (theme, this.part, this.state, prop, out result);
314 			return result;
315 		}
316 
317 		[MonoTODO(@"Can't find any values that return anything on MS to test against")]
GetString(StringProperty prop)318 		public string GetString (StringProperty prop)
319 		{
320 			if (!Enum.IsDefined (typeof (StringProperty), prop))
321 				throw new System.ComponentModel.InvalidEnumArgumentException ("prop", (int)prop, typeof (StringProperty));
322 
323 			string result;
324 			last_hresult = VisualStyles.UxThemeGetThemeString (theme, this.part, this.state, prop, out result);
325 			return result;
326 		}
327 
GetTextExtent(IDeviceContext dc, Rectangle bounds, string textToDraw, TextFormatFlags flags)328 		public Rectangle GetTextExtent (IDeviceContext dc, Rectangle bounds, string textToDraw, TextFormatFlags flags)
329 		{
330 			if (dc == null)
331 				throw new ArgumentNullException ("dc");
332 
333 			Rectangle result;
334 			last_hresult = VisualStyles.UxThemeGetThemeTextExtent (theme, dc, this.part, this.state, textToDraw, flags, bounds, out result);
335 			return result;
336 		}
337 
GetTextExtent(IDeviceContext dc, string textToDraw, TextFormatFlags flags)338 		public Rectangle GetTextExtent (IDeviceContext dc, string textToDraw, TextFormatFlags flags)
339 		{
340 			if (dc == null)
341 				throw new ArgumentNullException ("dc");
342 
343 			Rectangle result;
344 			last_hresult = VisualStyles.UxThemeGetThemeTextExtent (theme, dc, this.part, this.state, textToDraw, flags, out result);
345 			return result;
346 		}
347 
GetTextMetrics(IDeviceContext dc)348 		public TextMetrics GetTextMetrics (IDeviceContext dc)
349 		{
350 			if (dc == null)
351 				throw new ArgumentNullException ("dc", "dc cannot be null.");
352 
353 			TextMetrics result;
354 			last_hresult = VisualStyles.UxThemeGetThemeTextMetrics (theme, dc, this.part, this.state, out result);
355 			return result;
356 		}
357 
HitTestBackground(IDeviceContext dc, Rectangle backgroundRectangle, IntPtr hRgn, Point pt, HitTestOptions options)358 		public HitTestCode HitTestBackground (IDeviceContext dc, Rectangle backgroundRectangle, IntPtr hRgn, Point pt, HitTestOptions options)
359 		{
360 			if (dc == null)
361 				throw new ArgumentNullException ("dc");
362 
363 			HitTestCode result;
364 			last_hresult = VisualStyles.UxThemeHitTestThemeBackground(theme, dc, this.part, this.state, options, backgroundRectangle, hRgn, pt, out result);
365 			return result;
366 		}
367 
HitTestBackground(Graphics g, Rectangle backgroundRectangle, Region region, Point pt, HitTestOptions options)368 		public HitTestCode HitTestBackground (Graphics g, Rectangle backgroundRectangle, Region region, Point pt, HitTestOptions options)
369 		{
370 			if (g == null)
371 				throw new ArgumentNullException ("g");
372 
373 			IntPtr hRgn = region.GetHrgn(g);
374 
375 			return this.HitTestBackground(g, backgroundRectangle, hRgn, pt, options);
376 		}
377 
HitTestBackground(IDeviceContext dc, Rectangle backgroundRectangle, Point pt, HitTestOptions options)378 		public HitTestCode HitTestBackground (IDeviceContext dc, Rectangle backgroundRectangle, Point pt, HitTestOptions options)
379 		{
380 			return this.HitTestBackground (dc, backgroundRectangle, IntPtr.Zero, pt, options);
381 		}
382 
IsBackgroundPartiallyTransparent()383 		public bool IsBackgroundPartiallyTransparent ()
384 		{
385 			return VisualStyles.UxThemeIsThemeBackgroundPartiallyTransparent (theme, this.part, this.state);
386 		}
387 
SetParameters(string className, int part, int state)388 		public void SetParameters (string className, int part, int state)
389 		{
390 			if (theme != IntPtr.Zero)
391 				last_hresult = VisualStyles.UxThemeCloseThemeData (theme);
392 
393 			if (!IsSupported)
394 				throw new InvalidOperationException ("Visual Styles are not enabled.");
395 
396 			this.class_name = className;
397 			this.part = part;
398 			this.state = state;
399 			theme = VisualStyles.UxThemeOpenThemeData (IntPtr.Zero, this.class_name);
400 
401 			if (IsElementKnownToBeSupported (className, part, state))
402 				return;
403 			if (theme == IntPtr.Zero || !VisualStyles.UxThemeIsThemePartDefined (theme, this.part))
404 				throw new ArgumentException ("This element is not supported by the current visual style.");
405 		}
406 
SetParameters(VisualStyleElement element)407 		public void SetParameters (VisualStyleElement element)
408 		{
409 			this.SetParameters (element.ClassName, element.Part, element.State);
410 		}
411 		#endregion
412 
413 		#region Private Properties
414 		internal static IVisualStyles VisualStyles {
415 			get { return VisualStylesEngine.Instance; }
416 		}
417 		#endregion
418 
419 		#region Private Instance Methods
DrawBackgroundExcludingArea(IDeviceContext dc, Rectangle bounds, Rectangle excludedArea)420 		internal void DrawBackgroundExcludingArea (IDeviceContext dc, Rectangle bounds, Rectangle excludedArea)
421 		{
422 			VisualStyles.VisualStyleRendererDrawBackgroundExcludingArea (theme, dc, part, state, bounds, excludedArea);
423 		}
424 		#endregion
425 
426 		#region Private Static Methods
IsElementKnownToBeSupported(string className, int part, int state)427 		private static bool IsElementKnownToBeSupported (string className, int part, int state)
428 		{
429 			return className == "STATUS" && part == 0 && state == 0;
430 		}
431 		#endregion
432 
433 		#region Private Classes
434 		private class ThemeHandleManager
435 		{
436 			public VisualStyleRenderer VisualStyleRenderer;
~ThemeHandleManager()437 			~ThemeHandleManager ()
438 			{
439 				if (VisualStyleRenderer.theme == IntPtr.Zero)
440 					return;
441 				VisualStyles.UxThemeCloseThemeData (VisualStyleRenderer.theme);
442 			}
443 		}
444 		#endregion
445 	}
446 }
447