Click here to Skip to main content
15,891,708 members
Articles / Programming Languages / C#

Text Alignment Control for .NET

Rate me:
Please Sign up or sign in to vote.
4.22/5 (15 votes)
15 Sep 20042 min read 64.7K   2K   37  
An article on creating Text Alignment Control for .net
// 
// Designed and Developed by Norm Almond
// 15 September 2004
// Version 1.0.0
// www.software-kinetics.co.uk
//

using System;
using System.ComponentModel;
using System.Collections;
using System.Diagnostics;
using System.Windows.Forms;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Text;


namespace TextAlignCtrl
{
	/// <summary>
	/// Text Alignment Control
	/// </summary>
	[ToolboxBitmap(typeof(TextAlign), "images.TextAlign.bmp"),
	System.ComponentModel.DefaultPropertyAttribute("Angle")]
	public class TextAlign : System.Windows.Forms.UserControl
	{
		#region Events
		public delegate void OnAngleChangedHandler(object sender, int Angle);

		[Category("Action")]
		[Description("Fired when angle has changed")]
		public event OnAngleChangedHandler OnAngleChangedEvent;
		#endregion

		#region Variable Declarations
		private bool _trackMouse = false;
		private int _Angle   = 0;
		private const int PegSize = 3;
		private const int _Offset = 10;
		#endregion

		/// <summary>
		/// Required designer variable.
		/// </summary>
		private System.ComponentModel.Container components = null;

		public TextAlign(System.ComponentModel.IContainer container)
		{
			_Init();
			container.Add(this);
			InitializeComponent();
		}

		public TextAlign()
		{
			_Init();
			InitializeComponent();
		}

		/// <summary>
		/// Common Contructor Code
		/// </summary>
		private void _Init()
		{
			SetStyle(ControlStyles.AllPaintingInWmPaint, true);
			SetStyle(ControlStyles.DoubleBuffer, true);
			SetStyle(ControlStyles.ResizeRedraw, true);
			SetStyle(ControlStyles.UserPaint, true);
			UpdateStyles();
		}

		protected override bool ProcessDialogKey(Keys keyData)
		{
			if (!Enabled)
				return base.ProcessDialogKey (keyData);

			int delta = Angle;

			switch (keyData)
			{
				case Keys.Home:
					delta = 0;
					break;

				case (Keys.Down | Keys.Control):
					delta -= 15;
					delta = ((delta / 15) * 15);
					break;

				case Keys.Down:
					delta -= 1;
					break;

				case (Keys.Up | Keys.Control):
					delta += 15;
					delta = ((delta / 15) * 15);
					break;

				case Keys.Up:
					delta += 1;
					break;

				default:
					return base.ProcessDialogKey (keyData);

			}

			Angle = delta;
			return false;

		}


		/// <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()
		{
			// 
			// TextAlign
			// 
			this.BackColor = System.Drawing.SystemColors.Window;
			this.Name = "TextAlign";
			this.Size = new System.Drawing.Size(120, 176);
			this.SizeChanged += new System.EventHandler(this.TextAlign_SizeChanged);
			this.Enter += new System.EventHandler(this.TextAlign_Enter);
			this.MouseUp += new System.Windows.Forms.MouseEventHandler(this.TextAlign_MouseUp);
			this.Paint += new System.Windows.Forms.PaintEventHandler(this.TextAlign_Paint);
			this.Leave += new System.EventHandler(this.TextAlign_Leave);
			this.MouseMove += new System.Windows.Forms.MouseEventHandler(this.TextAlign_MouseMove);
			this.MouseDown += new System.Windows.Forms.MouseEventHandler(this.TextAlign_MouseDown);

		}
		#endregion

		#region Private Functions
		private void _Render(Graphics g, Rectangle rcClient)
		{
			// Render Background
			g.FillRectangle(Enabled ? new SolidBrush(this.BackColor) : SystemBrushes.Control, rcClient);

			// Render Frame
			g.DrawRectangle(Focused ? SystemPens.Highlight : SystemPens.ControlDark, rcClient.X, rcClient.Y, rcClient.Width-1, rcClient.Height-1);

			Point ptCenter = new Point(_Offset, rcClient.Height / 2);
  
			StringFormat format = new StringFormat(StringFormat.GenericDefault);
			format.HotkeyPrefix = HotkeyPrefix.None;
	
			SizeF sz = g.MeasureString("Text", Control.DefaultFont, Point.Empty,format);

			Rectangle rcText = rcClient;
			rcText.X += 2; // Indent from Left
			
			// Rotate text on given Angle
			g.TranslateTransform (ptCenter.X, ptCenter.Y);
			g.RotateTransform(-_Angle);
			g.DrawString("Text", Control.DefaultFont, SystemBrushes.WindowText, new Point(0,(int)-(sz.Height/2)), format);
			g.ResetTransform();	


			// Render Dial
			Rectangle rc;

			// Render tick @ 15 degree intervals
			for (int tick=-90; tick <= 90; tick+=15)
			{
				Point ptSelectStart = _AngleToPoint(ptCenter, (double)_Angle, ptCenter.X + sz.Width - 8);
				Point ptSelectEnd = _AngleToPoint(ptCenter	, (double)_Angle, rcClient.Width - 24);
		
				g.DrawLine(Pens.Black,ptSelectStart,ptSelectEnd);

				Point pt = _AngleToPoint(ptCenter, tick, rcClient.Width - 18);

				rc = new Rectangle(pt, Size.Empty);			
				rc.Inflate(1,1);

				g.FillRectangle(Brushes.Black, rc);

				if ((tick % 45) == 0)
				{
					using (GraphicsPath myPath = new GraphicsPath())
					{
						myPath.AddLine(pt.X, pt.Y - PegSize, pt.X + PegSize, pt.Y);
						myPath.AddLine(pt.X + PegSize, pt.Y, pt.X, pt.Y + PegSize);
						myPath.AddLine(pt.X, pt.Y + PegSize, pt.X - PegSize, pt.Y);
						myPath.CloseFigure();
						
						g.FillPath(tick == _Angle ? Brushes.Red : Brushes.Black, myPath);

						myPath.AddLine(pt.X, pt.Y - PegSize, pt.X + PegSize, pt.Y);
						myPath.AddLine(pt.X + PegSize, pt.Y, pt.X, pt.Y + PegSize);
						myPath.AddLine(pt.X, pt.Y + PegSize, pt.X - PegSize, pt.Y);
						myPath.CloseFigure();
						
						g.DrawPath(Pens.Black, myPath);
					}					
				}
			}
		}

		/// <summary>
		/// Convert Angle to point
		/// </summary>
		/// <param name="ptOffset"></param>
		/// <param name="angle"></param>
		/// <param name="radius"></param>
		/// <returns></returns>
		private Point _AngleToPoint(Point ptOffset, double angle, double radius) 
		{
			double radians = angle / (180.0 / Math.PI);
			int x = ptOffset.X + (int)((double)radius* Math.Cos(radians));
			int y = ptOffset.Y - (int)((double)radius * Math.Sin(radians));
			return new Point(x,y);
		}

		private double _ArcTangent(double ratio) 
		{
			double angle = Math.Atan(ratio);

			// convert radians to degrees 
			return angle * (180.0 / Math.PI);;
		}

		private double _ArcTangent(double x, double y) 
		{
			double angle = 0.0;

			// both x and y are 0
			if ((x == 0.0) && (y == 0.0))		 
			{	
				// Set tthe angle to Zero degrees 
				angle = 0.0;
			}
			else if (x == 0.0)
			{
				// If we're on the y axis line 
				angle = (y < 0.0) ? -90 : 90.0;
			}
			else
			{
				// else neither x or y is zero
				// Find the arc-tangent of y / x in degrees
				angle = _ArcTangent((y / x));   
				
				// if x is negative 
				if (x < 0.0)					
				{
					angle += (y > 0.0) ? 180.0 : -180.0;
				}

				if (angle < -90)
					angle = -90;

				if (angle > 90)
					angle = 90;

			}

			return angle;
		}

		/// <summary>
		/// Converts a point to angle
		/// </summary>
		/// <param name="point"></param>
		/// <param name="center"></param>
		/// <returns></returns>
		private double PointToAngle(Point point, Point center) 
		{
			// Calculate the position user click relative to the center
			int x = point.X - center.X;
			int y = center.Y - point.Y;

			//Convert xy position to an angle.
			double angle = _ArcTangent( (double) x, (double) y );

			return angle;
		}

		/// <summary>
		/// Render Control
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void TextAlign_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
		{
			_Render(e.Graphics, this.ClientRectangle);		
		}

		/// <summary>
		/// Update Angle in relation to mouse
		/// </summary>
		/// <param name="point"></param>
		private void _UpdateAngle(Point point)
		{
			Rectangle rcClient = this.ClientRectangle;
			Point ptCenter = new Point(_Offset, rcClient.Height / 2);

			if (_trackMouse)
			{
				//Convert xy position to an angle.
				Angle = (int)(PointToAngle(point, ptCenter));
			}

		}

		/// <summary>
		/// Track mouse as user moves mouse over control
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void TextAlign_MouseMove(object sender, System.Windows.Forms.MouseEventArgs e)
		{
			if (!Enabled)
				return;

			_UpdateAngle(new Point(e.X, e.Y));
			Invalidate();
		}

		/// <summary>
		/// End tracking mouse
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void TextAlign_MouseUp(object sender, System.Windows.Forms.MouseEventArgs e)
		{
			if (!Enabled)
				return;

			_UpdateAngle(new Point(e.X, e.Y));
			_trackMouse = false;
			Capture = false;
			Invalidate();

		}

		/// <summary>
		/// Begin Tracking mouse
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void TextAlign_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e)
		{
			if (!Enabled)
				return;

			_trackMouse = true;
			this.Capture = true;
			Focus();
			_UpdateAngle(new Point(e.X, e.Y));
			Invalidate();
		}

		/// <summary>
		/// Control size changed, refresh control
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void TextAlign_SizeChanged(object sender, System.EventArgs e)
		{
			Refresh();
		}

		#endregion

		private void TextAlign_Enter(object sender, System.EventArgs e)
		{
			Invalidate();
		}

		private void TextAlign_Leave(object sender, System.EventArgs e)
		{
			Invalidate();
		}


		#region Public Methods
		/// <summary>
		/// Get/Set Angle for control
		/// </summary>
		[Category("TextAlign")]
		[Description("Gets/Sets angle of text")]
		public int Angle
		{
			get
			{
				return _Angle;
			}

			set
			{
				if (!Enabled)
					return;

				if (value < -90 || value > 90)
					return;
			
				_Angle = value;
				Invalidate();

				if (OnAngleChangedEvent != null)
					OnAngleChangedEvent(this, _Angle);

			}
		}

		protected virtual void OnAngleChanged(object sender, int angle)
		{
			if (OnAngleChangedEvent != null)
				OnAngleChangedEvent(this, _Angle);		
		}
   
		#endregion
	}
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Software Developer (Senior) Software Kinetics
United Kingdom United Kingdom




Software Kinetics
are experts in developing customised and bespoke applications and have expertise in the development of desktop, mobile and internet applications on Windows.


We specialise in:

  • User Interface Design
  • Desktop Development
  • Windows Phone Development
  • Windows Presentation Framework
  • Windows Forms
  • Windows Communication Framework
  • Windows Services
  • Network Applications
  • Database Applications
  • Web Development
  • Web Services
  • Silverlight
  • ASP.net


Visit Software Kinetics

Comments and Discussions