using System.CodeDom; using System.Collections.Generic; using System.Drawing; using System.Drawing.Drawing2D; using System.Drawing.Imaging; using Vanara.Interop; using static Vanara.Interop.NativeMethods; namespace System.Windows.Forms.VisualStyles { internal static partial class VisualStyleRendererExtension { private static readonly Dictionary bmpCache = new Dictionary(); private delegate void DrawWrapperMethod(SafeDCHandle hdc); /// /// Draws the background image of the current visual style element within the specified bounding rectangle and optionally clipped to the specified clipping rectangle. /// /// The instance. /// The used to draw the background image. /// A in which the background image is drawn. /// A that defines a clipping rectangle for the drawing operation. /// If set to true flip the image for right to left layout. public static void DrawBackground(this VisualStyleRenderer rnd, Graphics dc, Rectangle bounds, Rectangle? clipRectangle = null, bool rightToLeft = false) { /*var h = rnd.GetHashCode(); Bitmap bmp; if (!bmpCache.TryGetValue(h, out bmp)) bmpCache.Add(h, bmp = GraphicsExtension.GetTransparentBitmap(GetBackgroundBitmap(rnd, Color.White), GetBackgroundBitmap(rnd, Color.Black))); if (rightToLeft) bmp.RotateFlip(RotateFlipType.RotateNoneFlipX); if (clipRectangle != null) dc.SetClip(clipRectangle.Value); using (var attr = new ImageAttributes()) { dc.CompositingMode = CompositingMode.SourceOver; dc.CompositingQuality = CompositingQuality.HighQuality; dc.InterpolationMode = InterpolationMode.HighQualityBicubic; dc.PixelOffsetMode = PixelOffsetMode.HighQuality; attr.SetWrapMode(WrapMode.TileFlipXY); dc.DrawImage(bmp, bounds, 0, 0, bmp.Width, bmp.Height, GraphicsUnit.Pixel, attr); }*/ rnd.DrawBackground(dc, bounds, clipRectangle ?? bounds); } /// /// Draws the background image of the current visual style element onto a glass background within the specified bounding rectangle and optionally clipped to the specified clipping rectangle. /// /// The instance. /// The used to draw the background image. /// A in which the background image is drawn. /// A that defines a clipping rectangle for the drawing operation. /// If set to true flip the image for right to left layout. public static void DrawGlassBackground(this VisualStyleRenderer rnd, IDeviceContext dc, Rectangle bounds, Rectangle? clipRectangle = null, bool rightToLeft = false) { DrawWrapper(dc, bounds, memoryHdc => { var rBounds = new RECT(bounds); //var opts = new DrawThemeBackgroundOptions(clipRectangle); // Draw background var oldLayout = DCLayout.GDI_ERROR; if (rightToLeft) if ((oldLayout = SetLayout(memoryHdc, DCLayout.LAYOUT_RTL)) == DCLayout.GDI_ERROR) throw new NotSupportedException("Unable to change graphics layout to RTL."); DrawThemeBackground(rnd, memoryHdc, rnd.Part, rnd.State, ref rBounds, clipRectangle); if (oldLayout != DCLayout.GDI_ERROR) SetLayout(memoryHdc, oldLayout); } ); } /// /// Draws the image from the specified within the specified bounds on a glass background. /// /// The instance. /// The used to draw the image. /// A in which the image is drawn. /// An that contains the to draw. /// The index of the within to draw. public static void DrawGlassImage(this VisualStyleRenderer rnd, Graphics g, Rectangle bounds, ImageList imageList, int imageIndex) { DrawWrapper(g, bounds, memoryHdc => { var rBounds = new RECT(bounds); DrawThemeIcon(rnd, memoryHdc, rnd.Part, rnd.State, ref rBounds, imageList.Handle, imageIndex); } ); } /// /// Draws the specified image within the specified bounds on a glass background. /// /// The instance. /// The used to draw the image. /// A in which the image is drawn. /// An that contains the to draw. /// if set to true draws the image in a disabled state using the method. public static void DrawGlassImage(this VisualStyleRenderer rnd, Graphics g, Rectangle bounds, Image image, bool disabled = false) { DrawWrapper(g, bounds, memoryHdc => { using (var mg = Graphics.FromHdc(memoryHdc.DangerousGetHandle())) { if (disabled) ControlPaint.DrawImageDisabled(mg, image, bounds.X, bounds.Y, Color.Transparent); else mg.DrawImage(image, bounds); } } ); } /// /// Draws glowing text in the specified bounding rectangle with the option of overriding text color and applying other text formatting. /// /// The instance. /// The used to draw the text. /// A in which the text is drawn. /// The text to draw. /// Optional font override. /// Optionally, the color to draw text in overriding the default color for the theme. /// A bitwise combination of the values. /// The size of the glow. public static void DrawGlowingText(this VisualStyleRenderer rnd, IDeviceContext dc, Rectangle bounds, string text, Font font, Color? color, TextFormatFlags flags = TextFormatFlags.Default, int glowSize = 10) { DrawWrapper(dc, bounds, memoryHdc => { // Create and select font using (var fontHandle = new SafeDCObjectHandle(memoryHdc, font?.ToHfont() ?? IntPtr.Zero)) { // Draw glowing text var dttOpts = new DrawThemeTextOptions(true) {GlowSize = glowSize, AntiAliasedAlpha = true}; if (color != null) dttOpts.TextColor = color.Value; var textBounds = new RECT(4, 0, bounds.Right - bounds.Left, bounds.Bottom - bounds.Top); DrawThemeTextEx(rnd, memoryHdc, rnd.Part, rnd.State, text, text.Length, flags, ref textBounds, ref dttOpts); } } ); } /// /// Draws text in the specified bounding rectangle with the option of applying other text formatting. /// /// The instance. /// The used to draw the text. /// A in which the text is drawn. /// The text to draw. /// A bitwise combination of the values. /// The . public static void DrawText(this VisualStyleRenderer rnd, IDeviceContext dc, ref Rectangle bounds, string text, TextFormatFlags flags, ref DrawThemeTextOptions options) { var rc = new RECT(bounds); using (var hdc = new SafeDCHandle(dc)) DrawThemeTextEx(rnd, hdc, rnd.Part, rnd.State, text, text.Length, flags, ref rc, ref options); bounds = rc; } /// /// Gets the background image of the current visual style element within the specified background color. If is set, the resulting image will contain each of the state images side by side. /// /// The instance. /// The background color. This color cannot have an alpha channel. /// The optional list of states to render side by side. /// The background image. public static Bitmap GetBackgroundBitmap(this VisualStyleRenderer rnd, Color clr, int[] states = null) { const int wh = 200; if (rnd == null) throw new ArgumentNullException(nameof(rnd)); rnd.SetParameters(rnd.Class, rnd.Part, 0); if (states == null) states = new[] { rnd.State }; var i = states.Length; // Get image size Size imgSz; using (var sg = Graphics.FromHwnd(IntPtr.Zero)) imgSz = rnd.GetPartSize(sg, new Rectangle(0, 0, wh, wh), ThemeSizeType.Draw); if (imgSz.Width == 0 || imgSz.Height == 0) imgSz = new Size(rnd.GetInteger(IntegerProperty.Width), rnd.GetInteger(IntegerProperty.Height)); var bounds = new Rectangle(0, 0, imgSz.Width * i, imgSz.Height); // Draw each background linearly down the bitmap using (var memoryHdc = SafeDCHandle.ScreenCompatibleDCHandle) { // Create a device-independent bitmap and select it into our DC var info = new BITMAPINFO(bounds.Width, -bounds.Height); IntPtr ppv; using (new SafeDCObjectHandle(memoryHdc, CreateDIBSection(SafeDCHandle.Null, ref info, DIBColorMode.DIB_RGB_COLORS, out ppv, IntPtr.Zero, 0))) { using (var memoryGraphics = Graphics.FromHdc(memoryHdc.DangerousGetHandle())) { // Setup graphics memoryGraphics.CompositingMode = CompositingMode.SourceOver; memoryGraphics.CompositingQuality = CompositingQuality.HighQuality; memoryGraphics.SmoothingMode = SmoothingMode.HighQuality; memoryGraphics.Clear(clr); // Draw each background linearly down the bitmap var rect = new Rectangle(0, 0, imgSz.Width, imgSz.Height); foreach (var state in states) { rnd.SetParameters(rnd.Class, rnd.Part, state); rnd.DrawBackground(memoryGraphics, rect); rect.X += imgSz.Width; } } // Copy DIB to Bitmap var bmp = new Bitmap(bounds.Width, bounds.Height, PixelFormat.Format32bppArgb); using (var primaryHdc = new SafeDCHandle(Graphics.FromImage(bmp))) BitBlt(primaryHdc, bounds.Left, bounds.Top, bounds.Width, bounds.Height, memoryHdc, 0, 0, RasterOperationMode.SRCCOPY); return bmp; } } } /// /// Returns the value of the specified font property for the current visual style element. /// /// The instance. /// The used to draw the text. /// A that contains the value of the property specified by the prop parameter for the current visual style element. public static Font GetFont2(this VisualStyleRenderer rnd, IDeviceContext dc = null) { using (var hdc = new SafeDCHandle(dc)) { LOGFONT f; var hres = GetThemeFont(rnd, hdc, rnd.Part, rnd.State, 210, out f); if (hres != 0) throw new System.ComponentModel.Win32Exception(hres); return f.ToFont(); } } private static void DrawWrapper(IDeviceContext dc, Rectangle bounds, DrawWrapperMethod func) { using (var sdc = new SafeDCHandle(dc)) { // Create a memory DC so we can work off screen using (var memoryHdc = sdc.GetCompatibleDCHandle()) { // Create a device-independent bitmap and select it into our DC var info = new BITMAPINFO(bounds.Width, -bounds.Height); IntPtr pBits; using (new SafeDCObjectHandle(memoryHdc, CreateDIBSection(sdc, ref info, 0, out pBits, IntPtr.Zero, 0))) { // Call method func(memoryHdc); // Copy to foreground BitBlt(sdc, bounds.Left, bounds.Top, bounds.Width, bounds.Height, memoryHdc, 0, 0, RasterOperationMode.SRCCOPY); } } } } private static long GetHashCode(this VisualStyleRenderer r) => (long)r.Class.GetHashCode() << 32 | ((uint)r.Part << 16 | (ushort)r.State); } }