VisualStyleRenderer.cs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524
  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 ([email protected])
  27. //
  28. using System.Drawing;
  29. namespace System.Windows.Forms.VisualStyles
  30. {
  31. #if NET_2_0
  32. public
  33. #endif
  34. sealed class VisualStyleRenderer
  35. {
  36. private string class_name;
  37. private int part;
  38. private int state;
  39. private IntPtr theme;
  40. private int last_hresult = 0;
  41. private ThemeHandleManager theme_handle_manager = new ThemeHandleManager ();
  42. #region Public Constructors
  43. public VisualStyleRenderer (string className, int part, int state)
  44. {
  45. theme_handle_manager.VisualStyleRenderer = this;
  46. this.SetParameters (className, part, state);
  47. }
  48. public VisualStyleRenderer (VisualStyleElement element)
  49. : this (element.ClassName, element.Part, element.State) {
  50. }
  51. #endregion
  52. #region Public Properties
  53. public String Class { get { return this.class_name; } }
  54. public IntPtr Handle { get { return this.theme; } }
  55. public int LastHResult { get { return this.last_hresult; } }
  56. public int Part { get { return this.part; } }
  57. public int State { get { return this.state; } }
  58. public static bool IsSupported {
  59. get {
  60. if (!VisualStyleInformation.IsEnabledByUser)
  61. return false;
  62. if (Application.VisualStyleState == VisualStyleState.ClientAndNonClientAreasEnabled ||
  63. Application.VisualStyleState == VisualStyleState.ClientAreaEnabled)
  64. return true;
  65. return false;
  66. }
  67. }
  68. #endregion
  69. #region Public Static Methods
  70. public static bool IsElementDefined (VisualStyleElement element)
  71. {
  72. if (!IsSupported)
  73. throw new InvalidOperationException ("Visual Styles are not enabled.");
  74. if (IsElementKnownToBeSupported (element.ClassName, element.Part, element.State))
  75. return true;
  76. IntPtr theme = UXTheme.OpenThemeData (IntPtr.Zero, element.ClassName);
  77. if (theme == IntPtr.Zero)
  78. return false;
  79. bool retval = UXTheme.IsThemePartDefined (theme, element.Part, 0);
  80. UXTheme.CloseThemeData (theme);
  81. return retval;
  82. }
  83. #endregion
  84. #region Public Instance Methods
  85. public void DrawBackground (IDeviceContext dc, Rectangle bounds)
  86. {
  87. if (dc == null)
  88. throw new ArgumentNullException ("dc");
  89. XplatUIWin32.RECT BoundsRect = XplatUIWin32.RECT.FromRectangle (bounds);
  90. last_hresult = UXTheme.DrawThemeBackground (theme, dc.GetHdc (), this.part, this.state, ref BoundsRect, IntPtr.Zero);
  91. dc.ReleaseHdc ();
  92. }
  93. public void DrawBackground (IDeviceContext dc, Rectangle bounds, Rectangle clipRectangle)
  94. {
  95. if (dc == null)
  96. throw new ArgumentNullException ("dc");
  97. XplatUIWin32.RECT BoundsRect = XplatUIWin32.RECT.FromRectangle (bounds);
  98. XplatUIWin32.RECT ClipRect = XplatUIWin32.RECT.FromRectangle (clipRectangle);
  99. last_hresult = UXTheme.DrawThemeBackground (theme, dc.GetHdc (), this.part, this.state, ref BoundsRect, ref ClipRect);
  100. dc.ReleaseHdc ();
  101. }
  102. public Rectangle DrawEdge (IDeviceContext dc, Rectangle bounds, Edges edges, EdgeStyle style, EdgeEffects effects)
  103. {
  104. if (dc == null)
  105. throw new ArgumentNullException ("dc");
  106. XplatUIWin32.RECT BoundsRect = XplatUIWin32.RECT.FromRectangle (bounds);
  107. XplatUIWin32.RECT retval;
  108. last_hresult = UXTheme.DrawThemeEdge (theme, dc.GetHdc (), this.part, this.state, ref BoundsRect, (uint)style, (uint)edges + (uint)effects, out retval);
  109. dc.ReleaseHdc ();
  110. return retval.ToRectangle ();
  111. }
  112. public void DrawImage (Graphics g, Rectangle bounds, ImageList imageList, int imageIndex)
  113. {
  114. if (g == null)
  115. throw new ArgumentNullException ("g");
  116. if (imageIndex < 0 || imageIndex > imageList.Images.Count - 1)
  117. throw new ArgumentOutOfRangeException ("imageIndex");
  118. if (imageList.Images[imageIndex] == null)
  119. throw new ArgumentNullException ("imageIndex");
  120. g.DrawImage (imageList.Images[imageIndex], bounds);
  121. }
  122. public void DrawImage (Graphics g, Rectangle bounds, Image image)
  123. {
  124. if (g == null)
  125. throw new ArgumentNullException ("g");
  126. if (image == null)
  127. throw new ArgumentNullException ("image");
  128. g.DrawImage (image, bounds);
  129. }
  130. public void DrawParentBackground (IDeviceContext dc, Rectangle bounds, Control childControl)
  131. {
  132. if (dc == null)
  133. throw new ArgumentNullException ("dc");
  134. XplatUIWin32.RECT BoundsRect = XplatUIWin32.RECT.FromRectangle (bounds);
  135. using (Graphics g = Graphics.FromHwnd (childControl.Handle)) {
  136. IntPtr hdc = g.GetHdc ();
  137. last_hresult = UXTheme.DrawThemeParentBackground (childControl.Handle, hdc, ref BoundsRect);
  138. g.ReleaseHdc (hdc);
  139. }
  140. }
  141. public void DrawText (IDeviceContext dc, Rectangle bounds, string textToDraw, bool drawDisabled, TextFormatFlags flags)
  142. {
  143. if (dc == null)
  144. throw new ArgumentNullException ("dc");
  145. XplatUIWin32.RECT BoundsRect = XplatUIWin32.RECT.FromRectangle (bounds);
  146. last_hresult = UXTheme.DrawThemeText (theme, dc.GetHdc (), this.part, this.state, textToDraw, textToDraw.Length, (uint)flags, 0, ref BoundsRect);
  147. dc.ReleaseHdc ();
  148. }
  149. public void DrawText (IDeviceContext dc, Rectangle bounds, string textToDraw, bool drawDisabled)
  150. {
  151. this.DrawText (dc, bounds, textToDraw, drawDisabled, TextFormatFlags.Default);
  152. }
  153. public void DrawText (IDeviceContext dc, Rectangle bounds, string textToDraw)
  154. {
  155. this.DrawText (dc, bounds, textToDraw, false, TextFormatFlags.Default);
  156. }
  157. public Rectangle GetBackgroundContentRectangle (IDeviceContext dc, Rectangle bounds)
  158. {
  159. if (dc == null)
  160. throw new ArgumentNullException ("dc");
  161. XplatUIWin32.RECT BoundsRect = XplatUIWin32.RECT.FromRectangle (bounds);
  162. XplatUIWin32.RECT retval;
  163. last_hresult = UXTheme.GetThemeBackgroundContentRect (theme, dc.GetHdc (), this.part, this.state, ref BoundsRect, out retval);
  164. dc.ReleaseHdc ();
  165. return retval.ToRectangle ();
  166. }
  167. public Rectangle GetBackgroundExtent (IDeviceContext dc, Rectangle contentBounds)
  168. {
  169. if (dc == null)
  170. throw new ArgumentNullException ("dc");
  171. XplatUIWin32.RECT BoundsRect = XplatUIWin32.RECT.FromRectangle (contentBounds);
  172. XplatUIWin32.RECT retval = new XplatUIWin32.RECT ();
  173. last_hresult = UXTheme.GetThemeBackgroundExtent (theme, dc.GetHdc (), this.part, this.state, ref BoundsRect, ref retval);
  174. dc.ReleaseHdc ();
  175. return retval.ToRectangle ();
  176. }
  177. [System.Security.SuppressUnmanagedCodeSecurity]
  178. public Region GetBackgroundRegion (IDeviceContext dc, Rectangle bounds)
  179. {
  180. if (dc == null)
  181. throw new ArgumentNullException ("dc");
  182. XplatUIWin32.RECT BoundsRect = XplatUIWin32.RECT.FromRectangle (bounds);
  183. IntPtr retval;
  184. last_hresult = UXTheme.GetThemeBackgroundRegion (theme, dc.GetHdc (), this.part, this.state, ref BoundsRect, out retval);
  185. dc.ReleaseHdc ();
  186. return Region.FromHrgn (retval);
  187. }
  188. public bool GetBoolean (BooleanProperty prop)
  189. {
  190. if (!Enum.IsDefined (typeof (BooleanProperty), prop))
  191. throw new System.ComponentModel.InvalidEnumArgumentException ("prop", (int)prop, typeof (BooleanProperty));
  192. int retval;
  193. last_hresult = UXTheme.GetThemeBool (theme, this.part, this.state, (int)prop, out retval);
  194. return retval == 0 ? false : true;
  195. }
  196. public Color GetColor (ColorProperty prop)
  197. {
  198. if (!Enum.IsDefined (typeof (ColorProperty), prop))
  199. throw new System.ComponentModel.InvalidEnumArgumentException ("prop", (int)prop, typeof (ColorProperty));
  200. int retval;
  201. last_hresult = UXTheme.GetThemeColor (theme, this.part, this.state, (int)prop, out retval);
  202. return System.Drawing.Color.FromArgb ((int)(0x000000FFU & retval),
  203. (int)(0x0000FF00U & retval) >> 8, (int)(0x00FF0000U & retval) >> 16);
  204. }
  205. public int GetEnumValue (EnumProperty prop)
  206. {
  207. if (!Enum.IsDefined (typeof (EnumProperty), prop))
  208. throw new System.ComponentModel.InvalidEnumArgumentException ("prop", (int)prop, typeof (EnumProperty));
  209. int retval;
  210. last_hresult = UXTheme.GetThemeEnumValue (theme, this.part, this.state, (int)prop, out retval);
  211. return retval;
  212. }
  213. public string GetFilename (FilenameProperty prop)
  214. {
  215. if (!Enum.IsDefined (typeof (FilenameProperty), prop))
  216. throw new System.ComponentModel.InvalidEnumArgumentException ("prop", (int)prop, typeof (FilenameProperty));
  217. Text.StringBuilder sb = new Text.StringBuilder (255);
  218. last_hresult = UXTheme.GetThemeFilename (theme, this.part, this.state, (int)prop, sb, sb.Capacity);
  219. return sb.ToString ();
  220. }
  221. [MonoTODO(@"I can't get MS's to return anything but null, so I can't really get this one right")]
  222. public Font GetFont (IDeviceContext dc, FontProperty prop)
  223. {
  224. throw new NotImplementedException();
  225. //if (dc == null)
  226. // throw new ArgumentNullException ("dc");
  227. //if (!Enum.IsDefined (typeof (FontProperty), prop))
  228. // throw new System.ComponentModel.InvalidEnumArgumentException ("prop", (int)prop, typeof (FontProperty));
  229. //UXTheme.LOGFONT lf = new UXTheme.LOGFONT();
  230. //UXTheme.GetThemeFont (theme, dc.GetHdc (), this.part, this.state, (int)prop, out lf);
  231. //IntPtr fontPtr = UXTheme.CreateFontIndirect(lf);
  232. //dc.ReleaseHdc();
  233. //return Font.FromLogFont(lf);
  234. //return null;
  235. }
  236. public int GetInteger (IntegerProperty prop)
  237. {
  238. if (!Enum.IsDefined (typeof (IntegerProperty), prop))
  239. throw new System.ComponentModel.InvalidEnumArgumentException ("prop", (int)prop, typeof (IntegerProperty));
  240. int retval;
  241. last_hresult = UXTheme.GetThemeInt (theme, this.part, this.state, (int)prop, out retval);
  242. return retval;
  243. }
  244. [MonoTODO(@"MS's causes a PInvokeStackUnbalance on me, so this is not verified against MS.")]
  245. public Padding GetMargins (IDeviceContext dc, MarginProperty prop)
  246. {
  247. if (dc == null)
  248. throw new ArgumentNullException ("dc");
  249. if (!Enum.IsDefined (typeof (MarginProperty), prop))
  250. throw new System.ComponentModel.InvalidEnumArgumentException ("prop", (int)prop, typeof (MarginProperty));
  251. UXTheme.MARGINS retval = new UXTheme.MARGINS ();
  252. XplatUIWin32.RECT BoundsRect;
  253. last_hresult = UXTheme.GetThemeMargins (theme, dc.GetHdc (), this.part, this.state, (int)prop, out BoundsRect, out retval);
  254. dc.ReleaseHdc ();
  255. return retval.ToPadding();
  256. }
  257. public Size GetPartSize (IDeviceContext dc, Rectangle bounds, ThemeSizeType type)
  258. {
  259. if (dc == null)
  260. throw new ArgumentNullException ("dc");
  261. if (!Enum.IsDefined (typeof (ThemeSizeType), type))
  262. throw new System.ComponentModel.InvalidEnumArgumentException ("prop", (int)type, typeof (ThemeSizeType));
  263. XplatUIWin32.RECT BoundsRect = XplatUIWin32.RECT.FromRectangle (bounds);
  264. UXTheme.SIZE retval;
  265. last_hresult = UXTheme.GetThemePartSize (theme, dc.GetHdc (), this.part, this.state, ref BoundsRect, (int)type, out retval);
  266. dc.ReleaseHdc ();
  267. return retval.ToSize();
  268. }
  269. public Size GetPartSize (IDeviceContext dc, ThemeSizeType type)
  270. {
  271. if (dc == null)
  272. throw new ArgumentNullException ("dc");
  273. if (!Enum.IsDefined (typeof (ThemeSizeType), type))
  274. throw new System.ComponentModel.InvalidEnumArgumentException ("prop", (int)type, typeof (ThemeSizeType));
  275. UXTheme.SIZE retval;
  276. last_hresult = UXTheme.GetThemePartSize (theme, dc.GetHdc (), this.part, this.state, 0, (int)type, out retval);
  277. dc.ReleaseHdc ();
  278. return retval.ToSize ();
  279. }
  280. public Point GetPoint (PointProperty prop)
  281. {
  282. if (!Enum.IsDefined (typeof (PointProperty), prop))
  283. throw new System.ComponentModel.InvalidEnumArgumentException ("prop", (int)prop, typeof (PointProperty));
  284. POINT retval;
  285. last_hresult = UXTheme.GetThemePosition (theme, this.part, this.state, (int)prop, out retval);
  286. return retval.ToPoint();
  287. }
  288. [MonoTODO(@"Can't find any values that return anything on MS to test against")]
  289. public string GetString (StringProperty prop)
  290. {
  291. if (!Enum.IsDefined (typeof (StringProperty), prop))
  292. throw new System.ComponentModel.InvalidEnumArgumentException ("prop", (int)prop, typeof (StringProperty));
  293. Text.StringBuilder sb = new Text.StringBuilder (255);
  294. last_hresult = UXTheme.GetThemeString (theme, this.part, this.state, (int)prop, sb, sb.Capacity);
  295. return sb.ToString ();
  296. }
  297. public Rectangle GetTextExtent (IDeviceContext dc, Rectangle bounds, string textToDraw, TextFormatFlags flags)
  298. {
  299. if (dc == null)
  300. throw new ArgumentNullException ("dc");
  301. XplatUIWin32.RECT BoundsRect = XplatUIWin32.RECT.FromRectangle (bounds);
  302. XplatUIWin32.RECT retval;
  303. last_hresult = UXTheme.GetThemeTextExtent (theme, dc.GetHdc (), this.part, this.state, textToDraw, textToDraw.Length, (int)flags, ref BoundsRect, out retval);
  304. dc.ReleaseHdc ();
  305. return retval.ToRectangle ();
  306. }
  307. public Rectangle GetTextExtent (IDeviceContext dc, string textToDraw, TextFormatFlags flags)
  308. {
  309. if (dc == null)
  310. throw new ArgumentNullException ("dc");
  311. XplatUIWin32.RECT retval;
  312. last_hresult = UXTheme.GetThemeTextExtent (theme, dc.GetHdc (), this.part, this.state, textToDraw, textToDraw.Length, (int)flags, 0, out retval);
  313. dc.ReleaseHdc ();
  314. return retval.ToRectangle ();
  315. }
  316. public TextMetrics GetTextMetrics (IDeviceContext dc)
  317. {
  318. if (dc == null)
  319. throw new ArgumentNullException ("dc", "dc cannot be null.");
  320. XplatUIWin32.TEXTMETRIC metrics;
  321. last_hresult = UXTheme.GetThemeTextMetrics (theme, dc.GetHdc (), this.part, this.state, out metrics);
  322. dc.ReleaseHdc ();
  323. TextMetrics retval = new TextMetrics ();
  324. retval.Ascent = metrics.tmAscent;
  325. retval.AverageCharWidth = metrics.tmAveCharWidth;
  326. retval.BreakChar =(char)metrics.tmBreakChar;
  327. retval.CharSet = (TextMetricsCharacterSet)metrics.tmCharSet;
  328. retval.DefaultChar = (char)metrics.tmDefaultChar;
  329. retval.Descent = metrics.tmDescent;
  330. retval.DigitizedAspectX = metrics.tmDigitizedAspectX;
  331. retval.DigitizedAspectY = metrics.tmDigitizedAspectY;
  332. retval.ExternalLeading = metrics.tmExternalLeading;
  333. retval.FirstChar = (char)metrics.tmFirstChar;
  334. retval.Height = metrics.tmHeight;
  335. retval.InternalLeading = metrics.tmInternalLeading;
  336. retval.Italic = metrics.tmItalic == 0 ? false : true;
  337. retval.LastChar = (char)metrics.tmLastChar;
  338. retval.MaxCharWidth = metrics.tmMaxCharWidth;
  339. retval.Overhang = metrics.tmOverhang;
  340. retval.PitchAndFamily = (TextMetricsPitchAndFamilyValues)metrics.tmPitchAndFamily;
  341. retval.StruckOut = metrics.tmStruckOut == 0 ? false : true;
  342. retval.Underlined = metrics.tmUnderlined == 0 ? false : true;
  343. retval.Weight = metrics.tmWeight;
  344. return retval;
  345. }
  346. public HitTestCode HitTestBackground (IDeviceContext dc, Rectangle backgroundRectangle, IntPtr hRgn, Point pt, HitTestOptions options)
  347. {
  348. if (dc == null)
  349. throw new ArgumentNullException ("dc");
  350. XplatUIWin32.RECT BoundsRect = XplatUIWin32.RECT.FromRectangle (backgroundRectangle);
  351. int retval;
  352. last_hresult = UXTheme.HitTestThemeBackground (theme, dc.GetHdc (), this.part, this.state, (uint)options, ref BoundsRect, hRgn, new POINT(pt.X, pt.Y), out retval);
  353. dc.ReleaseHdc ();
  354. return (HitTestCode)retval;
  355. }
  356. public HitTestCode HitTestBackground (Graphics g, Rectangle backgroundRectangle, Region region, Point pt, HitTestOptions options)
  357. {
  358. if (g == null)
  359. throw new ArgumentNullException ("g");
  360. IntPtr hRgn = region.GetHrgn(g);
  361. return this.HitTestBackground(g, backgroundRectangle, hRgn, pt, options);
  362. }
  363. public HitTestCode HitTestBackground (IDeviceContext dc, Rectangle backgroundRectangle, Point pt, HitTestOptions options)
  364. {
  365. return this.HitTestBackground (dc, backgroundRectangle, IntPtr.Zero, pt, options);
  366. }
  367. public bool IsBackgroundPartiallyTransparent ()
  368. {
  369. int retval = UXTheme.IsThemeBackgroundPartiallyTransparent (theme, this.part, this.state);
  370. return retval == 0 ? false : true;
  371. }
  372. public void SetParameters (string className, int part, int state)
  373. {
  374. if (theme != IntPtr.Zero)
  375. last_hresult = UXTheme.CloseThemeData (theme);
  376. if (!IsSupported)
  377. throw new InvalidOperationException ("Visual Styles are not enabled.");
  378. this.class_name = className;
  379. this.part = part;
  380. this.state = state;
  381. theme = UXTheme.OpenThemeData (IntPtr.Zero, this.class_name);
  382. if (IsElementKnownToBeSupported (className, part, state))
  383. return;
  384. if (theme == IntPtr.Zero || !UXTheme.IsThemePartDefined (theme, this.part, 0))
  385. throw new ArgumentException ("This element is not supported by the current visual style.");
  386. }
  387. public void SetParameters (VisualStyleElement element)
  388. {
  389. this.SetParameters (element.ClassName, element.Part, element.State);
  390. }
  391. #endregion
  392. #region Private Static Methods
  393. private static bool IsElementKnownToBeSupported (string className, int part, int state)
  394. {
  395. return className == "STATUS" && part == 0 && state == 0;
  396. }
  397. #endregion
  398. #region Private Classes
  399. private class ThemeHandleManager
  400. {
  401. public VisualStyleRenderer VisualStyleRenderer;
  402. ~ThemeHandleManager ()
  403. {
  404. if (VisualStyleRenderer.theme == IntPtr.Zero)
  405. return;
  406. UXTheme.CloseThemeData (VisualStyleRenderer.theme);
  407. }
  408. }
  409. #endregion
  410. }
  411. }