When we Draw to a Graphics object we can set AntiAlias so as to make the edges of complex shapes look smooth. This is done by altering the color of some of the pixels on and around the edge so that the jagged edges appear smooth.
When we create a region from a complex shape though, the recoloured pixels outside of the region are lost and as there is no way to trim the edges of the pixels (they are always square) our regions appear jagged. A simple solution to this is to put a pseudo transparent 1 pixel border around the control when shaping it. I doubt very much that you will notice this border, but it will greatly improve the appearance of the edges of your shaped control.
Because the Control in this example has rounded edges, it is not suitable for the AutoScrol property as the ScrollBars will be Clipped. For this reason the Control Inherits from Control and because we want to use it as a Container we have assigned the ParentControlDesigner to the Class.
When we create a region from a complex shape though, the recoloured pixels outside of the region are lost and as there is no way to trim the edges of the pixels (they are always square) our regions appear jagged. A simple solution to this is to put a pseudo transparent 1 pixel border around the control when shaping it. I doubt very much that you will notice this border, but it will greatly improve the appearance of the edges of your shaped control.
Because the Control in this example has rounded edges, it is not suitable for the AutoScrol property as the ScrollBars will be Clipped. For this reason the Control Inherits from Control and because we want to use it as a Container we have assigned the ParentControlDesigner to the Class.
VB net Sample:
Namespace Dotnetrix.Samples.VB <System.ComponentModel.Designer(GetType(System.Windows.Forms.Design.ParentControlDesigner))> _ Public Class RoundedPanel Inherits System.Windows.Forms.UserControl #Region " Windows Form Designer generated code " Public Sub New() MyBase.New() 'This call is required by the Windows Form Designer. InitializeComponent() 'Add any initialization after the InitializeComponent() call Me.SetStyle(ControlStyles.AllPaintingInWmPaint Or ControlStyles.DoubleBuffer Or ControlStyles.UserPaint, True) End Sub 'UserControl overrides dispose to clean up the component list. Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean) If disposing Then If Not (components Is Nothing) Then components.Dispose() End If End If MyBase.Dispose(disposing) End Sub 'Required by the Windows Form Designer Private components As System.ComponentModel.IContainer 'NOTE: The following procedure is required by the Windows Form Designer 'It can be modified using the Windows Form Designer. 'Do not modify it using the code editor. <System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent() components = New System.ComponentModel.Container End Sub #End Region Private m_BorderRadius As Int32 = 32 Public Property BorderRadius() As Int32 Get Return m_BorderRadius End Get Set(ByVal Value As Int32) m_BorderRadius = Value Me.Invalidate() End Set End Property Protected Overrides ReadOnly Property DefaultSize() As System.Drawing.Size Get Return New Size(200, 100) End Get End Property Protected Overrides Sub OnMove(ByVal e As System.EventArgs) MyBase.OnMove(e) Me.Invalidate() End Sub Protected Overrides Sub OnResize(ByVal e As System.EventArgs) MyBase.OnResize(e) Me.Invalidate() End Sub Protected Overrides Sub OnPaintBackground(ByVal pevent As System.Windows.Forms.PaintEventArgs) 'Draw the Parent onto our Control to give pseudo transparency. 'The BeginContainer and EndContainer calls stop incorrect painting of 'child controls when both container and child have BackColor set to Transparent. 'This only happens as a result of the TranslateTransform() call. Dim g As System.Drawing.Drawing2D.GraphicsContainer = pevent.Graphics.BeginContainer() Dim translateRect As Rectangle = Me.Bounds pevent.Graphics.TranslateTransform(-Me.Left, -Me.Top) Dim pe As PaintEventArgs = New PaintEventArgs(pevent.Graphics, translateRect) Me.InvokePaintBackground(Me.Parent, pe) Me.InvokePaint(Me.Parent, pe) pevent.Graphics.ResetTransform() pevent.Graphics.EndContainer(g) 'Define the custom Border Region, Brush and Pen. Dim border As System.Drawing.Drawing2D.GraphicsPath Dim paintBrush As New SolidBrush(Me.BackColor) Dim borderPen As New Pen(Me.ForeColor) Dim r As Rectangle = Me.ClientRectangle 'Set the Region of the Control Me.Region = New Region(RoundRegion(r)) r.Inflate(-1, -1) border = RoundRegion(r) 'Fill The Region with the Controls BackColor pevent.Graphics.FillPath(paintBrush, border) 'Paint any BackgroundImage that might have been set If Not (Me.BackgroundImage Is Nothing) Then Dim br As Brush = New TextureBrush(Me.BackgroundImage) pevent.Graphics.FillPath(br, border) br.Dispose() End If 'Draw the Region pevent.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality pevent.Graphics.DrawPath(borderPen, border) 'Clean Up borderPen.Dispose() paintBrush.Dispose() border.Dispose() End Sub Private Function RoundRegion(ByVal r As Rectangle) As System.Drawing.Drawing2D.GraphicsPath 'Scale the radius if it's too large to fit. Dim radius As Int32 = m_BorderRadius If (radius > (r.Width)) Then radius = r.Width If (radius > (r.Height)) Then radius = r.Height Dim path As New System.Drawing.Drawing2D.GraphicsPath If radius <= 0 Then path.AddRectangle(r) Else path.AddArc(r.Left, r.Top, radius, radius, 180, 90) path.AddArc(r.Right - radius, r.Top, radius, radius, 270, 90) path.AddArc(r.Right - radius, r.Bottom - radius, radius, radius, 0, 90) path.AddArc(r.Left, r.Bottom - radius, radius, radius, 90, 90) path.CloseFigure() End If Return path End Function End Class End Namespace
CSharp Sample:
using System; using System.Collections; using System.ComponentModel; using System.Drawing; using System.Data; using System.Windows.Forms; namespace Dotnetrix.Samples.CSharp { /// <summary> /// Summary description for RoundedPanel. /// </summary> [System.ComponentModel.Designer(typeof(System.Windows.Forms.Design.ParentControlDesigner))] public class RoundedPanel : System.Windows.Forms.Control { /// <summary> /// Required designer variable. /// </summary> private System.ComponentModel.Container components = null; public RoundedPanel() { // This call is required by the Windows.Forms Form Designer. InitializeComponent(); // TODO: Add any initialization after the InitializeComponent call this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.DoubleBuffer | ControlStyles.UserPaint, true); } /// <summary> /// Clean up any resources being used. /// </summary> protected override void Dispose( bool disposing ) { if( disposing ) { if(components != null) { components.Dispose(); } } base.Dispose( disposing ); } #region Component Designer generated code /// <summary> /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// </summary> private void InitializeComponent() { components = new System.ComponentModel.Container(); } #endregion private int borderRadius = 32; public int BorderRadius { get{return borderRadius;} set { borderRadius = value; this.Invalidate(); } } protected override Size DefaultSize { get{return new Size(200,100);} } protected override void OnMove(EventArgs e) { base.OnMove (e); this.Invalidate(); } protected override void OnResize(EventArgs e) { base.OnResize (e); this.Invalidate(); } protected override void OnPaintBackground(PaintEventArgs pevent) { //Draw the Parent onto our Control to give pseudo transparency. //The BeginContainer and EndContainer calls stop incorrect painting of //child controls when both container and child have BackColor set to Transparent. //This only happens as a result of the TranslateTransform() call. System.Drawing.Drawing2D.GraphicsContainer g = pevent.Graphics.BeginContainer(); Rectangle translateRect = this.Bounds; pevent.Graphics.TranslateTransform(-this.Left,-this.Top); PaintEventArgs pe = new PaintEventArgs(pevent.Graphics,translateRect); this.InvokePaintBackground(this.Parent,pe); this.InvokePaint(this.Parent,pe); pevent.Graphics.ResetTransform(); pevent.Graphics.EndContainer(g); //Define the custom Border Region, Brush and Pen. System.Drawing.Drawing2D.GraphicsPath border; Brush paintBrush = new SolidBrush(this.BackColor); Pen borderPen = new Pen(this.ForeColor); Rectangle r = this.ClientRectangle; //Set the Region of the Control this.Region = new Region(RoundRegion(r)); r.Inflate(-1,-1); border = RoundRegion(r); //Fill The Region with the Controls BackColor pevent.Graphics.FillPath(paintBrush,border); //Paint any BackgroundImage that might have been set if (this.BackgroundImage != null) { Brush br = new TextureBrush(this.BackgroundImage); pevent.Graphics.FillPath(br,border); br.Dispose(); } //Draw the Region pevent.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality; pevent.Graphics.DrawPath(borderPen,border); //Clean Up borderPen.Dispose(); paintBrush.Dispose(); border.Dispose(); } private System.Drawing.Drawing2D.GraphicsPath RoundRegion(Rectangle r) { //Scale the radius if it's too large to fit. int radius = borderRadius; if (radius > (r.Width)) radius = r.Width; if (radius > (r.Height)) radius = r.Height; System.Drawing.Drawing2D.GraphicsPath path = new System.Drawing.Drawing2D.GraphicsPath(); if (radius <= 0) { path.AddRectangle(r); } else { path.AddArc(r.Left,r.Top,radius,radius,180,90); path.AddArc(r.Right - radius,r.Top,radius,radius,270,90); path.AddArc(r.Right-radius,r.Bottom-radius,radius,radius,0,90); path.AddArc(r.Left,r.Bottom-radius,radius,radius,90,90); path.CloseFigure(); } return path; } } }
by: Mick Dohertys'
No comments:
Post a Comment