ArcObjects Library Reference  

DockableDigit

About the Implementing a schematic digitizing tool Sample

[C#]

DockableDigit.cs

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

using System.Xml;
using ESRI.ArcGIS.Schematic;
using ESRI.ArcGIS.Display;
using ESRI.ArcGIS.Carto;
using ESRI.ArcGIS.esriSystem;

namespace DigitTool
{
	/// <summary>
	/// Designer class of the dockable window add-in. It contains user interfaces that
	/// make up the dockable window.
	/// </summary>
	public partial class DigitDockableWindow : UserControl
	{
		private DigitTool m_digitCommand;

		private XmlDocument m_dom = null;
		private bool m_loading = true;
		private bool m_clickPanel = false;
		private long m_curfrmWidth;

		private System.Windows.Forms.SplitterPanel m_Panel1;
		private System.Windows.Forms.SplitterPanel m_Panel2;

		private System.Windows.Forms.SplitterPanel m_curPanel;
		private System.Drawing.Color m_MandatoryColor = System.Drawing.Color.White;

		private ISchematicLayer m_schematicLayer;
		private ISchematicFeature m_schematicFeature1 = null;
		private ISchematicFeature m_schematicFeature2 = null;
		private bool m_createNode = true; //update when click on panel and on new
		private bool m_isClosed = false;
		private ESRI.ArcGIS.Framework.IApplication m_app;

		//For button OK to retrieve the coordinate of the digit feature
		private int m_x;
		private int m_y;

		private XmlElement m_curLink = null;
		private XmlElement m_curNode = null;
		private XmlNodeList m_relations = null;
		private ISchematicDataset m_schDataset = null;
		private ISchematicElementClassContainer m_schEltClassCont = null;
		private ISchematicElementClass m_schEltClass = null;
		private ISchematicInMemoryDiagram m_SchematicInMemoryDiagram = null;
		private bool m_autoClear = false;


		public DigitDockableWindow(object hook)
		{
			InitializeComponent();
			this.Hook = hook;
		}

		/// <summary>
		/// Host object of the dockable window
		/// </summary>
		private object Hook
		{
			get;
			set;
		}

		/// <summary>
		/// Implementation class of the dockable window add-in. It is responsible for 
		/// creating and disposing the user interface class of the dockable window.
		/// </summary>
		public class AddinImpl : ESRI.ArcGIS.Desktop.AddIns.DockableWindow
		{
			private DigitDockableWindow m_windowUI;

			public AddinImpl()
			{
			}

			protected override IntPtr OnCreateChild()
			{
				m_windowUI = new DigitDockableWindow(this.Hook);

				CurrentDigitTool.CurrentTool.digitDockableWindow = m_windowUI;

				if (CurrentDigitTool.CurrentTool.currentDigit != null)
				{
					m_windowUI.m_digitCommand = CurrentDigitTool.CurrentTool.currentDigit;
					m_windowUI.m_digitCommand.m_dockableDigit = m_windowUI;
				}
				else
				{
					// CurrentDigitTool.CurrentTool.CurrentDigit is null when we open ArcMap, but OnCreateChild
					// is called if the dockable window was shown during the last ArcMap session.
					ESRI.ArcGIS.Framework.IDockableWindowManager dockWinMgr = ArcMap.DockableWindowManager;
					UID u= new UID();
					u.Value = "DigitTool_DockableWindowCS";
					CurrentDigitTool.CurrentTool.currentDockableWindow = dockWinMgr.GetDockableWindow(u);
				}

				return m_windowUI.Handle;
			}

			protected override void Dispose(bool disposing)
			{
				if (m_windowUI != null)
					m_windowUI.Dispose(disposing);

				base.Dispose(disposing);
			}

		}

		public void Init(ISchematicLayer schematicLayer)
		{
			try
			{
				if (schematicLayer == null)
					return;

				m_schematicLayer = schematicLayer;
				XmlNode col = null;
				String myString = "";

				m_schDataset = schematicLayer.SchematicDiagram.SchematicDiagramClass.SchematicDataset;

				m_schEltClassCont = (ISchematicElementClassContainer)m_schDataset;
				m_SchematicInMemoryDiagram = schematicLayer.SchematicInMemoryDiagram;

				m_dom = new XmlDocument();

				ISchematicDiagram schematicDiagram;
				schematicDiagram = m_SchematicInMemoryDiagram.SchematicDiagram;

				// get the path of the xml file that contains the definitions of the digitize dockable window
				String path;

				ISchematicDiagramClass schematicDiagramClass = schematicDiagram.SchematicDiagramClass;
				ISchematicAttributeContainer schematicAttributeContainer = (ISchematicAttributeContainer)schematicDiagramClass;

				ISchematicAttribute schematicAttribute = schematicAttributeContainer.GetSchematicAttribute("DigitizePropertiesLocation", true);

				if (schematicAttribute == null)
				{
					System.Windows.Forms.MessageBox.Show("Need an attribute named DigitizePropertiesLocation in the corresponding DiagramTemplate attributes");
					return;
				}

				path = (string)schematicAttribute.GetValue((ISchematicObject)schematicDiagram);

				if (IsRelative(path)) //Concat the workspace's path with this path
				{
					//current workspace path
					ISchematicDataset myDataset = schematicDiagramClass.SchematicDataset;
					if (myDataset != null)
					{
						ISchematicWorkspace mySchematicWorkspace = myDataset.SchematicWorkspace;
						if (mySchematicWorkspace != null)
						{
							ESRI.ArcGIS.Geodatabase.IWorkspace myWorkspace = mySchematicWorkspace.Workspace;
							if (myWorkspace != null)
							{
								string workspacePath = myWorkspace.PathName;
								//add "..\" to path to step back one level...
								string stepBack = "..\\";
								path = stepBack + path;

								path = System.IO.Path.Combine(workspacePath, path);
							}
						}
					}
				}
				//else keep the original hard path

				XmlReader reader = XmlReader.Create(path);

				m_dom.Load(reader);

				//Load Nodes
				XmlNodeList nodes = m_dom.SelectNodes("descendant::NodeFeature");

				//Clear combo box after each call
				cboNodeType.Items.Clear();
				foreach (XmlElement node in nodes)
				{
					cboNodeType.Items.Add(node.GetAttribute("FeatureClassName").ToString());
				}


				//Load Links
				XmlNodeList links = m_dom.SelectNodes("descendant::LinkFeature");

				//Clear combo box after each call
				cboLinkType.Items.Clear();
				foreach (XmlElement link in links)
				{
					cboLinkType.Items.Add(link.GetAttribute("FeatureClassName").ToString());
				}

				col = m_dom.SelectSingleNode("descendant::MandatoryColor");
				if (col != null)
				{
					myString = "System.Drawing.";
					myString = col.InnerText.ToString();
					m_MandatoryColor = System.Drawing.Color.FromName(myString);
				}

				col = m_dom.SelectSingleNode("descendant::FormName");
				if (col != null)
				{
					myString = col.InnerText.ToString();
					Text = myString;
				}


				XmlNodeList rels = m_dom.SelectNodes("descendant::Relation");
				if (rels.Count > 0)
					m_relations = rels;

				col = m_dom.SelectSingleNode("descendant::AutoClearAfterCreate");
				if ((col != null) && col.InnerText.ToString() == "True")
					m_autoClear = true;

			}
			catch (System.Exception e)
			{
				System.Windows.Forms.MessageBox.Show(e.Message);
			}

			m_Panel1 = Splitter.Panel1;
			m_Panel2 = Splitter.Panel2;
			m_curPanel = Splitter.Panel1;
			lblMode.Text = "Create Node";
			m_loading = false;
			m_clickPanel = false;
			m_schEltClass = null;

		}

		public bool ValidateFields()
		{
			bool blnValidated = true;
			System.Windows.Forms.MaskedTextBox mctrl = null;
			string errors = "";

			//check all mandatory fields
			System.Windows.Forms.Control ctrl2;

			foreach (System.Windows.Forms.Control ctrl in m_curPanel.Controls)
			{
				ctrl2 = ctrl;
				if (ctrl2.GetType() == typeof(System.Windows.Forms.Label))
				{
				}
				//ignore labels
				else
				{
					if ((string)ctrl2.Tag == "Mandatory")
					{
						if (ctrl2.GetType() == typeof(System.Windows.Forms.MaskedTextBox))
						{
							mctrl = (System.Windows.Forms.MaskedTextBox)ctrl2;
							if (mctrl.MaskCompleted == false)
							{
								blnValidated = false;
								if (errors.Length > 0)
									errors = "Incomplete mandatory field";
								else
									errors = errors + "\n" + "Incomplete mandatory field";
							}
						}
						else
						{
							if (ctrl2.Text.Length <= 0)
							{
								blnValidated = false;
								if (errors.Length <= 0)
									errors = "Incomplete mandatory field";
								else
									errors = errors + "\n" + "Incomplete mandatory field";
							}
						}
					}


					//check masked edit controls
					if (ctrl2.GetType() == typeof(System.Windows.Forms.MaskedTextBox))
					{
						mctrl = (System.Windows.Forms.MaskedTextBox)ctrl2;
						//if they typed something, but didn't complete it, then error
						//if they typed nothing and it is not mandatory, then it is OK
						if ((mctrl.Text.Length > 0) && mctrl.Modified && (!mctrl.MaskCompleted))
						{
							blnValidated = false;
							if (errors.Length > 0)
								errors = "Invalid entry in a masked text field";
							else
								errors = errors + "\n" + "Invalid entry in a masked text field";
						}
					}

					//check link connections
					if (m_curPanel == Splitter.Panel2)
					{
						//make sure that the relation is correct if it exists
						XmlNodeList fields = m_curLink.SelectNodes("descendant::Field");
						foreach (XmlElement field in fields)
						{
							//find the field with a type of "Relation"
							if (field.GetAttribute("Type") == "Relation")
							{
								ESRI.ArcGIS.Geodatabase.IDataset pDataset1;
								ESRI.ArcGIS.Geodatabase.IDataset pDataset2;

								string FeatureClass1Name;
								string FeatureClass2Name;

								pDataset1 = (ESRI.ArcGIS.Geodatabase.IDataset)m_schematicFeature1.SchematicElementClass;
								pDataset2 = (ESRI.ArcGIS.Geodatabase.IDataset)m_schematicFeature2.SchematicElementClass;

								FeatureClass1Name = pDataset1.Name;
								FeatureClass2Name = pDataset2.Name;

								foreach (XmlElement rel in m_relations)
								{
									//loop through the xml relations to match based on the from node and to node types

									if ((rel.GetAttribute("FromType") == FeatureClass1Name) && (rel.GetAttribute("ToType") == FeatureClass2Name))
									{
										//find the control with the pick list for relationships
										Control[] ctrls = m_curPanel.Controls.Find(field.GetAttribute("DBColumnName"), true);
										if (ctrls.Length > 0)
											ctrl2 = ctrls[0];

										XmlNodeList vals = rel.SelectNodes("descendant::Value");
										string myString = rel.GetAttribute("FromType") + "-" + rel.GetAttribute("ToType");
										//validate that the current control string is correct
										//if there are values, use them
										bool blnfound = false;
										if (vals.Count > 0)
										{
											foreach (XmlElement val in vals)
											{
												if (myString + "-" + val.InnerText.ToString() == ctrl2.Text)
												{
													blnfound = true;
													break;
												}
												else
													blnfound = false;
											}
											if (!blnfound)
											{
												blnValidated = false;
												if (errors.Length > 0)
													errors = "Invalid link connection";
												else
													errors = errors + "\n" + "Invalid link connection";
											}
										}
										else
										{
											if (ctrl2.Text != myString)
											{
												blnValidated = false;
												if (errors.Length > 0)
													errors = "Invalid link connection";
												else
													errors = errors + "\n" + "Invalid link connection";
											}
										}

										if (!blnfound) //fix connection list
										{
											XmlNodeList vlist = m_dom.SelectNodes("descendant::Relation");
											XmlNodeList rellist = null;
											System.Windows.Forms.ComboBox cboconn = (System.Windows.Forms.ComboBox)ctrl2;
											cboconn.Items.Clear();
											foreach (XmlElement v in vlist)
											{
												if ((v.GetAttribute("LinkType").ToString() == m_curLink.GetAttribute("FeatureClassName").ToString())
													//make sure the node types are ok
													&& ((v.GetAttribute("FromType").ToString() == FeatureClass1Name && (v.GetAttribute("ToType").ToString() == FeatureClass2Name))))
												{
													rellist = v.SelectNodes("descendant::Value");
													if (rellist.Count > 0)
													{
														foreach (XmlElement r in rellist)
														{
															myString = v.GetAttribute("FromType").ToString() + "-" + v.GetAttribute("ToType").ToString() + "-" + r.InnerText.ToString();
															cboconn.Items.Add(myString);
														}
													}
													else //assume they are not using subtypes
														cboconn.Items.Add(v.GetAttribute("FromType").ToString() + "-" + v.GetAttribute("ToType").ToString());
												}
											}
										}
									}
								}
							}
						}
					}
				}
			}

			if (errors.Length > 0)
			{

				if (m_curPanel == Splitter.Panel1)
				{
					btnOKPanel1.Visible = true;
					ErrorProvider1.SetError(btnOKPanel1, errors);
				}
				else
				{
					btnOKPanel2.Visible = true;
					ErrorProvider1.SetError(btnOKPanel2, errors);
				}
			}
			return blnValidated;

		}

		public void SelectionChanged()
		{

			try
			{
				System.Windows.Forms.Control ctrl = null;
				System.Windows.Forms.Control ctrl2 = null;
				Control[] ctrls = null;
				System.Collections.ArrayList ctrlstoremove = new System.Collections.ArrayList();
				string labelName = "";
				string featureClass = "";
				System.Windows.Forms.ComboBox cbo = null;
				System.Windows.Forms.Label lblMain = null;

				if (m_digitCommand == null)
					return;

				//clear any current elements
				if (m_schematicFeature1 != null)
				{
					m_schematicFeature1 = null;
					m_digitCommand.SchematicFeature1(m_schematicFeature1);
				}

				if (m_schematicFeature2 != null)
				{
					m_schematicFeature2 = null;
					m_digitCommand.SchematicFeature2(m_schematicFeature2);
				}

				if (m_curPanel == Splitter.Panel1)
				{
					labelName = "lblNodeLabel";
					featureClass = "descendant::NodeFeature";
					cbo = cboNodeType;
					lblMain = lblNodeLabel;
				}
				else
				{
					labelName = "lblLinkLabel";
					featureClass = "descendant::LinkFeature";
					cbo = cboLinkType;
					lblMain = lblLinkLabel;
				}

				foreach (System.Windows.Forms.Control ctrlfor in m_curPanel.Controls)
				{
					if (ctrlfor.Name.StartsWith("lbl") && (ctrlfor.Name.ToString() != labelName))
					{
						ctrls = m_curPanel.Controls.Find(ctrlfor.Name.Substring(3), true);
						ctrl2 = ctrls[0];
						ctrlstoremove.Add(ctrlfor);
						ctrlstoremove.Add(ctrl2);
					}
				}

				if (ctrlstoremove.Count > 0)
				{
					foreach (System.Windows.Forms.Control ctrol in ctrlstoremove)
					{
						m_curPanel.Controls.Remove(ctrol);
					}
				}

				XmlNodeList elems = null;
				m_curfrmWidth = m_curPanel.Width;
				elems = m_dom.SelectNodes(featureClass);

				bool blnFound = false;
				XmlElement elem = null;
				foreach (XmlElement elem0 in elems)
				{
					if (elem0.GetAttribute("FeatureClassName").ToString() == cbo.Text.ToString())
					{
						elem = elem0;
						blnFound = true;
						break;
					}
				}

				if (blnFound == false)
				{
					m_schEltClass = null;
					return;
				}

				if (m_curPanel == Splitter.Panel1)
					m_curNode = elem;
				else
					m_curLink = elem;

				//set grid
				elems = elem.SelectNodes("descendant::Field");
				int x = Splitter.Location.X;
				int y = lblMain.Location.Y + lblMain.Height + 5;

				System.Drawing.Point p = new System.Drawing.Point();

				foreach (XmlElement f in elems)
				{
					System.Windows.Forms.Label lbl = new System.Windows.Forms.Label();
					lbl.Name = "lbl" + f.GetAttribute("DBColumnName").ToString();
					lbl.Text = f.GetAttribute("DisplayName").ToString();
					lbl.AutoSize = true;
					m_curPanel.Controls.Add(lbl);
					p.X = 3;
					p.Y = y;
					lbl.Location = p;
					y = y + lbl.Height + 10;

					switch (f.GetAttribute("Type").ToString())
					{
						case "Text":
							System.Windows.Forms.TextBox tx = new System.Windows.Forms.TextBox();
							ctrl = tx;
							tx.Name = f.GetAttribute("DBColumnName").ToString();
							if (f.GetAttribute("Length").Length > 0)
								tx.MaxLength = System.Convert.ToInt32(f.GetAttribute("Length"));

							if (f.GetAttribute("Default").Length > 0)
								tx.Text = f.GetAttribute("Default");

							m_curPanel.Controls.Add(tx);
							break;

						case "Combo":

							System.Windows.Forms.ComboBox cb = new System.Windows.Forms.ComboBox();
							string defaulttext = "";
							ctrl = cb;
							cb.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
							cb.Name = f.GetAttribute("DBColumnName").ToString();
							XmlNodeList vlist = f.SelectNodes("descendant::Value");

							foreach (XmlElement v in vlist)
							{
								cb.Items.Add(v.InnerText.ToString());
								if (v.GetAttribute("Default").Length > 0)
									defaulttext = v.InnerText;
							}

							if (defaulttext.Length > 0)
								cb.Text = defaulttext;

							m_curPanel.Controls.Add(cb);
							break;

						case "MaskText":
							System.Windows.Forms.MaskedTextBox MaskControl = new System.Windows.Forms.MaskedTextBox();
							ctrl = MaskControl;
							string mask = "";
							MaskControl.Name = f.GetAttribute("DBColumnName").ToString();
							if (f.GetAttribute("Mask").Length > 0)
								mask = f.GetAttribute("Mask");
							else
								mask = "";

							MaskControl.Mask = mask;

							if (f.GetAttribute("Default").Length > 0)
								MaskControl.Text = f.GetAttribute("Default");

							MaskControl.Modified = false;
							m_curPanel.Controls.Add(MaskControl);
							MaskControl.TextChanged += new System.EventHandler(MaskedTextBox);

							break;

						case "Number":
							System.Windows.Forms.MaskedTextBox MaskControl2 = new System.Windows.Forms.MaskedTextBox();
							ctrl = MaskControl2;
							string mask2 = "";
							MaskControl2.Name = f.GetAttribute("DBColumnName").ToString();
							int i = 1;
							if (f.GetAttribute("Length").Length > 0)
							{
								for (i = 1; i <= System.Convert.ToInt32(f.GetAttribute("Length")); i++)
									mask = mask2 + "9";
							}
							else
							{
								if (f.GetAttribute("Mask").Length > 0)
									mask2 = f.GetAttribute("Mask");
								else
									mask2 = "";
							}

							MaskControl2.Mask = mask2;
							if (f.GetAttribute("Default").Length > 0)
								MaskControl2.Text = f.GetAttribute("Default");

							MaskControl2.Modified = false;
							m_curPanel.Controls.Add(MaskControl2);
							MaskControl2.TextChanged += new System.EventHandler(MaskedTextBox);
							break;

						case "Date":
							System.Windows.Forms.DateTimePicker dt = new System.Windows.Forms.DateTimePicker();
							ctrl = dt;
							dt.Name = f.GetAttribute("DBColumnName").ToString();
							dt.Value = DateTime.Now;
							dt.Format = System.Windows.Forms.DateTimePickerFormat.Short;
							m_curPanel.Controls.Add(dt);
							break;

						case "Relation":
							System.Windows.Forms.ComboBox cb2 = new System.Windows.Forms.ComboBox();
							ctrl = cb2;
							cb2.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
							cb2.Name = f.GetAttribute("DBColumnName").ToString();
							XmlNodeList vlist2 = m_dom.SelectNodes("descendant::Relation");
							XmlNodeList rellist = null;
							string str = null;
							foreach (XmlElement v in vlist2)
							{
								if (v.GetAttribute("LinkType").ToString() == elem.GetAttribute("FeatureClassName").ToString())
								{
									rellist = v.SelectNodes("descendant::Value");
									if (rellist.Count > 0)
										foreach (XmlElement r in rellist)
										{
											str = v.GetAttribute("FromType").ToString() + "-" + v.GetAttribute("ToType").ToString() + "-" + r.InnerText.ToString();
											cb2.Items.Add(str);
										}
									else //assume they are not using subtypes
										cb2.Items.Add(v.GetAttribute("FromType").ToString() + "-" + v.GetAttribute("ToType").ToString());
								}
							}
							//relations are always mandatory
							ctrl.BackColor = m_MandatoryColor;
							ctrl.Tag = "Mandatory";
							m_curPanel.Controls.Add(cb2);
							break;
					}

					if (f.GetAttribute("Mandatory").ToString() == "True")
					{
						ctrl.BackColor = m_MandatoryColor;
						ctrl.Tag = "Mandatory";
					}
				}
				ResizeForm();

				// set m_schEltClass
				XmlElement curElement = null;

				if (m_curPanel == Splitter.Panel1)
					curElement = m_curNode;
				else
					curElement = m_curLink;

				if (m_schEltClass == null)
					m_schEltClass = m_schEltClassCont.GetSchematicElementClass(curElement.GetAttribute("FeatureClassName"));
				else
					if (m_schEltClass.Name != curElement.GetAttribute("FeatureClassName"))
						m_schEltClass = m_schEltClassCont.GetSchematicElementClass(curElement.GetAttribute("FeatureClassName"));
			}

			catch (System.Exception e)
			{
				System.Windows.Forms.MessageBox.Show(e.Message);
			}
		}

		public bool CheckValidFeature(bool blnFromNode)
		{
			if (m_curLink == null)
				return false;

			XmlNodeList fields = m_curLink.SelectNodes("descendant::Field");

			foreach (XmlElement field in fields)
			{
				if (field.GetAttribute("Type") == "Relation")
				{
					foreach (XmlElement rel in m_relations)
					{
						if (blnFromNode)
						{
							if (m_schematicFeature1 == null)
								return false;

							if (rel.GetAttribute("FromType") == m_schematicFeature1.SchematicElementClass.Name)
								return true;
						}
						else
						{
							if (m_schematicFeature2 == null)
								return false;

							if (rel.GetAttribute("ToType") == m_schematicFeature2.SchematicElementClass.Name)
								return true;
						}
					}
					return false;
				}
			}
			return true;
		}

		public void FillValue(ref ISchematicFeature schFeature)
		{
			try
			{
				if (m_schEltClass == null)
					throw new Exception("Error getting Feature Class");

				int fldIndex;

				foreach (System.Windows.Forms.Control ctrl in m_curPanel.Controls)
				{
					if (!((ctrl.GetType() == typeof(System.Windows.Forms.Label)) || (ctrl.Name == "cboNodeType")))
					{
						if ((ctrl.GetType() == typeof(System.Windows.Forms.TextBox)) || (ctrl.GetType() == typeof(System.Windows.Forms.ComboBox)))
						{
							if (ctrl.Text.Length > 0)
							{
								//insert in DB
								fldIndex = schFeature.Fields.FindField(ctrl.Name);
								if (fldIndex > -1)
								{
									schFeature.set_Value(fldIndex, ctrl.Text);
									schFeature.Store();
								}
							}
						}
						else if (ctrl.GetType() == typeof(System.Windows.Forms.DateTimePicker))
						{
							fldIndex = schFeature.Fields.FindField(ctrl.Name);
							if (fldIndex > -1)
							{
								schFeature.set_Value(fldIndex, ctrl.Text);
								schFeature.Store();
							}
						}
						else if (ctrl.GetType() == typeof(System.Windows.Forms.MaskedTextBox))
						{
							System.Windows.Forms.MaskedTextBox mctrl = (System.Windows.Forms.MaskedTextBox)ctrl;
							if ((mctrl.Text.Length > 0) && (mctrl.Modified) && (mctrl.MaskCompleted))
							{

								fldIndex = schFeature.Fields.FindField(ctrl.Name);
								if (fldIndex > -1)
								{
									schFeature.set_Value(fldIndex, ctrl.Text);
									schFeature.Store();
								}
							}
						}
					}
				}

				return;
			}

			catch (System.Exception e)
			{
				System.Windows.Forms.MessageBox.Show(e.Message);
			}
		}

		private void ResizeForm()
		{
			try
			{
				System.Windows.Forms.Control ctr2;
				Control[] ctrls = null;
				System.Drawing.Point p = new System.Drawing.Point();
				//handle panel 1
				foreach (System.Windows.Forms.Control ctr in Splitter.Panel1.Controls)
				{
					if ((ctr.Name.StartsWith("lbl")) && (ctr.Name.ToString() != "lblNodeLabel"))
					{
						ctrls = Splitter.Panel1.Controls.Find(ctr.Name.Substring(3), true);
						if (ctrls.GetLength(0) > 0)
						{
							ctr2 = ctrls[0];

							p.Y = ctr.Location.Y;
							p.X = ctr.Width + 3;
							ctr2.Location = p;
							ctr2.Width = Splitter.Panel1.Width - ctr.Width - 5;
						}
					}
				}

				Splitter.Panel1.Refresh();
				//handle panel 2
				foreach (System.Windows.Forms.Control ctr in Splitter.Panel2.Controls)
				{
					if ((ctr.Name.StartsWith("lbl")) && (ctr.Name.ToString() != "lblLinkLabel"))
					{
						ctrls = Splitter.Panel2.Controls.Find(ctr.Name.Substring(3), true);
						if (ctrls.GetLength(0) > 0)
						{
							ctr2 = ctrls[0];
							p.Y = ctr.Location.Y;
							p.X = ctr.Width + 3;
							ctr2.Location = p;
							ctr2.Width = Splitter.Panel2.Width - ctr.Width - 5;
						}
					}
				}

				Splitter.Panel2.Refresh();
			}
			catch (System.Exception e)
			{
				System.Windows.Forms.MessageBox.Show(e.Message);
			}
		}

		private void cboType_SelectedIndexChanged(Object sender, System.EventArgs e)
		{
			SelectionChanged();
		}

		public void cboLinkType_SelectedIndexChanged(Object sender, System.EventArgs e)
		{
			SelectionChanged();
		}

		private void MaskedTextBox(Object sender, System.EventArgs e)
		{
			System.Windows.Forms.MaskedTextBox mctrl = (System.Windows.Forms.MaskedTextBox)sender;
			mctrl.Modified = true;
		}

		/// <summary>
		/// m_createNode is true when the active panel is the one that digitize nodes.
		/// </summary>
		/// <returns></returns>
		public bool CreateNode()
		{
			return m_createNode;
		}

		public bool AutoClear()
		{
			return m_autoClear;
		}

		public bool IsClosed()
		{
			return m_isClosed;
		}

		public ISchematicElementClass FeatureClass()
		{
			return m_schEltClass;
		}

		public void x(int x)
		{
			m_x = x;
			return;
		}

		public void y(int y)
		{
			m_y = y;
			return;
		}

		public void SchematicFeature1(ISchematicFeature schematicFeature)
		{
			m_schematicFeature1 = schematicFeature;
			return;
		}

		public void SchematicFeature2(ISchematicFeature schematicFeature)
		{
			m_schematicFeature2 = schematicFeature;
			return;
		}

		public void FrmApplication(ESRI.ArcGIS.Framework.IApplication app)
		{
			m_app = app;
			return;
		}

		private void Splitter_Panel2_Click(object sender, EventArgs e)
		{
			if (m_digitCommand == null)
				m_digitCommand = CurrentDigitTool.CurrentTool.currentDigit;

			if (m_digitCommand == null)
				return;
			
			m_createNode = false;

			if (m_curPanel != Splitter.Panel2)
			{

				m_curPanel = Splitter.Panel2;
				foreach (System.Windows.Forms.Control ctrl in Splitter.Panel1.Controls)
					ctrl.Enabled = false;

				foreach (System.Windows.Forms.Control ctrl in Splitter.Panel2.Controls)
					ctrl.Enabled = true;

				lblMode.Text = "Create Link";

				if (m_schematicFeature1 != null)
				{
					m_schematicFeature1 = null;
					m_digitCommand.SchematicFeature1(m_schematicFeature1);
				}

				if (m_schematicFeature2 != null)
				{
					m_schematicFeature2 = null;
					m_digitCommand.SchematicFeature2(m_schematicFeature2);
				}

				if (m_curPanel == Splitter.Panel1)
				{
					btnOKPanel1.Visible = false;
					ErrorProvider1.SetError(btnOKPanel1, "");
				}
				else
				{
					btnOKPanel2.Visible = false;
					ErrorProvider1.SetError(btnOKPanel2, "");
				}

				System.EventArgs e2 = new System.EventArgs();
				cboLinkType_SelectedIndexChanged(sender, e2);

			}

		}

		private void Splitter_Panel1_Click(object sender, EventArgs e)
		{
			if (m_digitCommand == null)
				m_digitCommand = CurrentDigitTool.CurrentTool.currentDigit;

			if (m_digitCommand != null)
				m_digitCommand.EndFeedBack();

			m_createNode = true;

			if (m_curPanel != Splitter.Panel1 || m_clickPanel == false)
			{
				m_clickPanel = true;
				m_curPanel = Splitter.Panel1;

				foreach (System.Windows.Forms.Control ctrl in Splitter.Panel2.Controls)
					ctrl.Enabled = false;

				foreach (System.Windows.Forms.Control ctrl in Splitter.Panel1.Controls)
					ctrl.Enabled = true;

				lblMode.Text = "Create Node";

				if (m_curPanel == Splitter.Panel1)
				{
					btnOKPanel1.Visible = false;
					ErrorProvider1.SetError(btnOKPanel1, "");
				}
				else
				{
					btnOKPanel2.Visible = false;
					ErrorProvider1.SetError(btnOKPanel2, "");
				}
				System.EventArgs e2 = new System.EventArgs();
				cboType_SelectedIndexChanged(sender, e2);
			}
		}

		private void btnOKPanel1_Click(object sender, EventArgs e)
		{
			//try to create the node at the original point
			if (m_digitCommand != null)
				m_digitCommand.MyMouseUp(m_x, m_y);

			ErrorProvider1.SetError(btnOKPanel1, "");
			btnOKPanel1.Visible = false;
		}

		private void btnOKPanel2_Click(object sender, EventArgs e)
		{
			//try to create the link with the original points
			if (m_digitCommand != null)
				m_digitCommand.MyMouseUp(m_x, m_y);

			ErrorProvider1.SetError(btnOKPanel1, "");
			btnOKPanel1.Visible = false;
		}

		private void WindowVisibleChange(object sender, EventArgs e)
		{

			if (m_digitCommand == null)
				m_digitCommand = CurrentDigitTool.CurrentTool.currentDigit;

			if (m_digitCommand == null)
				return;

			if ((this.Visible) && CurrentDigitTool.CurrentTool.currentDockableWindow.IsVisible())
			{			
				if (!(m_digitCommand.FromDeactivate()))
				{
					ESRI.ArcGIS.Framework.IApplication app = (ESRI.ArcGIS.Framework.IApplication) this.Hook;
					app.CurrentTool = null;
				}
			}
			m_digitCommand.EndFeedBack();
			m_digitCommand.FromDeactivate(false);
		}
		
		private void DigitWindow_Resize(object sender, EventArgs e)
		{
			if (!m_loading)
				ResizeForm();
		}
		 

		//IsRelative return true when the path start with "."
		private bool IsRelative(string path)
		{
			bool startwith = path.StartsWith(".");
			return startwith;
		}

	}
}

[Visual Basic .NET]

DockableDigit.vb

Imports System.Xml
Imports ESRI.ArcGIS.Schematic
Imports ESRI.ArcGIS.esriSystem

''' <summary>
''' Designer class of the dockable window add-in. It contains user interfaces that
''' make up the dockable window.
''' </summary>
Public Class DigitDockableWindow

	Private m_digitCommand As DigitTool

	Dim m_dom As XmlDocument = Nothing
	Dim m_loading As Boolean = True
	Dim m_clickPanel As Boolean = False
	Dim m_curfrmWidth As Long
	Friend WithEvents m_Panel1 As System.Windows.Forms.SplitterPanel
	Friend WithEvents m_Panel2 As System.Windows.Forms.SplitterPanel
	Dim m_curPanel As System.Windows.Forms.SplitterPanel
	Dim m_mandatoryColor As System.Drawing.Color = Drawing.Color.White

	Private m_schematicLayer As ISchematicLayer
	Private m_schematicFeature1 As ISchematicFeature = Nothing
	Private m_schematicFeature2 As ISchematicFeature = Nothing
	Private m_createNode As Boolean = True 'update when click on panel and on new

	'For button OK to retrieve the coordinate of the digit feature
	Private m_x As Integer
	Private m_y As Integer

	Private m_curLink As XmlElement = Nothing
	Private m_curNode As XmlElement = Nothing
	Private m_relations As XmlNodeList = Nothing
	Private m_schDataset As ISchematicDataset = Nothing
	Private m_schEltClassCont As ISchematicElementClassContainer = Nothing
	Private m_schEltClass As ISchematicElementClass = Nothing
	Private m_schematicInMemoryDiagram As ISchematicInMemoryDiagram = Nothing
	Private m_autoClear As Boolean = False
	Private m_schematicExtension As ESRI.ArcGIS.esriSystem.IExtension

	Public Sub New(ByVal hook As Object)

		' This call is required by the Windows Form Designer.
		InitializeComponent()

		' Add any initialization after the InitializeComponent() call.
		Me.Hook = hook

	End Sub



	Private m_hook As Object
	''' <summary>
	''' Host object of the dockable window
	''' </summary> 
	Public Property Hook() As Object
		Get
			Return m_hook
		End Get
		Set(ByVal value As Object)
			m_hook = value
		End Set
	End Property

	''' <summary>
	''' Implementation class of the dockable window add-in. It is responsible for
	''' creating and disposing the user interface class for the dockable window.
	''' </summary>
	Public Class AddinImpl
		Inherits ESRI.ArcGIS.Desktop.AddIns.DockableWindow

		Private m_windowUI As DigitDockableWindow

		Protected Overrides Function OnCreateChild() As System.IntPtr
			m_windowUI = New DigitDockableWindow(Me.Hook)

			CurrentDigitTool.CurrentTool.digitDockableWindow = m_windowUI

			If (CurrentDigitTool.CurrentTool.currentDigit IsNot Nothing) Then
				m_windowUI.m_digitCommand = CurrentDigitTool.CurrentTool.currentDigit
				m_windowUI.m_digitCommand.m_dockableDigit = m_windowUI
			Else
                ' CurrentDigitTool.CurrentTool.CurrentDigit is null when we open ArcMap, but OnCreateChild
                ' is called if the dockable window was shown during the last ArcMap session.
				Dim windowID As UID = New UIDClass
				windowID.Value = "DigitTool_DockableWindowVB"
				CurrentDigitTool.CurrentTool.currentDockableWindow = My.ArcMap.DockableWindowManager.GetDockableWindow(windowID)
			End If

			Return m_windowUI.Handle
		End Function

		Protected Overrides Sub Dispose(ByVal Param As Boolean)
			If m_windowUI IsNot Nothing Then
				m_windowUI.Dispose(Param)
			End If

			MyBase.Dispose(Param)

		End Sub

	End Class


	Public Sub Init(ByVal schematicLayer As ISchematicLayer)

		Try

			If schematicLayer Is Nothing Then
				Return
			End If

			m_schematicLayer = schematicLayer
			Dim col As XmlNode = Nothing
			Dim myString As String = Nothing

			m_schDataset = schematicLayer.SchematicDiagram.SchematicDiagramClass.SchematicDataset

			m_schEltClassCont = m_schDataset
			m_schematicInMemoryDiagram = schematicLayer.SchematicInMemoryDiagram

			m_dom = New XmlDocument

			Dim schematicDiagram As ISchematicDiagram
			schematicDiagram = m_schematicInMemoryDiagram.SchematicDiagram

			' get the path of the xml file that contains the definitions of the digitize dockable window
			Dim path As String

			Dim schematicDiagramClass As ISchematicDiagramClass = schematicDiagram.SchematicDiagramClass
			Dim schematicAttributeContainer As ISchematicAttributeContainer = schematicDiagramClass

			Dim schematicAttribute As ISchematicAttribute
			schematicAttribute = schematicAttributeContainer.GetSchematicAttribute("DigitizePropertiesLocation", True)

			If (schematicAttribute Is Nothing) Then
				MsgBox("Need an attribute named DigitizePropertiesLocation in the corresponding DiagramTemplate attributes")
				Return
			End If

			path = schematicAttribute.GetValue(schematicDiagram)


			If IsRelative(path) Then 'Concat the workspace's path with this path
				'current workspace path
				Dim myDataset As ISchematicDataset = schematicDiagramClass.SchematicDataset
				If Not myDataset Is Nothing Then
					Dim mySchematicWorkspace As ISchematicWorkspace = myDataset.SchematicWorkspace
					If Not mySchematicWorkspace Is Nothing Then
						Dim myWorkspace As ESRI.ArcGIS.Geodatabase.IWorkspace = mySchematicWorkspace.Workspace
						If Not myWorkspace Is Nothing Then
							Dim workspacePath As String = myWorkspace.PathName
							'add "..\" to path to step back one level...
							Dim stepBack As String = "..\"
							path = stepBack + path

							path = System.IO.Path.Combine(workspacePath, path)

						End If
					End If
				End If
			End If
			'else keep the original hard path

			Dim reader As System.Xml.XmlReader = System.Xml.XmlReader.Create(path)

			m_dom.Load(reader)

			'Load Nodes
			Dim nodes As XmlNodeList = Nothing
			nodes = m_dom.SelectNodes("descendant::NodeFeature")

			cboNodeType.Items.Clear()
			Dim node As XmlElement = Nothing
			For Each node In nodes
				Me.cboNodeType.Items.Add(node.GetAttribute("FeatureClassName").ToString)
			Next

			'Load Links
			Dim links As XmlNodeList = Nothing
			links = m_dom.SelectNodes("descendant::LinkFeature")

			cboLinkType.Items.Clear()
			Dim link As XmlElement = Nothing
			For Each link In links
				Me.cboLinkType.Items.Add(link.GetAttribute("FeatureClassName").ToString)
			Next

			col = m_dom.SelectSingleNode("descendant::MandatoryColor")
			If Not col Is Nothing Then
				myString = "System.Drawing."
				myString = col.InnerText.ToString
				m_mandatoryColor = System.Drawing.Color.FromName(myString)
			End If

			col = m_dom.SelectSingleNode("descendant::FormName")
			If Not col Is Nothing Then
				myString = col.InnerText.ToString
				Me.Text = myString
			End If


			Dim rels As XmlNodeList = m_dom.SelectNodes("descendant::Relation")
			If rels.Count > 0 Then
				m_relations = rels
			End If

			col = m_dom.SelectSingleNode("descendant::AutoClearAfterCreate")
			If Not col Is Nothing Then
				If col.InnerText.ToString = "True" Then
					m_autoClear = True
				End If
			End If
		Catch ex As Exception

		End Try

		m_Panel1 = Splitter.Panel1
		m_Panel2 = Splitter.Panel2
		m_curPanel = Splitter.Panel1
		lblMode.Text = "Create Node"
		m_loading = False
		m_clickPanel = False
		m_schEltClass = Nothing

	End Sub


	Public Function ValidateFields() As Boolean
		Dim blnValidated As Boolean = True
		Dim ctrl As Windows.Forms.Control = Nothing
		Dim mctrl As Windows.Forms.MaskedTextBox = Nothing
		Dim errors As String = ""

		'check all mandatory fields
		For Each ctrl In m_curPanel.Controls
			If TypeOf ctrl Is Windows.Forms.Label Then
				'ignore labels
			Else
				If ctrl.Tag = "Mandatory" Then
					If TypeOf ctrl Is Windows.Forms.MaskedTextBox Then
						mctrl = ctrl
						If mctrl.MaskCompleted = False Then
							blnValidated = False
							If errors.Length > 0 Then
								errors = "Incomplete mandatory field"
							Else
								errors = errors & vbCrLf & "Incomplete mandatory field"
							End If
						End If
					Else
						If Not ctrl.Text.Length > 0 Then
							blnValidated = False
							If errors.Length > 0 Then
								errors = "Incomplete mandatory field"
							Else
								errors = errors & vbCrLf & "Incomplete mandatory field"
							End If
						End If
					End If
				End If

				'check masked edit controls
				If TypeOf ctrl Is Windows.Forms.MaskedTextBox Then
					mctrl = ctrl
					'if they typed something, but didn't complete it, then error
					'if they typed nothing and it is not mandatory, then it is OK
					If mctrl.Text.Length > 0 Then
						If mctrl.Modified = True Then
							If mctrl.MaskCompleted = False Then
								blnValidated = False
								If errors.Length > 0 Then
									errors = "Invalid entry in a masked text field"
								Else
									errors = errors & vbCrLf & "Invalid entry in a masked text field"
								End If
							End If
						End If
					End If
				End If
				'End If

				'check link connections
				If m_curPanel Is Splitter.Panel2 Then
					'make sure that the relation is correct if it exists
					Dim fields As XmlNodeList = m_curLink.SelectNodes("descendant::Field")
					Dim field As XmlElement = Nothing
					For Each field In fields
						'find the field with a type of "Relation"
						If field.GetAttribute("Type") = "Relation" Then
							Dim rel As XmlElement = Nothing

							Dim dataset1 As ESRI.ArcGIS.Geodatabase.IDataset
							Dim dataset2 As ESRI.ArcGIS.Geodatabase.IDataset

							Dim FeatureClass1Name As String
							Dim FeatureClass2Name As String

							dataset1 = m_schematicFeature1.SchematicElementClass
							dataset2 = m_schematicFeature2.SchematicElementClass

							FeatureClass1Name = dataset1.Name
							FeatureClass2Name = dataset2.Name

							For Each rel In m_relations
                                'loop through the xml relations to match based on the from node and to node types

								If rel.GetAttribute("FromType") = FeatureClass1Name And rel.GetAttribute("ToType") = FeatureClass2Name Then
                                    'find the control with the pick list for relationships
									Dim ctrls() As Windows.Forms.Control = m_curPanel.Controls.Find(field.GetAttribute("DBColumnName"), True)
									If ctrls.Length > 0 Then
										ctrl = ctrls(0)
									End If
									Dim vals As XmlNodeList = rel.SelectNodes("descendant::Value")
									Dim val As XmlElement = Nothing
									Dim myString As String = rel.GetAttribute("FromType") & "-" & rel.GetAttribute("ToType")
									'validate that the current control string is correct
									'if there are values, use them
									Dim blnfound As Boolean = False
									If vals.Count > 0 Then
										For Each val In vals
											If myString & "-" & val.InnerText.ToString = ctrl.Text Then
												blnfound = True
												Exit For
											Else
												blnfound = False
											End If
										Next
										If blnfound = False Then
											blnValidated = False
											If errors.Length > 0 Then
												errors = "Invalid link connection"
											Else
												errors = errors & vbCrLf & "Invalid link connection"
											End If
										End If
									Else
										If ctrl.Text <> myString Then
											blnValidated = False
											If errors.Length > 0 Then
												errors = "Invalid link connection"
											Else
												errors = errors & vbCrLf & "Invalid link connection"
											End If
										End If
									End If
									If blnfound = False Then 'fix connection list
										Dim vlist As XmlNodeList = m_dom.SelectNodes("descendant::Relation")
										Dim v As XmlElement
										Dim rellist As XmlNodeList = Nothing
										Dim r As XmlElement = Nothing
										Dim cboconn As Windows.Forms.ComboBox = ctrl
										cboconn.Items.Clear()
										For Each v In vlist
											If v.GetAttribute("LinkType").ToString = m_curLink.GetAttribute("FeatureClassName").ToString Then
												'make sure the node types are ok
												If v.GetAttribute("FromType").ToString = FeatureClass1Name And v.GetAttribute("ToType").ToString = FeatureClass2Name Then	'ToString ??
													rellist = v.SelectNodes("descendant::Value")
													If rellist.Count > 0 Then	'
														For Each r In rellist
															myString = v.GetAttribute("FromType").ToString & "-" & v.GetAttribute("ToType").ToString & "-" & r.InnerText.ToString
															cboconn.Items.Add(myString)
														Next
													Else 'assume they are not using subtypes
														cboconn.Items.Add(v.GetAttribute("FromType").ToString & "-" & v.GetAttribute("ToType").ToString)
													End If
												End If
											End If
										Next
									End If
								End If
							Next
						End If
					Next
				End If
			End If
		Next

		If errors.Length > 0 Then

			If m_curPanel Is Splitter.Panel1 Then
				btnOKPanel1.Visible = True

				ErrorProvider1.SetError(btnOKPanel1, errors)
			Else
				btnOKPanel2.Visible = True
				ErrorProvider1.SetError(btnOKPanel2, errors)
			End If
		End If
		Return blnValidated
	End Function

	Public Sub cboType_SelectedValueChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles cboNodeType.SelectedValueChanged
		SelectionChanged()
	End Sub


	Public Sub SelectionChanged()
		Try
			Dim ctrl As System.Windows.Forms.Control = Nothing
			Dim ctrl2 As System.Windows.Forms.Control = Nothing
			Dim ctrls() As Object = Nothing
			Dim ctrlstoremove As Collection = New Collection
			Dim labelName As String = ""
			Dim featureClass As String = ""
			Dim cbo As Windows.Forms.ComboBox = Nothing
			Dim lblMain As Windows.Forms.Label = Nothing

			If m_digitCommand Is Nothing Then
				Return
			End If

			'clear any current elements
			If Not m_schematicFeature1 Is Nothing Then
				m_schematicFeature1 = Nothing
				m_digitCommand.SchematicFeature1() = m_schematicFeature1
			End If
			If Not m_schematicFeature2 Is Nothing Then
				m_schematicFeature2 = Nothing
				m_digitCommand.SchematicFeature2() = m_schematicFeature2
			End If

			If m_curPanel Is Splitter.Panel1 Then
				labelName = "lblNodeLabel"
				featureClass = "descendant::NodeFeature"
				cbo = cboNodeType
				lblMain = lblNodeLabel
			Else
				labelName = "lblLinkLabel"
				featureClass = "descendant::LinkFeature"
				cbo = cboLinkType
				lblMain = lblLinkLabel
			End If

			For Each ctrl In m_curPanel.Controls
				If ctrl.Name.StartsWith("lbl") And ctrl.Name.ToString <> labelName Then
					ctrls = m_curPanel.Controls.Find(ctrl.Name.Substring(3), True)
					ctrl2 = ctrls(0)
					ctrlstoremove.Add(ctrl)
					ctrlstoremove.Add(ctrl2)
				End If
			Next

			If ctrlstoremove.Count > 0 Then
				Dim ctrol As System.Windows.Forms.Control
				For Each ctrol In ctrlstoremove
					m_curPanel.Controls.Remove(ctrol)
					ctrol = Nothing
				Next
			End If

			Dim elem As XmlElement = Nothing
			Dim elems As XmlNodeList = Nothing
			m_curfrmWidth = m_curPanel.Width
			elems = m_dom.SelectNodes(featureClass)

			Dim blnFound As Boolean = False
			For Each elem In elems
				If elem.GetAttribute("FeatureClassName").ToString = cbo.Text.ToString Then
					blnFound = True
					Exit For
				End If
			Next
			If blnFound = False Then
				m_schEltClass = Nothing
				Exit Sub
			End If

			If m_curPanel Is Splitter.Panel1 Then
				m_curNode = elem
			Else
				m_curLink = elem
			End If

			'set grid
			elems = elem.SelectNodes("descendant::Field")
			Dim f As XmlElement
			Dim x As Integer = Splitter.Location.X
			Dim y As Integer = lblMain.Location.Y + lblMain.Height + 5

			Dim p As New System.Drawing.Point

			Dim rcount As Integer = 1
			For Each f In elems
				Dim lbl As New System.Windows.Forms.Label
				lbl.Name = "lbl" & f.GetAttribute("DBColumnName").ToString
				lbl.Text = f.GetAttribute("DisplayName").ToString
				lbl.AutoSize = True
				m_curPanel.Controls.Add(lbl)
				p.X = 3
				p.Y = y
				lbl.Location = p
				y = y + lbl.Height + 10
				Select Case f.GetAttribute("Type").ToString
					Case "Text"
						Dim tx As New System.Windows.Forms.TextBox
						ctrl = tx
						tx.Name = f.GetAttribute("DBColumnName").ToString
						If f.GetAttribute("Length").Length > 0 Then
							tx.MaxLength = CInt(f.GetAttribute("Length"))
						End If
						If f.GetAttribute("Default").Length > 0 Then
							tx.Text = f.GetAttribute("Default")
						End If
						m_curPanel.Controls.Add(tx)

					Case "Combo"
						Dim cb As New System.Windows.Forms.ComboBox
						Dim defaulttext As String = ""
						ctrl = cb
						cb.DropDownStyle = Windows.Forms.ComboBoxStyle.DropDownList
						cb.Name = f.GetAttribute("DBColumnName").ToString
						Dim vlist As XmlNodeList = f.SelectNodes("descendant::Value")
						Dim v As XmlElement
						For Each v In vlist
							cb.Items.Add(v.InnerText.ToString)
							If v.GetAttribute("Default").Length > 0 Then
								defaulttext = v.InnerText
							End If
						Next
						If defaulttext.Length > 0 Then
							cb.Text = defaulttext
						End If
						m_curPanel.Controls.Add(cb)

					Case "MaskText"
						Dim MaskControl As New System.Windows.Forms.MaskedTextBox
						ctrl = MaskControl
						Dim mask As String = ""
						MaskControl.Name = f.GetAttribute("DBColumnName").ToString
						If f.GetAttribute("Mask").Length > 0 Then
							mask = f.GetAttribute("Mask")
						Else
							mask = ""
						End If
						MaskControl.Mask = mask
						If f.GetAttribute("Default").Length > 0 Then
							MaskControl.Text = f.GetAttribute("Default")
						End If
						MaskControl.Modified = False
						m_curPanel.Controls.Add(MaskControl)
						AddHandler MaskControl.TextChanged, AddressOf MaskedTextBox

					Case "Number"
						Dim MaskControl As New System.Windows.Forms.MaskedTextBox
						ctrl = MaskControl
						Dim mask As String = ""
						MaskControl.Name = f.GetAttribute("DBColumnName").ToString
						Dim i As Int16 = 1
						If f.GetAttribute("Length").Length > 0 Then
							For i = 1 To CInt(f.GetAttribute("Length"))
								mask = mask & "9"
							Next
						Else
							If f.GetAttribute("Mask").Length > 0 Then
								mask = f.GetAttribute("Mask")
							Else
								mask = ""
							End If
						End If
						MaskControl.Mask = mask
						If f.GetAttribute("Default").Length > 0 Then
							MaskControl.Text = CInt(f.GetAttribute("Default"))
						End If
						MaskControl.Modified = False
						m_curPanel.Controls.Add(MaskControl)
						AddHandler MaskControl.TextChanged, AddressOf MaskedTextBox

					Case "Date"
						Dim dt As New System.Windows.Forms.DateTimePicker
						ctrl = dt
						dt.Name = f.GetAttribute("DBColumnName").ToString
						dt.Value = Now.Date
						dt.Format = Windows.Forms.DateTimePickerFormat.Short
						m_curPanel.Controls.Add(dt)

					Case "Relation"
						Dim cb As New System.Windows.Forms.ComboBox
						ctrl = cb
						cb.DropDownStyle = Windows.Forms.ComboBoxStyle.DropDownList
						cb.Name = f.GetAttribute("DBColumnName").ToString
						Dim vlist As XmlNodeList = m_dom.SelectNodes("descendant::Relation")
						Dim v As XmlElement
						Dim rellist As XmlNodeList = Nothing
						Dim r As XmlElement = Nothing
						Dim myString As String = Nothing
						For Each v In vlist
							If v.GetAttribute("LinkType").ToString = elem.GetAttribute("FeatureClassName").ToString Then
								rellist = v.SelectNodes("descendant::Value")
								If rellist.Count > 0 Then	'
									For Each r In rellist
										myString = v.GetAttribute("FromType").ToString & "-" & v.GetAttribute("ToType").ToString & "-" & r.InnerText.ToString
										cb.Items.Add(myString)
									Next
								Else 'assume they are not using subtypes
									cb.Items.Add(v.GetAttribute("FromType").ToString & "-" & v.GetAttribute("ToType").ToString)
								End If
							End If
						Next
						'relations are always mandatory
						ctrl.BackColor = m_mandatoryColor
						ctrl.Tag = "Mandatory"
						m_curPanel.Controls.Add(cb)

				End Select
				If f.GetAttribute("Mandatory").ToString = "True" Then
					ctrl.BackColor = m_mandatoryColor
					ctrl.Tag = "Mandatory"
				End If
			Next
			ResizeForm()

			' set m_schEltClass
			Dim schElement As ISchematicElement = Nothing
			Dim curElement As XmlElement = Nothing

			If m_curPanel Is Splitter.Panel1 Then
				curElement = m_curNode
			Else
				curElement = m_curLink
			End If

			If m_schEltClass Is Nothing Then
				m_schEltClass = m_schEltClassCont.GetSchematicElementClass(curElement.GetAttribute("FeatureClassName"))
			Else
				If m_schEltClass.Name <> curElement.GetAttribute("FeatureClassName") Then
					m_schEltClass = m_schEltClassCont.GetSchematicElementClass(curElement.GetAttribute("FeatureClassName"))
				End If
			End If


		Catch ex As Exception
			MsgBox(ex.Message)
		End Try
	End Sub


	Public Function CheckValidFeature(ByVal blnFromNode As Boolean) As Boolean

		If m_curLink Is Nothing Then
			Return False
		End If

		Dim fields As XmlNodeList = m_curLink.SelectNodes("descendant::Field")
		Dim field As XmlElement = Nothing
		For Each field In fields
			If field.GetAttribute("Type") = "Relation" Then
				Dim rel As XmlElement = Nothing
				For Each rel In m_relations
					If blnFromNode = True Then
						If m_schematicFeature1 Is Nothing Then
							Return False
						End If
						If rel.GetAttribute("FromType") = m_schematicFeature1.SchematicElementClass.Name Then
							Return True
						End If
					Else
						If m_schematicFeature2 Is Nothing Then
							Return False
						End If
						If rel.GetAttribute("ToType") = m_schematicFeature2.SchematicElementClass.Name Then
							Return True
						End If
					End If
				Next
				Return False
			End If
		Next
		Return True
	End Function

	Private Sub frmDigitize_Resize(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Resize
		If m_loading = False Then
			ResizeForm()
		End If
	End Sub

	Private Sub ResizeForm()
		Try
			Dim ctr As System.Windows.Forms.Control
			Dim ctr2 As System.Windows.Forms.Control
			Dim ctrls() As Object
			Dim p As System.Drawing.Point
			'handle panel 1
			For Each ctr In Splitter.Panel1.Controls
				If ctr.Name.StartsWith("lbl") And ctr.Name.ToString <> "lblNodeLabel" Then
					'ctr.Width = Splitter.panel1.Width / 3
					'MsgBox(ctr.Name.Substring(3))
					ctrls = Splitter.Panel1.Controls.Find(ctr.Name.Substring(3), True)
					If ctrls.GetLength(0) > 0 Then
						ctr2 = ctrls(0)

						p.Y = ctr.Location.Y
						p.X = ctr.Width + 3
						ctr2.Location = p
						ctr2.Width = Splitter.Panel1.Width - ctr.Width - 5
					End If
				End If
			Next
			Splitter.Panel1.Refresh()
			'handle panel 2
			For Each ctr In Splitter.Panel2.Controls
				If ctr.Name.StartsWith("lbl") And ctr.Name.ToString <> "lblLinkLabel" Then
					'ctr.Width = Splitter.panel1.Width / 3
					'MsgBox(ctr.Name.Substring(3))
					ctrls = Splitter.Panel2.Controls.Find(ctr.Name.Substring(3), True)
					If ctrls.GetLength(0) > 0 Then
						ctr2 = ctrls(0)
						p.Y = ctr.Location.Y
						p.X = ctr.Width + 3
						ctr2.Location = p
						ctr2.Width = Splitter.Panel2.Width - ctr.Width - 5
					End If
				End If
			Next
			Splitter.Panel2.Refresh()
		Catch ex As Exception
			MsgBox(ex.Message)
		End Try
	End Sub

	Private Sub cboLinkType_SelectedValueChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles cboLinkType.SelectedValueChanged
		SelectionChanged()
	End Sub

	Public Sub FillValue(ByRef schFeature As ISchematicFeature)

		Try

			If m_schEltClass Is Nothing Then
				Err.Raise(513, "CreateNewFeature", "Error getting Feature Class")
			End If

			Dim fldIndex As Int16 = Nothing

			Dim ctrl As Windows.Forms.Control = Nothing
			For Each ctrl In m_curPanel.Controls
				If TypeOf ctrl Is Windows.Forms.Label Or ctrl.Name = "cboNodeType" Then
					'do nothing
				Else
					If TypeOf ctrl Is Windows.Forms.TextBox Or TypeOf ctrl Is Windows.Forms.ComboBox Then
						If ctrl.Text.Length > 0 Then
							'insert in DB
							fldIndex = schFeature.Fields.FindField(ctrl.Name)
							If (fldIndex > -1) Then
								schFeature.Value(fldIndex) = ctrl.Text
								schFeature.Store()
							End If
						End If
					ElseIf TypeOf ctrl Is Windows.Forms.DateTimePicker Then
						fldIndex = schFeature.Fields.FindField(ctrl.Name)
						If (fldIndex > -1) Then
							schFeature.Value(fldIndex) = ctrl.Text
							schFeature.Store()
						End If

					ElseIf TypeOf ctrl Is Windows.Forms.MaskedTextBox Then
						Dim mctrl As Windows.Forms.MaskedTextBox = ctrl
						If mctrl.Text.Length > 0 Then
							If mctrl.Modified = True Then
								If mctrl.MaskCompleted = True Then
									fldIndex = schFeature.Fields.FindField(ctrl.Name)
									If (fldIndex > -1) Then
										schFeature.Value(fldIndex) = ctrl.Text
										schFeature.Store()
									End If
								End If
							End If
						End If

					End If

				End If
			Next

			Return

		Catch ex As Exception
			MsgBox(ex.Message)
		End Try
	End Sub


	Private Sub btnOKPanel1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnOKPanel1.Click
		'try to create the node at the original point
		If Not m_digitCommand Is Nothing Then
			m_digitCommand.MyMouseUp(m_x, m_y)
		End If

		ErrorProvider1.SetError(btnOKPanel1, "")
		btnOKPanel1.Visible = False
	End Sub

	Private Sub btnOKPanel2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnOKPanel2.Click
		'try to create the link with the original points
		m_digitCommand.MyMouseUp(m_x, m_y)

		ErrorProvider1.SetError(btnOKPanel1, "")
		btnOKPanel1.Visible = False
	End Sub

	Private Sub MaskedTextBox(ByVal sender As Object, ByVal e As EventArgs)
		Dim mctrl As Windows.Forms.MaskedTextBox = sender
		mctrl.Modified = True
	End Sub

	Private Sub WindowVisibleChange() Handles Me.VisibleChanged

		If (m_digitCommand Is Nothing) Then
			m_digitCommand = CurrentDigitTool.CurrentTool.currentDigit
		End If

		If (m_digitCommand Is Nothing) Then
			Return
		End If

		If Me.Visible = True And CurrentDigitTool.CurrentTool.currentDockableWindow.IsVisible() = True Then
			If m_digitCommand.FromDeactivate = False Then
				Dim app As ESRI.ArcGIS.Framework.IApplication = Me.Hook
				app.CurrentTool = Nothing
			End If
		End If

		m_digitCommand.EndFeedBack()
		m_digitCommand.FromDeactivate = False

	End Sub

	Friend ReadOnly Property CreateNode() As Boolean
		Get
			Return m_createNode
		End Get
	End Property

	Friend ReadOnly Property AutoClear() As Boolean
		Get
			Return m_autoClear
		End Get
	End Property

	Friend ReadOnly Property FeatureClass() As ISchematicElementClass
		Get
			Return m_schEltClass
		End Get
	End Property

	Public WriteOnly Property x() As Integer
		Set(ByVal Value As Integer)
			m_x = Value
		End Set
	End Property

	Public WriteOnly Property y() As Integer
		Set(ByVal Value As Integer)
			m_y = Value
		End Set
	End Property

	Public WriteOnly Property SchematicFeature1() As ISchematicFeature
		Set(ByVal Value As ISchematicFeature)
			m_schematicFeature1 = Value
		End Set
	End Property

	Public WriteOnly Property SchematicFeature2() As ISchematicFeature
		Set(ByVal Value As ISchematicFeature)
			m_schematicFeature2 = Value
		End Set
	End Property

	'IsRelative return true when the path start with "."
	Private Function IsRelative(ByVal path As String) As Boolean
		If path(0) = "." Then
			Return True
		Else
			Return False
		End If
	End Function

	Private Sub cboNodeType_SelectedIndexChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cboNodeType.SelectedIndexChanged
		SelectionChanged()
	End Sub


	Private Sub Splitter_Panel1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Splitter.Panel1.Click
		If (m_digitCommand Is Nothing) Then
			m_digitCommand = CurrentDigitTool.CurrentTool.currentDigit
		End If

		If Not m_digitCommand Is Nothing Then
			m_digitCommand.EndFeedBack()
		End If

		m_createNode = True

		If (Not m_curPanel Is Splitter.Panel1) Or (m_clickPanel = False) Then

			m_clickPanel = True

			Dim ctrl As Windows.Forms.Control
			m_curPanel = Splitter.Panel1
			For Each ctrl In Splitter.Panel2.Controls
				ctrl.Enabled = False
			Next
			For Each ctrl In Splitter.Panel1.Controls
				ctrl.Enabled = True
			Next
			lblMode.Text = "Create Node"

			If m_curPanel Is Splitter.Panel1 Then
				btnOKPanel1.Visible = False
				ErrorProvider1.SetError(btnOKPanel1, "")
			Else
				btnOKPanel2.Visible = False
				ErrorProvider1.SetError(btnOKPanel2, "")
			End If
			Dim e2 As New System.EventArgs
			cboType_SelectedValueChanged(sender, e2)
		End If
	End Sub

	Private Sub Splitter_Panel2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Splitter.Panel2.Click
		If (m_digitCommand Is Nothing) Then
			m_digitCommand = CurrentDigitTool.CurrentTool.currentDigit
		End If

		If m_digitCommand Is Nothing Then
			Return
		End If

		m_createNode = False

		If Not m_curPanel Is Splitter.Panel2 Then
			Dim ctrl As Windows.Forms.Control
			m_curPanel = Splitter.Panel2
			For Each ctrl In Splitter.Panel1.Controls
				ctrl.Enabled = False
			Next
			For Each ctrl In Splitter.Panel2.Controls
				ctrl.Enabled = True
			Next
			lblMode.Text = "Create Link"

			If Not m_schematicFeature1 Is Nothing Then
				m_schematicFeature1 = Nothing
				m_digitCommand.SchematicFeature1() = m_schematicFeature1
			End If
			If Not m_schematicFeature2 Is Nothing Then
				m_schematicFeature2 = Nothing
				m_digitCommand.SchematicFeature2() = m_schematicFeature2
			End If

			If m_curPanel Is Splitter.Panel1 Then
				btnOKPanel1.Visible = False
				ErrorProvider1.SetError(btnOKPanel1, "")
			Else
				btnOKPanel2.Visible = False
				ErrorProvider1.SetError(btnOKPanel2, "")
			End If
			Dim e2 As New System.EventArgs
			cboLinkType_SelectedValueChanged(sender, e2)
		End If
	End Sub
End Class