Click here to Skip to main content
15,886,095 members
Articles / Programming Languages / C#

An XML- and State Machine-based Design of a WinForms Control

Rate me:
Please Sign up or sign in to vote.
4.98/5 (35 votes)
1 Dec 20024 min read 86.1K   1.3K   49  
Usage of state machine and comprehensive XML description of control helps its user to considerably simplify control handling code.
using System;
using System.Xml;
using System.Windows.Forms;
using System.Drawing;

namespace OGCLib
{
	///////////////////////////////////////////////////////////////////////////////////
	// Class Configurator - 
	//   responsible for reading from/writing to control's XML config. file
	///////////////////////////////////////////////////////////////////////////////////
	public class Configurator 
	{
		// CONST

		// Minimal height of control caption. If actual caption height is less then this value
		// then caption is not drawn and control is not dockable.
		private const int captionHeightMin = 12;
		
		// Name of XML file containing error messages templates
		private const string errMsgFileName = "OGCErrorMessages.xml";

		// VARIABLES
		protected OGControl ogControl = null;
		private XmlDocument	xmlDoc = null;
		private string		resourceDir;
		private IElement	currentElement = null;
		private bool        operationLoad = true;
	
		// CONSTRUCTOR
		public Configurator(OGControl ogControl, string xmlConfFileName,
							bool operationLoad)
		{
			if (null != ogControl)
			{
				this.ogControl = ogControl;
				this.operationLoad = operationLoad;

				// Using XML DOM...
				xmlDoc = new XmlDocument();

				// Load error message templates
				if (operationLoad)
					LoadErrorMessages();

				// Load other parameters
				LoadSaveParameters(xmlConfFileName);
			}
		}

		// METHODS			

		// Load error message templates from "errMsgFileName" file
		private void LoadErrorMessages()
		{
			try
			{
				xmlDoc.Load(errMsgFileName);
			}
			catch
			{
				string errorMessage = "Loading file '" + errMsgFileName + "' failed";
				ogControl.OnError(new ErrorEventArgs(ErrorSeverity.Warning, errorMessage));
			}		

			try
			{
				ProcessNode(xmlDoc.DocumentElement);
			}
			catch
			{
				ErrorType errorType = ErrorType.ErrorMessages;
				ogControl.OnError(new ErrorEventArgs(ErrorSeverity.Warning, 
					ogControl.GetErrorMessage((int)errorType)));
			}		
		}
	
		// Load parameters from/Save to XML config. file
		private void LoadSaveParameters(string xmlConfFileName)
		{
			try
			{
				xmlDoc.Load(xmlConfFileName);
			}
			catch
			{
				ErrorType errorType = ErrorType.LoadConfigFile;
				string errorMessage = string.Format(
				ogControl.GetErrorMessage((int)errorType), xmlConfFileName);
				ogControl.OnError(new ErrorEventArgs(ErrorSeverity.Error, errorMessage));
			}		

			try
			{
				// Recursive nodes processing
				ProcessNode(xmlDoc.DocumentElement);
			}
			catch
			{
				ErrorType errorType = ErrorType.ProcessNode;
				ogControl.OnError(new ErrorEventArgs(ErrorSeverity.Error, 
				ogControl.GetErrorMessage((int)errorType)));
			}

			try
			{
				if (!operationLoad)
					xmlDoc.Save(xmlConfFileName);
			}
			catch
			{
				ErrorType errorType = ErrorType.LoadConfigFile;
				string errorMessage = string.Format(
					ogControl.GetErrorMessage((int)errorType), xmlConfFileName);
				ogControl.OnError(new ErrorEventArgs(ErrorSeverity.Error, errorMessage));
			}		
		}
	
		// Recursive method for XML nodes processing  
		private void ProcessNode(XmlNode node)
		{
			if (null != node)
			{
				Filter(node);
				XmlNodeList nodeList = node.ChildNodes;
				int length = node.ChildNodes.Count;

				XmlNode childNode = null;
				int index = 0;
				do
				{
					childNode = nodeList.Item(index++);
					ProcessNode(childNode);
				} while (null != childNode);
			}
		}
		
		// State machine for XML processing
		protected virtual void Filter(XmlNode node)
		{
			if (null != node && null != ogControl)
			{
				if (!operationLoad)
				{
					// Save parameters
					switch (node.Name)
					{
						case "OGControl":
							try
							{	
								Write(node, "DockStyle", ogControl.Dock);
							}
							catch
							{
								ErrorType errorType = ErrorType.WritingTag;
								string errorMessage = string.Format(
									ogControl.GetErrorMessage((int)errorType),	"TagOGControl");
								ogControl.OnError(new ErrorEventArgs(ErrorSeverity.Warning, errorMessage));
							}		
							break;

						case "InitControlState":
							try
							{
								Write(node, "Value", ogControl.CurrentState);
							}
							catch
							{
								ErrorType errorType = ErrorType.WritingTag;
								string errorMessage = string.Format(
									ogControl.GetErrorMessage((int)errorType), "InitControlState");
								ogControl.OnError(new ErrorEventArgs(ErrorSeverity.Error, errorMessage));							
							}		
							break;
						}

					return;
				}

				// Load parameters
				switch (node.Name)
				{
					case "ErrorMessages":
						int i=0;
						while (null != ogControl.GetErrorTag(i))
							ogControl.AddErrorMessage(GetString(node, ogControl.GetErrorTag(i++)));					
						break;

					case "OGControl":
						try
						{
							ogControl.Name = ReadStr(node, "Name");
							ogControl.InitDock = DockStyleFromString(node, "InitDockStyle");
							ogControl.Dock = DockStyleFromString(node, "DockStyle");
							ogControl.Size = GetSize(node);				
							resourceDir = ReadStr(node, "ResourcePath");
							ogControl.ActiveCursorFileName = resourceDir + ReadStr(node, "ActiveCursor");
							ogControl.Rotate = GetYesNo(node, "Rotate");
							ogControl.BackColor = Color.FromName(ReadStr(node, "BackColor"));
						}
						catch
						{
							ErrorType errorType = ErrorType.ReadingTag;
							string errorMessage = string.Format(
								ogControl.GetErrorMessage((int)errorType), "TagOGControl");
							ogControl.OnError(new ErrorEventArgs(ErrorSeverity.Error, errorMessage));
						}		
						break;

					case "CaptionStyle":
						try
						{
							GetCaptionStyle(node);
							ogControl.CaptionTextSize = ReadInt(node, "TextSize");
						}
						catch
						{
							ErrorType errorType = ErrorType.ReadingTag;
							string errorMessage = string.Format(
								ogControl.GetErrorMessage((int)errorType), "CaptionStyle");
							ogControl.OnError(new ErrorEventArgs(ErrorSeverity.Warning, errorMessage));							
						}		
						break;

					case "Element":
						try
						{
							string type = ReadStr(node, "Type");
							currentElement = ogControl.AddElement(type, ReadStr(node, "Name"), 
								GetPosition(node, Orientation.Horizontal),
								GetPosition(node, Orientation.Vertical),
								GetElementSize(node));
						}
						catch
						{
							ErrorType errorType = ErrorType.ReadingTag;
							string errorMessage = string.Format(
								ogControl.GetErrorMessage((int)errorType),	"Element");
							ogControl.OnError(new ErrorEventArgs(ErrorSeverity.Error, errorMessage));							
						}		
						break;

					case "State":
						try
						{
							if (null != currentElement)
							{
								string fileName = "";
								string fileExt = "";
								if (currentElement.HasImage())
								{
									fileName = resourceDir + ReadStr(node, "FileName");
									fileExt = ReadStr(node, "FileExt");
								}

								currentElement.AddState(ReadStr(node, "Name"), fileName, fileExt);
							}
						}
						catch
						{
							ErrorType errorType = ErrorType.TagState;
							string errorMessage = string.Format(
								ogControl.GetErrorMessage((int)errorType), 
								"State", currentElement.GetName());
							ogControl.OnError(new ErrorEventArgs(ErrorSeverity.Error, errorMessage));							
						}		
						break;				

					case "MaxControlStates":
						try
						{
							ogControl.ControlStatesMax = ReadInt(node, "Value");
							if (0 >= ogControl.ControlStatesMax)
								throw(new Exception());
						}
						catch
						{
							ErrorType errorType = ErrorType.ReadingTag;
							string errorMessage = string.Format(
								ogControl.GetErrorMessage((int)errorType), "MaxControlStates");
							ogControl.OnError(new ErrorEventArgs(ErrorSeverity.Error, errorMessage));							
						}		
						break;
					
					case "InitControlState":
						try
						{
							ogControl.CurrentState = ReadInt(node, "Value");
							if (0 > ogControl.CurrentState ||
								ogControl.ControlStatesMax <= ogControl.CurrentState)
								throw(new Exception());
						}
						catch
						{
							ErrorType errorType = ErrorType.ReadingTag;
							string errorMessage = string.Format(
								ogControl.GetErrorMessage((int)errorType), "InitControlState");
							ogControl.OnError(new ErrorEventArgs(ErrorSeverity.Error, errorMessage));							
						}		
						break;

					case "StateTransition":
						if (null != currentElement)
							for (int ctrlState=0; ctrlState<ogControl.ControlStatesMax; ctrlState++)
								for (int elemState=0; elemState<currentElement.GetNextStatesNum(); elemState++)
								{
									string valName = (1 < currentElement.GetNextStatesNum())
											? "m"+ctrlState.ToString()+"_"+elemState.ToString()
											: "v"+ctrlState.ToString();

									int	newCtrlState = -1;
									try
									{
										newCtrlState = ReadInt(node, valName);
									}
									catch
									{
									}
									
									if (-1 <= newCtrlState && 
										ogControl.ControlStatesMax > newCtrlState)
											currentElement.SetControlStateTransitionElement(
														ctrlState, elemState, newCtrlState);
									else
									{
										ErrorType errorType = ErrorType.StateTransition;
										string errorMessage = string.Format(
											ogControl.GetErrorMessage((int)errorType),	
											currentElement.GetName(), ctrlState, elemState, newCtrlState);
										ogControl.OnError(new ErrorEventArgs(ErrorSeverity.Error, errorMessage));
									}
								}
						break;

					case "ControlStatesMatrixElement":
						SetMatrixElement(node, "ControlState");
						break;					
				}
			}
		}

		// Methods for simple objects Reading from/Writing to XML config. file file
		private string ReadStr(XmlNode node, string name)
		{
			return (string)node.Attributes.GetNamedItem(name).Value;			
		}

		private int ReadInt(XmlNode node, string name)
		{
			return Int32.Parse(ReadStr(node, name));			
		}
		
		private void Write(XmlNode node, string name, object obj)
		{
			node.Attributes.GetNamedItem(name).Value = obj.ToString();
		}

		// Helper methods
		private DockStyle DockStyleFromString(XmlNode node, string itemName)
		{
			switch (ReadStr(node, itemName))
			{
				case "Top":    return DockStyle.Top;	
				case "Bottom": return DockStyle.Bottom;	
				case "Left":   return DockStyle.Left;	
				case "Right":  return DockStyle.Right;	
				case "Fill":   return DockStyle.Fill;	
			}

			return DockStyle.None;
		}

		private Size GetSize(XmlNode node)
		{
			Size size = new Size();
			size.Width  = ReadInt(node, "Width");
			size.Height = ReadInt(node, "Height");
			int captionHeight = ReadInt(node, "CaptionHeight");
			captionHeight = (captionHeightMin <= captionHeight) ? captionHeight : 0;
			size.Height += captionHeight;
			ogControl.CaptionHeight = captionHeight;
			
			return size;
		}
		
		private Point GetPosition(XmlNode node, Orientation orientation)
		{
			string strX = "";
			string strY = "";
			switch (orientation)
			{
				case Orientation.Horizontal:
					strX = "HorizX";
					strY = "HorizY";
					break;

				case Orientation.Vertical:
					strX = "VertX";
					strY = "VertY";
					break;
			}
			
			return  new Point(ReadInt(node, strX), ReadInt(node, strY));
		}

		private Size GetElementSize(XmlNode node)
		{
			Size size;	
			try
			{
				size = new Size(ReadInt(node, "Width"), ReadInt(node, "Height"));
			}
			catch
			{
				size = new Size(0, 0);
			}
			
			return size;
		}

		private void SetMatrixElement(XmlNode node, string toggle)
		{
			int i = 0, j = 0, val = -1;
			try
			{
				i = ReadInt(node, "ControlState");
				j = ReadInt(node, "Element");
				val = ReadInt(node, "Value");
			}
			catch
			{
				return;
			}

			if (-1 <= val && ogControl.ControlStatesMax > val)
				ogControl.SetMatrixElement(i, j, val);
			else
			{
				ErrorType errorType = ErrorType.ControlStatesMatrix;
				string errorMessage = string.Format( 
						ogControl.GetErrorMessage((int)errorType),	i, j, val);
				ogControl.OnError(new ErrorEventArgs(ErrorSeverity.Error, errorMessage));
			}
		}

		private string GetString(XmlNode node, string key)
		{
			string value;
			try
			{
				value = ReadStr(node, key);
			}
			catch
			{
				value = null;
			}

			return value;
		}

		private bool GetYesNo(XmlNode node, string key)
		{
			bool br = false;

			try
			{
				string str = ReadStr(node, key);
				br = IsYes(str);			
			}
			catch
			{
				br = false;
			}

			return br;
		}

		private void GetCaptionStyle(XmlNode node)
		{
			Color backColor, textColor;
			string bold = "", italic = "";
			FontStyle fontStyle = FontStyle.Regular;
			try
			{
				backColor = Color.FromName(ReadStr(node, "BackColor"));
				textColor = Color.FromName(ReadStr(node, "TextColor"));
				bold = ReadStr(node, "Bold");
				italic = ReadStr(node, "Italic");

				if (IsYes(bold))
					fontStyle |= FontStyle.Bold; 			
				if (IsYes(italic))
					fontStyle |= FontStyle.Italic; 			
			}
			catch
			{
				backColor  = Color.Navy;
				textColor  = Color.White;
				fontStyle  = FontStyle.Regular;
			}
				
			ogControl.CaptionStyle(backColor, textColor, fontStyle);
		}

		private bool IsYes(string str)
		{
			return "Yes" == str || "yes" == str || "Y"   == str || "y"   == str;			
		}
	}
}

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)
Israel Israel


  • Nov 2010: Code Project Contests - Windows Azure Apps - Winner
  • Feb 2011: Code Project Contests - Windows Azure Apps - Grand Prize Winner



Comments and Discussions