using System; using System.ComponentModel; using System.Drawing; using System.Windows.Forms; using System.Windows.Forms.VisualStyles; using Vanara.Extensions; using static Vanara.PInvoke.UxTheme; namespace Vanara.Windows.Forms { /// /// A table layout panel that supports a glass overlay. /// [ToolboxItem(true), ToolboxBitmap(typeof(ThemedTableLayoutPanel), "ThemedTableLayoutPanel.bmp")] public class ThemedTableLayoutPanel : TableLayoutPanel { private const string defaultClass = "WINDOW"; private const int defaultPart = 29; private const int defaultState = 0; private string styleClass; private int stylePart; private int styleState; private bool supportGlass; private VisualTheme theme; /// Initializes a new instance of the class. public ThemedTableLayoutPanel() { SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw, true); SetTheme(defaultClass, defaultPart, defaultState); HandleCreated += (s, e) => BindFormEvents(true); HandleDestroyed += (s, e) => BindFormEvents(false); } /// Sets the theme using a defined . [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public VisualStyleRenderer Style { get => new VisualStyleRenderer(styleClass, stylePart, styleState); set => SetTheme(value?.Class, value?.Part ?? 0, value?.State ?? 0); } /// Gets or sets the style class. /// The style class. [DefaultValue(defaultClass), Category("Appearance")] public string StyleClass { get => styleClass; set { if (styleClass != value) { styleClass = value; ResetTheme(); } } } /// Gets or sets the style part. /// The style part. [DefaultValue(defaultPart), Category("Appearance")] public int StylePart { get => stylePart; set { if (stylePart != value) { stylePart = value; Invalidate(); } } } /// Gets or sets the style part. /// The style part. [DefaultValue(defaultState), Category("Appearance")] public int StyleState { get => styleState; set { if (styleState != value) { styleState = value; Invalidate(); } } } /// Gets or sets a value indicating whether this table supports glass (can be enclosed in the glass margin). /// true if supports glass; otherwise, false. [DefaultValue(false), Category("Appearance")] public bool SupportGlass { get => supportGlass; set { if (supportGlass != value) { supportGlass = value; Invalidate(); } } } /// Gets or sets a style part value that is used when focus is lost. /// The non-focused style part value. A value of -1 will set the state to the same value as . [DefaultValue(-1), Category("Appearance")] public int UnfocusedStyleState { get; set; } = -1; private bool ThemingSupported => Application.RenderWithVisualStyles || DesktopWindowManager.CompositionEnabled; /// Sets the theme using theme class information. /// Name of the theme class. /// The theme part. /// The theme state. public void SetTheme(string className, int part, int state) { styleClass = className; stylePart = part; styleState = state; ResetTheme(); } /// Sets the theme using information. /// The visual style. public void SetTheme(VisualStyleElement visualStyle) => SetTheme(visualStyle?.ClassName, visualStyle?.Part ?? 0, visualStyle?.State ?? 0); /// Raises the event. /// A that contains the event data. protected override void OnPaint(PaintEventArgs e) { if (Visible) { if (!this.IsDesignMode() && SupportGlass && ThemingSupported) try { e.Graphics.Clear(Color.Black); } catch { } else { var state = UnfocusedStyleState == -1 || FindForm().Focused ? styleState : UnfocusedStyleState; if (theme != null && ThemingSupported) { if (theme.IsBackgroundPartiallyTransparent(stylePart, state)) theme.DrawParentBackground(this, e.Graphics, ClientRectangle); theme.DrawBackground(e.Graphics, stylePart, state, ClientRectangle, e.ClipRectangle); if (!string.IsNullOrEmpty(Text)) theme.DrawText(e.Graphics, stylePart, state, ClientRectangle, Text, this.BuildTextFormatFlags(false), !Enabled); } else { try { e.Graphics.Clear(BackColor); } catch { } if (!string.IsNullOrEmpty(Text)) TextRenderer.DrawText(e.Graphics, Text, Font, ClientRectangle, ForeColor, this.BuildTextFormatFlags(false)); } } } base.OnPaint(e); } /// /// Fires the event indicating that the panel has been resized. Inheriting controls should use this in favor of actually listening to /// the event, but should still call base.onResize to ensure that the event is fired for external listeners. /// /// An that contains the event data. protected override void OnResize(EventArgs eventargs) { base.OnResize(eventargs); Refresh(); } private void BindFormEvents(bool attach) { var pForm = FindForm(); if (pForm != null) { if (attach) { pForm.Activated += ParentStateChanged; pForm.Deactivate += ParentStateChanged; } else { pForm.Activated -= ParentStateChanged; pForm.Deactivate -= ParentStateChanged; } } } private void ParentStateChanged(object sender, EventArgs e) => Refresh(); private void ResetTheme() { if (styleClass != null && ThemingSupported) { try { theme = new VisualTheme(Parent, styleClass, SupportGlass ? OpenThemeDataOptions.OTD_NONCLIENT : OpenThemeDataOptions.None); } catch { theme = null; } } else theme = null; Refresh(); } } }