Moin,
da sich hier nur wenige Leute Visual Styles kennen, möchte ich sie hier mal vorstellen und Verwendungsmöglichkeiten vorstellen.
Vorweg
Dieses Thema ist für Leute gedacht, die sich schon mit GDI+ und im Idealfall auch mit der Erstellung von eigenen Controls auskennen. Die Codes sind alle in C# geschrieben, lassen sich aber ohne weiteres über einen Konverter übersetzen.
Nun zum Thema
Bei Visual Styles lässt man Windows die Sachen zeichnen, anstatt dass man es selbst macht. Das hat den Vorteil, dass sich erstellte Controls immer dem aktuellen Theme von Windows anpassen und dass sich der benötigte Code stark reduziert.
Als Beispiel möchte ich euch zeigen, wie man einen
Aber erstmal zu den Basics:
Will man Windows Controls zeichnen lassen, macht man das mit Hilfe des
Hier ist nun einiges an Erklärung fällig:
Jetzt haben wir dieses Element aber noch nicht gezeichnet - dafür brauchen wir einen
Nun müssen wir diesen Button nur noch zeichnen - ich mach das mal im Paint-Event:
Und schon haben wir unseren NavigationButton gerendert:
Hier noch eine mögliche Umsetzung des kompletten NavigationButton (Auszug aus der Library, an welcher ich gerade arbeite):
NavigationButton Umsetzung
Control-Klasse
Designer-Klasse
Renderer-Klasse
Dieser Code ließe sich aber natürlich bei Bedarf um ein Vielfaches verkürzen - dies ist ja wie gesagt ein Auszug aus einer Library.
[line][/line]
Hier möchte ich auch noch meine VisualStyle-Library vorstellen. Sie ist noch in Arbeit, aber schon komplett Nutzbar. Ich stelle hier einfach mal ein Klassendiagramm (nur die Renderer-Klassen, die Enums hab ich weg gelassen - das Bild sollte man direkt anzeigen lassen, um auch nur irgendwas lesen zu können) und die Projektmappe zur Verfügung:
Die Projektmappe befindet sich im Anhang (@Mods: Es sind keine ausführbaren Dateien vorhanden ;))
Hoffentlich konnte ich mit diesem kleinen Artikel etwas auf die VisualStyles aufmerksam machen und vielleicht wird sich ja auch der ein oder andere damit beschäftigen.
Grüße,
Stefan
da sich hier nur wenige Leute Visual Styles kennen, möchte ich sie hier mal vorstellen und Verwendungsmöglichkeiten vorstellen.
Vorweg
Dieses Thema ist für Leute gedacht, die sich schon mit GDI+ und im Idealfall auch mit der Erstellung von eigenen Controls auskennen. Die Codes sind alle in C# geschrieben, lassen sich aber ohne weiteres über einen Konverter übersetzen.
Nun zum Thema
Bei Visual Styles lässt man Windows die Sachen zeichnen, anstatt dass man es selbst macht. Das hat den Vorteil, dass sich erstellte Controls immer dem aktuellen Theme von Windows anpassen und dass sich der benötigte Code stark reduziert.
Als Beispiel möchte ich euch zeigen, wie man einen
NavigationButton
zeichnen kann - das gleiche Control wie Artentus in seinem Projekt [OpenSource] ExplorerNavigationButton von Hand erstellt hat.Aber erstmal zu den Basics:
Will man Windows Controls zeichnen lassen, macht man das mit Hilfe des
System.Windows.Forms.VisualStyles
-Namespace. Um ein Control zu zeichnen, braucht man nun einen VisualStyleRenderer
, wofür man wiederum ein VisualStyleElement
braucht. Dieses Element repräsentiert einen Teil eines bestimmten Controls. Man kriegt dieses Element entweder über die Unterklassen (z.B. VisualStyleElement.Button.PushButton.Default
) oder über die CreateElement
-Methode. Da die Unterklassen nicht komplett sind, holen wir uns mal ein Element über seine ID:Hier ist nun einiges an Erklärung fällig:
"Navigation"
ist der Name der Klasse, in der sich das gewünschte Element befindet. Die 1
ist die PartId
- hier die Id für einen nach links zeigenden Navigation-Button. Die 2
ist die StateId
, sie bestimmt das genauere Aussehen, beim NavigationButton stellt sie den Hover
-Status dar.Jetzt haben wir dieses Element aber noch nicht gezeichnet - dafür brauchen wir einen
VisualStyleRenderer
:Nun müssen wir diesen Button nur noch zeichnen - ich mach das mal im Paint-Event:
C#-Quellcode
- protected override void OnPaint(PaintEventArgs e)
- {
- VisualStyleElement NavigationBackButton = VisualStyleElement.CreateElement("Navigation", 1, 2);
- VisualStyleRenderer NavigationBackButtonRenderer = new VisualStyleRenderer(NavigationBackButton);
- NavigationBackButtonRenderer.DrawBackground(e.Graphics, new Rectangle(0, 0, 30, 30));
- base.OnPaint(e);
- }
Und schon haben wir unseren NavigationButton gerendert:
Hier noch eine mögliche Umsetzung des kompletten NavigationButton (Auszug aus der Library, an welcher ich gerade arbeite):
C#-Quellcode
- using System;
- using System.Collections.Generic;
- using System.ComponentModel;
- using System.Diagnostics;
- using System.Drawing;
- using System.Linq;
- using System.Text;
- using System.Threading.Tasks;
- using System.Windows.Forms;
- using VisualStyleControls.Rendering;
- namespace VisualStyleControls.Controls
- {
- /// <summary>
- /// A simple Back/Forward Button drawn by Windows via Visual Styles.
- /// </summary>
- [ToolboxBitmap(typeof(Button))]
- [Designer(typeof(NavigationButtonDesigner))]
- [DefaultEvent("Click")]
- [Description("A simple Back/Forward Button drawn by Windows via Visual Styles")]
- public class NavigationButton
- : Control
- {
- /// <summary>
- /// Initializes a new instance of the <see cref="T:VisualStyleControls.Controls.NavigationButton"/> class.
- /// </summary>
- public NavigationButton()
- : base()
- {
- this.SuspendLayout();
- this.Size = new Size(30, 30);
- this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.UserPaint, true);
- this.UpdateStyles();
- this.ResumeLayout(false);
- }
- /// <summary>
- /// Gets raised if the arrow direction has changed.
- /// </summary>
- [Category("Appearance")]
- [Description("Gets raised when the Button Type got changed.")]
- public event EventHandler ButtonTypeChanged;
- /// <summary>
- /// Raises the <see cref="E:VisualStyleControls.Controls.NavigationButton.ButtonTypeChanged"/> event.
- /// </summary>
- protected virtual void OnButtonTypeChanged()
- {
- if (this.ButtonTypeChanged != null)
- {
- this.ButtonTypeChanged(this, EventArgs.Empty);
- }
- }
- private NavigationButtonType type = NavigationButtonType.Back;
- private VisualStyleControls.Rendering.ButtonState state = VisualStyleControls.Rendering.ButtonState.Normal;
- /// <summary>
- /// Indicates the Type of this Button.
- /// </summary>
- /// <value>
- /// The current Type.
- /// </value>
- [Category("Appearance")]
- [Description("Indicates the Type of this Button.")]
- public NavigationButtonType ButtonType
- {
- get
- {
- return this.type;
- }
- set
- {
- if (value != this.type)
- {
- this.type = value;
- this.Invalidate();
- this.OnButtonTypeChanged();
- }
- }
- }
- protected override void OnPaint(PaintEventArgs e)
- {
- switch (this.ButtonType)
- {
- case NavigationButtonType.Back:
- NavigationButtonRenderer.RenderBackButton(e.Graphics, new Rectangle(0, 0, this.Width, this.Height), this.Enabled ? this.state : VisualStyleControls.Rendering.ButtonState.Disabled);
- break;
- case NavigationButtonType.Forward:
- NavigationButtonRenderer.RenderForwardButton(e.Graphics, new Rectangle(0, 0, this.Width, this.Height), this.Enabled ? this.state : VisualStyleControls.Rendering.ButtonState.Disabled);
- break;
- default:
- Debug.WriteLine("Really don't know what happened here but the ButtonType property has a value nonexistent in NavigationButtonType");
- break;
- }
- base.OnPaint(e);
- }
- protected override void OnMouseEnter(EventArgs e)
- {
- this.state = VisualStyleControls.Rendering.ButtonState.Hot;
- this.Invalidate();
- base.OnMouseEnter(e);
- }
- protected override void OnMouseLeave(EventArgs e)
- {
- this.state = VisualStyleControls.Rendering.ButtonState.Normal;
- this.Invalidate();
- base.OnMouseLeave(e);
- }
- protected override void OnMouseDown(MouseEventArgs e)
- {
- this.state = VisualStyleControls.Rendering.ButtonState.Pressed;
- this.Invalidate();
- base.OnMouseDown(e);
- }
- protected override void OnMouseUp(MouseEventArgs e)
- {
- this.state = (e.X >= 0 && e.X < this.Width && e.Y >= 0 && e.Y < this.Height) ? VisualStyleControls.Rendering.ButtonState.Hot : VisualStyleControls.Rendering.ButtonState.Normal;
- this.Invalidate();
- base.OnMouseUp(e);
- }
- protected override void OnEnabledChanged(EventArgs e)
- {
- this.Invalidate();
- base.OnEnabledChanged(e);
- }
- }
- }
C#-Quellcode
- using System;
- using System.Collections.Generic;
- using System.ComponentModel.Design;
- using System.Linq;
- using System.Text;
- using System.Threading.Tasks;
- using System.Windows.Forms.Design;
- namespace VisualStyleControls.Controls
- {
- public class NavigationButtonDesigner
- : ControlDesigner
- {
- private DesignerActionListCollection actionLists;
- public override DesignerActionListCollection ActionLists
- {
- get
- {
- return this.actionLists != null ?
- this.actionLists :
- this.actionLists = new DesignerActionListCollection(new DesignerActionList[] { new NavigationButtonDesignerActionList(this.Component) });
- }
- }
- public override SelectionRules SelectionRules
- {
- get
- {
- return SelectionRules.Moveable;
- }
- }
- }
- }
- //-----------------------
- using System;
- using System.Collections.Generic;
- using System.ComponentModel;
- using System.ComponentModel.Design;
- using System.Linq;
- using System.Text;
- using System.Threading.Tasks;
- namespace VisualStyleControls.Controls
- {
- /// <summary>
- /// Provides a DesignerActionList for the <see cref="T:VisualStyleControls.Controls.NavigationButton"/> Control.
- /// </summary>
- public class NavigationButtonDesignerActionList
- : DesignerActionListBase<NavigationButton>
- {
- /// <summary>
- /// Initializes a new instance of the <see cref="NavigationButtonDesignerActionList"/> class.
- /// </summary>
- /// <param name="component">The component.</param>
- public NavigationButtonDesignerActionList(IComponent component)
- : base(component)
- { }
- /// <summary>
- /// Returns a list of <see cref="T:System.ComponentModel.Design.DesignerActionItem"/> representing the DesignerActionList items.
- /// </summary>
- /// <returns>
- /// A <see cref="T:System.ComponentModel.Design.DesignerActionItem"/>-Array.
- /// </returns>
- public override DesignerActionItemCollection GetSortedActionItems()
- {
- DesignerActionItemCollection aItems = new DesignerActionItemCollection();
- aItems.Add(new DesignerActionPropertyItem("ButtonType", "Type", "Appearance", "Indicates the Type of this Button."));
- return aItems;
- }
- /// <summary>
- /// Indicates the Type of the targeted <see cref="T:VisualStyleControls.Controls.NavigationButton"/>.
- /// </summary>
- /// <value>
- /// The current Type.
- /// </value>
- public NavigationButtonType ButtonType
- {
- get
- {
- return this.TargetComponent.ButtonType;
- }
- set
- {
- this.TargetComponent.ButtonType = value;
- this.DesignerActionUIService.Refresh(this.Component);
- }
- }
- }
- }
- //----------------
- using System;
- using System.Collections.Generic;
- using System.ComponentModel;
- using System.ComponentModel.Design;
- using System.Linq;
- using System.Text;
- using System.Threading.Tasks;
- namespace VisualStyleControls.Controls.Helpers
- {
- /// <summary>
- /// A generic DesignerActionList.
- /// </summary>
- public abstract class DesignerActionListBase<T>
- : DesignerActionList
- where T : class, IComponent
- {
- /// <summary>
- /// Initializes a new instance of the <see cref="T:VisualStyleControls.Controls.Helpers.DesignerActionListBase"/> class.
- /// </summary>
- /// <param name="component">A component related to this <see cref="T:VisualStyleControls.Controls.DesignerActionListBase"/>.</param>
- public DesignerActionListBase(IComponent component)
- : base(component)
- {
- this.TargetComponent = component as T;
- this.DesignerActionUIService = this.GetService(typeof(DesignerActionUIService)) as DesignerActionUIService;
- }
- /// <summary>
- /// Returns the <see cref="T:System.ComponentModel.IComponent"/> this <see cref="T:VisualStyleControls.Controls.Helpers.DesignerActionListBase"/> is targeted at.
- /// </summary>
- /// <value>
- /// The target component.
- /// </value>
- protected T TargetComponent { get; private set; }
- /// <summary>
- /// Returns the <see cref="T:System.ComponentModel.Design.DesignerActionUIService"/>.
- /// </summary>
- /// <value>
- /// The designer action UI service.
- /// </value>
- protected DesignerActionUIService DesignerActionUIService { get; private set; }
- /// <summary>
- /// Returns a list of <see cref="T:System.ComponentModel.Design.DesignerActionItem"/> representing the DesignerActionList items.
- /// </summary>
- /// <returns>
- /// A <see cref="T:System.ComponentModel.Design.DesignerActionItem"/>-Array.
- /// </returns>
- public override DesignerActionItemCollection GetSortedActionItems()
- {
- throw new NotImplementedException();
- }
- }
- }
C#-Quellcode
- using System;
- using System.Collections.Generic;
- using System.Drawing;
- using System.Linq;
- using System.Text;
- using System.Threading.Tasks;
- using System.Windows.Forms.VisualStyles;
- namespace VisualStyleControls.Rendering
- {
- /// <summary>
- /// Renders the Windows Navigation Button
- /// </summary>
- public static class NavigationButtonRenderer
- {
- /// <summary>
- /// Renders a Windows Navigation Button BackButton
- /// </summary>
- /// <param name="dc">The dc to draw to (you can also pass a <see cref="System.Drawing.Graphics"/> object).</param>
- /// <param name="bounds">The bounds.</param>
- /// <param name="state">The state.</param>
- public static void RenderBackButton(IDeviceContext dc, Rectangle bounds, ButtonState state)
- {
- new VisualStyleRenderer(NavigationButtonRenderer.CreateBackButtonElement(state)).DrawBackground(dc, bounds);
- }
- /// <summary>
- /// Creates a Windows Navigation Button BackButton Visual Style Element
- /// </summary>
- /// <param name="state">The state.</param>
- public static VisualStyleElement CreateBackButtonElement(ButtonState state)
- {
- return VisualStyleElement.CreateElement("Navigation", 1, (int)state);
- }
- /// <summary>
- /// Renders a Windows Navigation Button ForwardButton
- /// </summary>
- /// <param name="dc">The dc to draw to (you can also pass a <see cref="System.Drawing.Graphics"/> object).</param>
- /// <param name="bounds">The bounds.</param>
- /// <param name="state">The state.</param>
- public static void RenderForwardButton(IDeviceContext dc, Rectangle bounds, ButtonState state)
- {
- new VisualStyleRenderer(NavigationButtonRenderer.CreateForwardButtonElement(state)).DrawBackground(dc, bounds);
- }
- /// <summary>
- /// Creates a Windows Navigation Button ForwardButton Visual Style Element
- /// </summary>
- /// <param name="state">The state.</param>
- public static VisualStyleElement CreateForwardButtonElement(ButtonState state)
- {
- return VisualStyleElement.CreateElement("Navigation", 2, (int)state);
- }
- /// <summary>
- /// Renders a Windows Navigation Button MenuButton
- /// </summary>
- /// <param name="dc">The dc to draw to (you can also pass a <see cref="System.Drawing.Graphics"/> object).</param>
- /// <param name="bounds">The bounds.</param>
- /// <param name="state">The state.</param>
- public static void RenderMenuButton(IDeviceContext dc, Rectangle bounds, ButtonState state)
- {
- new VisualStyleRenderer(NavigationButtonRenderer.CreateMenuButtonElement(state)).DrawBackground(dc, bounds);
- }
- /// <summary>
- /// Creates a Windows Navigation Button MenuButton Visual Style Element
- /// </summary>
- /// <param name="state">The state.</param>
- public static VisualStyleElement CreateMenuButtonElement(ButtonState state)
- {
- return VisualStyleElement.CreateElement("Navigation", 3, (int)state);
- }
- }
- }
- //-------------------------
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Threading.Tasks;
- namespace VisualStyleControls.Rendering
- {
- /// <summary>
- /// Represents a Button state
- /// </summary>
- public enum ButtonState
- : int
- {
- /// <summary>
- /// The normal state
- /// </summary>
- Normal = 1,
- /// <summary>
- /// The hot/hover state
- /// </summary>
- Hot = 2,
- /// <summary>
- /// The pressed state
- /// </summary>
- Pressed = 3,
- /// <summary>
- /// The disabled state
- /// </summary>
- Disabled = 4
- }
- }
Dieser Code ließe sich aber natürlich bei Bedarf um ein Vielfaches verkürzen - dies ist ja wie gesagt ein Auszug aus einer Library.
[line][/line]
Hier möchte ich auch noch meine VisualStyle-Library vorstellen. Sie ist noch in Arbeit, aber schon komplett Nutzbar. Ich stelle hier einfach mal ein Klassendiagramm (nur die Renderer-Klassen, die Enums hab ich weg gelassen - das Bild sollte man direkt anzeigen lassen, um auch nur irgendwas lesen zu können) und die Projektmappe zur Verfügung:
Die Projektmappe befindet sich im Anhang (@Mods: Es sind keine ausführbaren Dateien vorhanden ;))
Hoffentlich konnte ich mit diesem kleinen Artikel etwas auf die VisualStyles aufmerksam machen und vielleicht wird sich ja auch der ein oder andere damit beschäftigen.
Grüße,
Stefan