490 lines
20 KiB
C#

using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
namespace DevComponents.DotNetBar.Controls
{
/// <summary>
/// A single horizontal or vertical line control.
/// </summary>
[ToolboxBitmap(typeof(Line), "Controls.Line.ico"), ToolboxItem(true), Description("Horizontal or Vertical Line Control")]
public class Line : Control
{
#region Constructor
/// <summary>
/// Initializes a new instance of the Line class.
/// </summary>
public Line()
{
this.SetStyle(ControlStyles.AllPaintingInWmPaint
| ControlStyles.OptimizedDoubleBuffer
| ControlStyles.UserPaint
| ControlStyles.SupportsTransparentBackColor
, true);
}
#endregion
#region Implementation
protected override void OnPaint(PaintEventArgs e)
{
Graphics g = e.Graphics;
SmoothingMode sm = g.SmoothingMode;
g.SmoothingMode = SmoothingMode.None;
using (Pen pen = new Pen(ForeColor, _Thickness))
{
pen.DashStyle = _DashStyle;
pen.DashOffset = _DashOffset;
Point lineStart = LineStartPoint;
Point lineEnd = LineEndPoint;
if (_StartLineCap != eLineEndType.None && _Thickness > 1)
{
if (_VerticalLine)
lineStart.Y += _StartLineCapSize.Height / 2;
else
lineStart.X += _StartLineCapSize.Width / 2;
}
if (_EndLineCap != eLineEndType.None && _Thickness > 1)
{
if (_VerticalLine)
lineEnd.Y -= _EndLineCapSize.Height / 2;
else
lineEnd.X -= _EndLineCapSize.Width / 2;
}
g.DrawLine(pen, lineStart, lineEnd);
}
if (_StartLineCap != eLineEndType.None && _StartLineCapSize.Width > 0 && _StartLineCapSize.Height > 0)
DrawLineCap(g, LineStartPoint, _StartLineCap, _StartLineCapSize, true);
if (_EndLineCap != eLineEndType.None && _EndLineCapSize.Width > 0 && _EndLineCapSize.Height > 0)
DrawLineCap(g, LineEndPoint, _EndLineCap, _EndLineCapSize, false);
g.SmoothingMode = sm;
base.OnPaint(e);
}
private void DrawLineCap(Graphics g, Point linePoint, eLineEndType lineCap, Size capSize, bool isStartCap)
{
if (lineCap == eLineEndType.Arrow)
{
SmoothingMode sm = g.SmoothingMode;
g.SmoothingMode = SmoothingMode.HighQuality;
using (GraphicsPath path = new GraphicsPath())
{
if (isStartCap)
{
if (VerticalLine)
path.AddLines(new Point[] {
new Point(linePoint.X, linePoint.Y),
new Point(linePoint.X - capSize.Width/2, linePoint.Y+capSize.Height),
new Point(linePoint.X+capSize.Width / 2, linePoint.Y+capSize.Height)});
else
path.AddLines(new Point[] {
new Point(linePoint.X, linePoint.Y),
new Point(linePoint.X + capSize.Width, linePoint.Y-capSize.Height/2),
new Point(linePoint.X+capSize.Width, linePoint.Y+ capSize.Height/2)});
}
else
{
if (VerticalLine)
path.AddLines(new Point[] {
new Point(linePoint.X, linePoint.Y),
new Point(linePoint.X + capSize.Width / 2, linePoint.Y - capSize.Height),
new Point(linePoint.X - capSize.Width / 2, linePoint.Y - capSize.Height)});
else
path.AddLines(new Point[] {
new Point(linePoint.X, linePoint.Y),
new Point(linePoint.X - capSize.Width, linePoint.Y + capSize.Height / 2),
new Point(linePoint.X - capSize.Width, linePoint.Y - capSize.Height/2)});
}
path.CloseAllFigures();
using (SolidBrush brush = new SolidBrush(ForeColor))
g.FillPath(brush, path);
}
g.SmoothingMode = sm;
}
else if (lineCap == eLineEndType.Circle)
{
SmoothingMode sm = g.SmoothingMode;
g.SmoothingMode = SmoothingMode.HighQuality;
using (SolidBrush brush = new SolidBrush(ForeColor))
{
if (VerticalLine && isStartCap)
g.FillEllipse(brush, new Rectangle(linePoint.X - capSize.Width/2, linePoint.Y , capSize.Width, capSize.Height));
else if (VerticalLine)
g.FillEllipse(brush, new Rectangle(linePoint.X - capSize.Width/2, linePoint.Y - capSize.Height - 1, capSize.Width, capSize.Height));
else if (isStartCap)
g.FillEllipse(brush, new Rectangle(linePoint.X, linePoint.Y - capSize.Height / 2, capSize.Width, capSize.Height));
else
g.FillEllipse(brush, new Rectangle(linePoint.X - capSize.Width - 1, linePoint.Y - capSize.Height / 2, capSize.Width, capSize.Height));
}
g.SmoothingMode = sm;
}
else if (lineCap == eLineEndType.Diamond)
{
SmoothingMode sm = g.SmoothingMode;
g.SmoothingMode = SmoothingMode.HighQuality;
using (GraphicsPath path = new GraphicsPath())
{
if (isStartCap)
{
if (VerticalLine)
path.AddLines(new Point[] {
new Point(linePoint.X, linePoint.Y),
new Point(linePoint.X - capSize.Width/2, linePoint.Y+capSize.Height / 2),
new Point(linePoint.X, linePoint.Y+capSize.Height),
new Point(linePoint.X+capSize.Width / 2, linePoint.Y+capSize.Height / 2)});
else
path.AddLines(new Point[] {
new Point(linePoint.X, linePoint.Y),
new Point(linePoint.X + capSize.Width/2, linePoint.Y-capSize.Height/2),
new Point(linePoint.X + capSize.Width, linePoint.Y),
new Point(linePoint.X+capSize.Width / 2, linePoint.Y+ capSize.Height/2)});
}
else
{
if (VerticalLine)
{
linePoint.Y--;
path.AddLines(new Point[] {
new Point(linePoint.X, linePoint.Y),
new Point(linePoint.X + capSize.Width / 2, linePoint.Y - capSize.Height / 2),
new Point(linePoint.X, linePoint.Y - capSize.Height),
new Point(linePoint.X - capSize.Width / 2, linePoint.Y - capSize.Height / 2)});
}
else
{
linePoint.X--;
path.AddLines(new Point[] {
new Point(linePoint.X, linePoint.Y),
new Point(linePoint.X - capSize.Width / 2, linePoint.Y - capSize.Height / 2),
new Point(linePoint.X - capSize.Width, linePoint.Y),
new Point(linePoint.X - capSize.Width / 2, linePoint.Y + capSize.Height/2)});
}
}
path.CloseAllFigures();
using (SolidBrush brush = new SolidBrush(ForeColor))
g.FillPath(brush, path);
}
g.SmoothingMode = sm;
}
else if (lineCap == eLineEndType.Rectangle)
{
SmoothingMode sm = g.SmoothingMode;
g.SmoothingMode = SmoothingMode.HighQuality;
using (SolidBrush brush = new SolidBrush(ForeColor))
{
if (VerticalLine && isStartCap)
g.FillRectangle(brush, new Rectangle(linePoint.X - capSize.Width / 2, linePoint.Y, capSize.Width, capSize.Height));
else if (VerticalLine)
g.FillRectangle(brush, new Rectangle(linePoint.X - capSize.Width / 2, linePoint.Y - capSize.Height - 1, capSize.Width, capSize.Height));
else if (isStartCap)
g.FillRectangle(brush, new Rectangle(linePoint.X, linePoint.Y - capSize.Height / 2, capSize.Width, capSize.Height));
else
g.FillRectangle(brush, new Rectangle(linePoint.X - capSize.Width - 1, linePoint.Y - capSize.Height / 2, capSize.Width, capSize.Height));
}
g.SmoothingMode = sm;
}
}
private Point LineStartPoint
{
get
{
if (!_VerticalLine)
{
if (_LineAlignment == eItemAlignment.Center)
return new Point(0, this.Height / 2);
else if (_LineAlignment == eItemAlignment.Near)
return new Point(0, _Thickness / 2 + ((_StartLineCap != eLineEndType.None) ? _StartLineCapSize.Height / 2 : 0));
else if (_LineAlignment == eItemAlignment.Far)
return new Point(0, this.Height - _Thickness / 2 - ((_StartLineCap != eLineEndType.None) ? _StartLineCapSize.Height / 2 : 0));
}
else
{
if (_LineAlignment == eItemAlignment.Center)
return new Point(this.Width / 2, 0);
else if (_LineAlignment == eItemAlignment.Near)
{
return new Point(_Thickness / 2 + ((_EndLineCap != eLineEndType.None) ? _EndLineCapSize.Width / 2 : 0), 0);
}
else if (_LineAlignment == eItemAlignment.Far)
return new Point(this.Width - _Thickness / 2 - ((_EndLineCap != eLineEndType.None) ? _EndLineCapSize.Width / 2 : 0), 0);
}
return Point.Empty;
}
}
private Point LineEndPoint
{
get
{
if (!_VerticalLine)
{
return new Point(this.Width, LineStartPoint.Y);
}
else
{
return new Point(LineStartPoint.X, this.Height);
}
}
}
private eItemAlignment _LineAlignment = eItemAlignment.Center;
/// <summary>
/// Specifies the line alignment within control bounds.
/// </summary>
[DefaultValue(eItemAlignment.Center), Category("Appearance"), Description("Specifies the line alignment within control bounds.")]
public eItemAlignment LineAlignment
{
get { return _LineAlignment; }
set { _LineAlignment = value; this.Invalidate(); }
}
private float _DashOffset;
/// <summary>
/// Specifies distance from the start of a line to the beginning of a dash pattern.
/// </summary>
[DefaultValue(0f), Category("Appearance"), Description("Specifies distance from the start of a line to the beginning of a dash pattern.")]
public float DashOffset
{
get { return _DashOffset; }
set { _DashOffset = value; this.Invalidate(); }
}
private DashStyle _DashStyle = DashStyle.Solid;
/// <summary>
/// Specifies the line dash style.
/// </summary>
[DefaultValue(DashStyle.Solid), Category("Appearance"), Description("Specifies the line dash style.")]
public DashStyle DashStyle
{
get { return _DashStyle; }
set { _DashStyle = value; this.Invalidate(); }
}
//private LineCap _LineCap = System.Drawing.Drawing2D.LineCap.Round;
///// <summary>
///// Gets or sets the line cap i.e. line ending.
///// </summary>
//[DefaultValue(LineCap.Round), Category("Appearance"), Description("Specifies line cap i.e. line ending.")]
//public LineCap LineCap
//{
// get { return _LineCap; }
// set { _LineCap = value; this.Invalidate(); }
//}
private int _Thickness = 1;
/// <summary>
/// Gets or sets the line thickness in pixels.
/// </summary>
[DefaultValue(1), Category("Appearance"), Description("Indicates line thickness in pixels.")]
public int Thickness
{
get { return _Thickness; }
set
{
_Thickness = value;
this.Invalidate();
if (this.AutoSize) AdjustSize();
}
}
private bool _VerticalLine = false;
/// <summary>
/// Gets or sets whether vertical line is drawn. Default value is false which means horizontal line is drawn.
/// </summary>
[DefaultValue(false), Category("Appearance"), Description("Indicates whether vertical line is drawn.")]
public bool VerticalLine
{
get { return _VerticalLine; }
set
{
_VerticalLine = value;
this.Invalidate();
}
}
/// <summary>
/// Gets or sets a value indicating whether the control is automatically resized to display its entire contents. You can set MaximumSize.Width property to set the maximum width used by the control.
/// </summary>
[Browsable(true), DefaultValue(false), EditorBrowsable(EditorBrowsableState.Always), DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public override bool AutoSize
{
get
{
return base.AutoSize;
}
set
{
if (this.AutoSize != value)
{
base.AutoSize = value;
AdjustSize();
}
}
}
private void AdjustSize()
{
this.Size = GetPreferredSize(this.Size);
}
public override Size GetPreferredSize(Size proposedSize)
{
if (this.VerticalLine)
return new Size(_Thickness, this.Height);
else
return new Size(this.Width, _Thickness);
//return base.GetPreferredSize(proposedSize);
}
private eLineEndType _StartLineCap = eLineEndType.None;
/// <summary>
/// Indicates the start of the line cap.
/// </summary>
[DefaultValue(eLineEndType.None), Category("Appearance"), Description("Indicates the start of the line cap.")]
public eLineEndType StartLineCap
{
get { return _StartLineCap; }
set
{
if (value != _StartLineCap)
{
eLineEndType oldValue = _StartLineCap;
_StartLineCap = value;
OnStartLineCapChanged(oldValue, value);
}
}
}
/// <summary>
/// Called when StartLineCap property has changed.
/// </summary>
/// <param name="oldValue">Old property value</param>
/// <param name="newValue">New property value</param>
protected virtual void OnStartLineCapChanged(eLineEndType oldValue, eLineEndType newValue)
{
//OnPropertyChanged(new PropertyChangedEventArgs("StartLineCap"));
this.Refresh();
}
private static readonly Size DefaultCapSize = new Size(6, 6);
private Size _StartLineCapSize = DefaultCapSize;
/// <summary>
/// Indicates the size of the start cap.
/// </summary>
[Category("Appearance"), Description("Indicates the size of the start cap.")]
public Size StartLineCapSize
{
get { return _StartLineCapSize; }
set
{
if (value != _StartLineCapSize)
{
Size oldValue = _StartLineCapSize;
_StartLineCapSize = value;
OnStartLineCapSizeChanged(oldValue, value);
}
}
}
/// <summary>
/// Called when StartLineCapSize property has changed.
/// </summary>
/// <param name="oldValue">Old property value</param>
/// <param name="newValue">New property value</param>
protected virtual void OnStartLineCapSizeChanged(Size oldValue, Size newValue)
{
//OnPropertyChanged(new PropertyChangedEventArgs("StartLineCapSize"));
this.Refresh();
}
/// <summary>
/// Gets whether property should be serialized.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public bool ShouldSerializeStartLineCapSize()
{
return _StartLineCapSize != DefaultCapSize;
}
/// <summary>
/// Resets property to its default value.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public void ResetStartLineCapSize()
{
this.StartLineCapSize = DefaultCapSize;
}
private eLineEndType _EndLineCap = eLineEndType.None;
/// <summary>
/// Indicates the start of the line cap.
/// </summary>
[DefaultValue(eLineEndType.None), Category("Appearance"), Description("Indicates the start of the line cap.")]
public eLineEndType EndLineCap
{
get { return _EndLineCap; }
set
{
if (value != _EndLineCap)
{
eLineEndType oldValue = _EndLineCap;
_EndLineCap = value;
OnEndLineCapChanged(oldValue, value);
}
}
}
/// <summary>
/// Called when EndLineCap property has changed.
/// </summary>
/// <param name="oldValue">Old property value</param>
/// <param name="newValue">New property value</param>
protected virtual void OnEndLineCapChanged(eLineEndType oldValue, eLineEndType newValue)
{
//OnPropertyChanged(new PropertyChangedEventArgs("EndLineCap"));
this.Refresh();
}
private Size _EndLineCapSize = DefaultCapSize;
/// <summary>
/// Indicates end line cap size.
/// </summary>
[Category("Appearance"), Description("Indicates end line cap size.")]
public Size EndLineCapSize
{
get { return _EndLineCapSize; }
set { _EndLineCapSize = value; this.Refresh(); }
}
/// <summary>
/// Gets whether property should be serialized.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public bool ShouldSerializeEndLineCapSize()
{
return _EndLineCapSize != DefaultCapSize;
}
/// <summary>
/// Resets property to its default value.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public void ResetEndLineCapSize()
{
this.EndLineCapSize = DefaultCapSize;
}
#endregion
}
/// <summary>
/// Defined line end types.
/// </summary>
public enum eLineEndType
{
None,
Arrow,
Rectangle,
Circle,
Diamond
}
}