using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.IO;
using System.Reflection;
using System.Xml;
using System.Xml.Schema;
/*
* An XSD-driven dynamic form creator and XML data editor.
* Using an Xml Schema Definition (XSD) document, this utility dynamically generates a data entry form to create and edit XML data.
*
* Things to do:
*
* Implement a "Close" menu item (closes both XSD and XML)
* Look at XmlSchemaSimpleType and derived classes like XmlSchemaSimpleTypeList
*
* Bugs:
*
* A simple type that references a simple type with enumerations doesn't show up as a combo box. Why?
* A local element ref'ing a global element type'ing a complex element caused an assertion. Why?
* An element type'd as a complex type is causing problems
*
* In the Schema Editor:
* 1. Can't remove elements of simple type (simple types that are a children to a complex type)
* 2. missing feature: DEL key to remove nodes
* 3. Renaming a complex type of a complex type caused the child complex type to have a name, instead of the parent element of complex type
* 4. Did min inclusive create a max inclusive node instead?
* 5. If a simple type has a global simple type restriction base (instead of a built-in one) it doesn't show up in the global list
*
*/
namespace xmlDataEditor
{
/// <summary>
/// Summary description for Form1.
/// </summary>
public class Form1 : System.Windows.Forms.Form
{
private System.Windows.Forms.Panel pnlDynForm;
private System.Windows.Forms.MainMenu mnuMain;
private System.Windows.Forms.MenuItem menuItem1;
private System.Windows.Forms.MenuItem mnuOpenXSD;
private System.Windows.Forms.MenuItem menuItem6;
private System.Windows.Forms.OpenFileDialog dlgOpen;
private System.Windows.Forms.MenuItem mnuExit;
private XmlSchema schema=null;
private XmlDataDocument doc=null;
private string xmlFileName=null;
private Hashtable tableInfo=new Hashtable();
private System.Windows.Forms.MenuItem mnuAbout;
private System.Windows.Forms.MenuItem mnuOpenXML;
private System.Windows.Forms.MenuItem mnuSaveXmlAs;
private System.Windows.Forms.MenuItem menuItem2;
private System.Windows.Forms.SaveFileDialog saveXmlAsDlg;
private System.Windows.Forms.MenuItem mnuSaveXML;
private System.Windows.Forms.MenuItem mnuInferXSD;
private System.Windows.Forms.Label label1;
private System.Windows.Forms.Button btnXpathQuery;
private System.Windows.Forms.TextBox edXPath;
private System.Windows.Forms.Label label2;
private System.Windows.Forms.TextBox edNewRowOn;
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.Container components = null;
public Form1()
{
//
// Required for Windows Form Designer support
//
InitializeComponent();
//
// TODO: Add any constructor code after InitializeComponent call
//
}
/// <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 Windows Form 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()
{
this.pnlDynForm = new System.Windows.Forms.Panel();
this.mnuMain = new System.Windows.Forms.MainMenu();
this.menuItem1 = new System.Windows.Forms.MenuItem();
this.mnuOpenXSD = new System.Windows.Forms.MenuItem();
this.mnuInferXSD = new System.Windows.Forms.MenuItem();
this.mnuOpenXML = new System.Windows.Forms.MenuItem();
this.menuItem2 = new System.Windows.Forms.MenuItem();
this.mnuSaveXML = new System.Windows.Forms.MenuItem();
this.mnuSaveXmlAs = new System.Windows.Forms.MenuItem();
this.menuItem6 = new System.Windows.Forms.MenuItem();
this.mnuExit = new System.Windows.Forms.MenuItem();
this.mnuAbout = new System.Windows.Forms.MenuItem();
this.dlgOpen = new System.Windows.Forms.OpenFileDialog();
this.saveXmlAsDlg = new System.Windows.Forms.SaveFileDialog();
this.label1 = new System.Windows.Forms.Label();
this.edXPath = new System.Windows.Forms.TextBox();
this.btnXpathQuery = new System.Windows.Forms.Button();
this.label2 = new System.Windows.Forms.Label();
this.edNewRowOn = new System.Windows.Forms.TextBox();
this.SuspendLayout();
//
// pnlDynForm
//
this.pnlDynForm.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.pnlDynForm.AutoScroll = true;
this.pnlDynForm.Location = new System.Drawing.Point(0, 80);
this.pnlDynForm.Name = "pnlDynForm";
this.pnlDynForm.Size = new System.Drawing.Size(528, 704);
this.pnlDynForm.TabIndex = 0;
//
// mnuMain
//
this.mnuMain.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] {
this.menuItem1,
this.mnuAbout});
//
// menuItem1
//
this.menuItem1.Index = 0;
this.menuItem1.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] {
this.mnuOpenXSD,
this.mnuInferXSD,
this.mnuOpenXML,
this.menuItem2,
this.mnuSaveXML,
this.mnuSaveXmlAs,
this.menuItem6,
this.mnuExit});
this.menuItem1.Text = "File";
//
// mnuOpenXSD
//
this.mnuOpenXSD.Index = 0;
this.mnuOpenXSD.Text = "Open XS&D";
this.mnuOpenXSD.Click += new System.EventHandler(this.mnuOpenXSD_Click);
//
// mnuInferXSD
//
this.mnuInferXSD.Enabled = false;
this.mnuInferXSD.Index = 1;
this.mnuInferXSD.Text = "&Infer XSD";
this.mnuInferXSD.Click += new System.EventHandler(this.mnuInferXSD_Click);
//
// mnuOpenXML
//
this.mnuOpenXML.Index = 2;
this.mnuOpenXML.Text = "Open XM&L";
this.mnuOpenXML.Click += new System.EventHandler(this.mnuOpenXML_Click);
//
// menuItem2
//
this.menuItem2.Index = 3;
this.menuItem2.Text = "-";
//
// mnuSaveXML
//
this.mnuSaveXML.Index = 4;
this.mnuSaveXML.Text = "&Save XML";
this.mnuSaveXML.Click += new System.EventHandler(this.mnuSaveXML_Click);
//
// mnuSaveXmlAs
//
this.mnuSaveXmlAs.Index = 5;
this.mnuSaveXmlAs.Text = "Save XML &As";
this.mnuSaveXmlAs.Click += new System.EventHandler(this.mnuSaveXmlAs_Click);
//
// menuItem6
//
this.menuItem6.Index = 6;
this.menuItem6.Text = "-";
//
// mnuExit
//
this.mnuExit.Index = 7;
this.mnuExit.Text = "E&xit";
this.mnuExit.Click += new System.EventHandler(this.mnuExit_Click);
//
// mnuAbout
//
this.mnuAbout.Index = 1;
this.mnuAbout.Text = "&About";
//
// dlgOpen
//
this.dlgOpen.DefaultExt = "xsd";
//
// saveXmlAsDlg
//
this.saveXmlAsDlg.DefaultExt = "xml";
//
// label1
//
this.label1.Location = new System.Drawing.Point(8, 16);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(48, 16);
this.label1.TabIndex = 1;
this.label1.Text = "XPath:";
this.label1.TextAlign = System.Drawing.ContentAlignment.BottomLeft;
//
// edXPath
//
this.edXPath.Location = new System.Drawing.Point(56, 16);
this.edXPath.Name = "edXPath";
this.edXPath.Size = new System.Drawing.Size(400, 20);
this.edXPath.TabIndex = 2;
this.edXPath.Text = "";
//
// btnXpathQuery
//
this.btnXpathQuery.Location = new System.Drawing.Point(464, 16);
this.btnXpathQuery.Name = "btnXpathQuery";
this.btnXpathQuery.Size = new System.Drawing.Size(48, 20);
this.btnXpathQuery.TabIndex = 3;
this.btnXpathQuery.Text = "Query";
this.btnXpathQuery.TextAlign = System.Drawing.ContentAlignment.BottomCenter;
this.btnXpathQuery.Click += new System.EventHandler(this.btnXpathQuery_Click);
//
// label2
//
this.label2.Location = new System.Drawing.Point(8, 48);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(98, 16);
this.label2.TabIndex = 4;
this.label2.Text = "New Row On:";
this.label2.TextAlign = System.Drawing.ContentAlignment.BottomLeft;
//
// edNewRowOn
//
this.edNewRowOn.Location = new System.Drawing.Point(96, 48);
this.edNewRowOn.Name = "edNewRowOn";
this.edNewRowOn.Size = new System.Drawing.Size(144, 20);
this.edNewRowOn.TabIndex = 5;
this.edNewRowOn.Text = "";
//
// Form1
//
this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
this.ClientSize = new System.Drawing.Size(536, 793);
this.Controls.Add(this.edNewRowOn);
this.Controls.Add(this.label2);
this.Controls.Add(this.btnXpathQuery);
this.Controls.Add(this.edXPath);
this.Controls.Add(this.label1);
this.Controls.Add(this.pnlDynForm);
this.Menu = this.mnuMain;
this.Name = "Form1";
this.Text = "XML Data Editor";
this.ResumeLayout(false);
}
#endregion
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.Run(new Form1());
}
public class TypeInfo
{
public TypeInfo() {typeName="";}
public TypeInfo(string typeName)
{
this.typeName=typeName;
length="-1";
minLength="-1";
maxLength="-1";
pattern="";
maxInclusive=null;
maxExclusive=null;
minInclusive=null;
minExclusive=null;
fractionDigits="-1";
totalDigits="-1";
whiteSpace="";
enumeration=new ArrayList();
}
public string typeName;
// facet information
public string length;
public string minLength;
public string maxLength;
public string pattern;
public string maxInclusive;
public string maxExclusive;
public string minInclusive;
public string minExclusive;
public string fractionDigits;
public string totalDigits;
public string whiteSpace; // "preserve", "replace", or "collapse"
public ArrayList enumeration;
}
public class TableInfo
{
public TableInfo()
{
tb=null;
rows=0;
pos=0;
}
public TextBox tb;
public int rows;
public int pos;
public DataRow[] dataRows;
}
#region Menu And Button Events
#region Open XSD
private void mnuOpenXSD_Click(object sender, System.EventArgs e)
{
dlgOpen.DefaultExt="xsd";
dlgOpen.CheckFileExists=true;
dlgOpen.Filter="XSD Files (*.xsd)|*.xsd|All Files (*.*)|*.*";
DialogResult res=dlgOpen.ShowDialog();
if (res==DialogResult.OK)
{
// try
{
// get a stream reader for the XSD file
StreamReader tr=new StreamReader(dlgOpen.FileName);
// read the file into the XmlSchema object
schema=XmlSchema.Read(tr, new ValidationEventHandler(SchemaValidationHandler));
tr.Close();
// report any problems with the schema by compiling it
CompileSchema();
// create the document
doc=new XmlDataDocument();
// open the schema again
tr=new StreamReader(dlgOpen.FileName);
// read it into the DataSet member
doc.DataSet.ReadXmlSchema(tr);
tr.Close();
ConstructGUI(doc.DataSet);
}
// catch(Exception err)
// {
// MessageBox.Show(err.ToString(), "Error processing XSD schema:");
// schema=null;
// doc=null;
// }
}
}
#endregion
#region Infer XSD
private void mnuInferXSD_Click(object sender, System.EventArgs e)
{
dlgOpen.DefaultExt="xml";
dlgOpen.CheckFileExists=true;
dlgOpen.Filter="XML Files (*.xml)|*.xml|All Files (*.*)|*.*";
DialogResult res=dlgOpen.ShowDialog();
if (res==DialogResult.OK)
{
try
{
// read into XmlSchema because we want the information in that format
doc=new XmlDataDocument();
StreamReader tr=new StreamReader(dlgOpen.FileName);
doc.DataSet.InferXmlSchema(tr, null);
tr.Close();
string strSchema=doc.DataSet.GetXmlSchema();
StreamWriter sw=new StreamWriter("temp.txt", false, System.Text.Encoding.UTF8);
sw.Write(strSchema);
sw.Flush();
sw.Close();
StringReader sr=new StringReader(strSchema);
schema=XmlSchema.Read(sr, new ValidationEventHandler(SchemaValidationHandler));
sr.Close();
CompileSchema();
ConstructGUI(doc.DataSet);
}
catch(Exception err)
{
MessageBox.Show(err.ToString(), "Error inferring schema:");
schema=null;
doc=null;
}
}
}
#endregion
#region Open XML
private void mnuOpenXML_Click(object sender, System.EventArgs e)
{
if (schema==null)
{
MessageBox.Show("Please load an XSD first.", "Cannot load XML");
return;
}
dlgOpen.DefaultExt="xml";
dlgOpen.CheckFileExists=true;
dlgOpen.Filter="XML Files (*.xml)|*.xml|All Files (*.*)|*.*";
DialogResult res=dlgOpen.ShowDialog();
if (res==DialogResult.OK)
{
xmlFileName=dlgOpen.FileName;
// try
{
XmlTextReader tr=new XmlTextReader(dlgOpen.FileName);
doc.Load(tr);
tr.Close();
// setup to point to the first record in the root table
foreach (DataTable dt in doc.DataSet.Tables)
{
// but we're only interested in the toplevel tables
if (dt.ParentRelations.Count==0)
{
TableInfo ti=tableInfo[dt] as TableInfo;
ti.pos=1;
ti.rows=dt.Rows.Count;
FirstRecord(dt, ti);
UpdateRecordCountInfo(dt);
}
}
}
// catch(Exception err)
// {
// MessageBox.Show(err.ToString(), "Error processing XML document:");
// }
}
}
#endregion
#region Save XML
private void mnuSaveXML_Click(object sender, System.EventArgs e)
{
if (doc.DataSet==null)
{
MessageBox.Show("No XML data to save.");
return;
}
if (xmlFileName != null)
{
doc.DataSet.AcceptChanges();
StreamWriter sw=new StreamWriter(xmlFileName, false, System.Text.Encoding.UTF8);
doc.DataSet.WriteXml(sw);
sw.Flush();
sw.Close();
}
else
{
mnuSaveXmlAs_Click(sender, e);
}
}
private void mnuSaveXmlAs_Click(object sender, System.EventArgs e)
{
if (doc.DataSet==null)
{
MessageBox.Show("No XML data to save.");
return;
}
doc.DataSet.AcceptChanges();
DialogResult res=saveXmlAsDlg.ShowDialog();
if (res==DialogResult.OK)
{
xmlFileName=saveXmlAsDlg.FileName;
StreamWriter sw=new StreamWriter(xmlFileName, false, System.Text.Encoding.UTF8);
doc.DataSet.WriteXml(sw);
sw.Flush();
sw.Close();
}
}
#endregion
#region Exit Program
private void mnuExit_Click(object sender, System.EventArgs e)
{
Close(); // signal form to close
}
#endregion
#region XPath Query
private void btnXpathQuery_Click(object sender, System.EventArgs e)
{
if (doc==null)
{
MessageBox.Show("Please load a schema and XML data set first.");
return;
}
XmlNodeList nodeList=null;
try
{
nodeList=doc.SelectNodes(edXPath.Text);
}
catch(Exception err)
{
MessageBox.Show(err.Message, "Error processing XPath query:");
return;
}
DlgXPathResult dlg=new DlgXPathResult(nodeList, edNewRowOn.Text);
dlg.ShowDialog(this);
}
#endregion
#endregion
#region Schema Validation Handler
// report any errors to the schema error edit box
private void SchemaValidationHandler(object sender, ValidationEventArgs args)
{
MessageBox.Show(args.Message, "Schema compilation error:");
}
#endregion
#region CompileSchema
// Compile the schema as it exists in the XmlSchema structure, displaying any
// errors in the schema error edit box and displaying the schema itself in
// the schema edit box.
private void CompileSchema()
{
schema.Compile(new ValidationEventHandler(SchemaValidationHandler));
}
#endregion
#region Functions That Inspect The Schema
private XmlSchemaAttribute GetLocalAttribute(XmlSchemaComplexType ct, string name)
{
for (int i=0; i<ct.Attributes.Count; i++)
{
XmlSchemaAttribute attr=ct.Attributes[i] as XmlSchemaAttribute;
if (attr != null)
{
if (attr.QualifiedName.Name==name)
{
return attr;
}
}
}
return null;
}
private XmlSchemaElement GetLocalElement(XmlSchemaComplexType ct, string name)
{
XmlSchemaSequence seq=ct.Particle as XmlSchemaSequence;
if (seq != null)
{
for (int i=0; i<seq.Items.Count; i++)
{
XmlSchemaElement el=seq.Items[i] as XmlSchemaElement;
if (el != null)
{
if (el.QualifiedName.Name==name)
{
return el;
}
}
}
}
return null;
}
private XmlSchemaComplexType GetLocalComplexType(XmlSchemaElement el)
{
XmlSchemaComplexType obj=el.SchemaType as XmlSchemaComplexType;
return obj;
}
private XmlSchemaElement GetGlobalElement(string name)
{
XmlQualifiedName qname=new XmlQualifiedName(name, schema.TargetNamespace);
XmlSchemaObject obj=schema.Elements[qname];
return obj as XmlSchemaElement;
}
private XmlSchemaSimpleType GetGlobalSimpleType(string name)
{
for (int i=0; i<schema.Items.Count; i++)
{
XmlSchemaSimpleType obj=schema.Items[i] as XmlSchemaSimpleType;
if (obj != null)
{
if (obj.Name==name)
{
return obj;
}
}
}
return null;
}
private XmlSchemaAttribute GetGlobalAttribute(string name)
{
XmlSchemaAttribute obj=schema.Attributes[new XmlQualifiedName(name, schema.TargetNamespace)] as XmlSchemaAttribute;
return obj;
}
private XmlSchemaComplexType GetGlobalComplexType(string name)
{
for (int i=0; i<schema.Items.Count; i++)
{
XmlSchemaComplexType obj=schema.Items[i] as XmlSchemaComplexType;
if (obj != null)
{
if (obj.Name==name)
{
return obj;
}
}
else
{
XmlSchemaElement el2=schema.Items[i] as XmlSchemaElement;
if (el2 != null)
{
if (el2.SchemaType is XmlSchemaComplexType)
{
obj=el2.SchemaType as XmlSchemaComplexType;
if (((XmlSchemaElement)schema.Items[i]).Name==name)
{
// *** The returning complex type does not have any associated qualified name!!! ****
return obj;
}
}
}
}
}
return null;
}
private void GetFacetInfo(TypeInfo ti, XmlSchemaSimpleTypeRestriction rs)
{
foreach (XmlSchemaFacet facet in rs.Facets)
{
// There's probably a better way to do this with reflection or some such
// process, but this old C++ programmers don't learn new tricks easily.
switch(facet.GetType().ToString())
{
case "System.Xml.Schema.XmlSchemaLengthFacet":
{
ti.length=facet.Value;
break;
}
case "System.Xml.Schema.XmlSchemaMinLengthFacet":
{
ti.minLength=facet.Value;
break;
}
case "System.Xml.Schema.XmlSchemaMaxLengthFacet":
{
ti.maxLength=facet.Value;
break;
}
case "System.Xml.Schema.XmlSchemaPatternFacet":
{
ti.pattern=facet.Value;
break;
}
case "System.Xml.Schema.XmlSchemaEnumerationFacet":
{
ti.enumeration.Add(facet.Value);
break;
}
case "System.Xml.Schema.XmlSchemaMaxInclusiveFacet":
{
ti.maxInclusive=facet.Value;
break;
}
case "System.Xml.Schema.XmlSchemaMaxExclusiveFacet":
{
ti.maxExclusive=facet.Value;
break;
}
case "System.Xml.Schema.XmlSchemaMinInclusiveFacet":
{
ti.minInclusive=facet.Value;
break;
}
case "System.Xml.Schema.XmlSchemaMinExclusiveFacet":
{
ti.minExclusive=facet.Value;
break;
}
case "System.Xml.Schema.XmlSchemaFractionDigitsFacet":
{
ti.fractionDigits=facet.Value;
break;
}
case "System.Xml.Schema.XmlSchemaTotalDigitsFacet":
{
ti.totalDigits=facet.Value;
break;
}
case "System.Xml.Schema.XmlSchemaWhiteSpaceFacet":
{
ti.whiteSpace=facet.Value;
break;
}
}
}
}
private TypeInfo GetTypeInfo(XmlSchemaComplexType ct, string name)
{
TypeInfo ti=null;
// is it an element?
XmlSchemaElement el=null;
el=GetLocalElement(ct, name);
// *** 5/10/2003 *** Search global element list also
if (el == null)
{
el=GetGlobalElement(name);
}
// or is it an attribute?
XmlSchemaAttribute attr=null;
if (ct != null)
{
attr=GetLocalAttribute(ct, name);
}
if (el != null)
{
// is it an element of simple type?
if (el.SchemaType is XmlSchemaSimpleType)
{
// (paths have been tested)
// yes...get the restriction base to find the type
XmlSchemaSimpleType st=el.SchemaType as XmlSchemaSimpleType;
XmlSchemaSimpleTypeRestriction rest=st.Content as XmlSchemaSimpleTypeRestriction;
if (rest != null)
{
if (rest.BaseTypeName.Namespace=="")
{
st=GetGlobalSimpleType(rest.BaseTypeName.Name);
rest=st.Content as XmlSchemaSimpleTypeRestriction;
string typename=rest.BaseTypeName.Name;
ti=new TypeInfo(typename);
GetFacetInfo(ti, rest);
}
else
{
string typename=rest.BaseTypeName.Name;
ti=new TypeInfo(typename);
GetFacetInfo(ti, rest);
}
}
}
else
{
// no...get the attribute type
// does it reference a global element?
if (el.RefName.Name != "")
{
// (path has been tested)
el=GetGlobalElement(el.RefName.Name);
if (el != null)
{
string typename=el.SchemaTypeName.Name;
ti=new TypeInfo(typename);
}
}
else
{
// (path has been tested)
// no...the type is specified in the element
// Does this reference a global simple type?
string typename=el.SchemaTypeName.Name;
if (el.SchemaTypeName.Namespace=="")
{
XmlSchemaSimpleType st=GetGlobalSimpleType(typename);
XmlSchemaSimpleTypeRestriction rest=st.Content as XmlSchemaSimpleTypeRestriction;
if (rest != null)
{
typename=rest.BaseTypeName.Name;
ti=new TypeInfo(typename);
GetFacetInfo(ti, rest);
}
}
else
{
ti=new TypeInfo(typename);
}
}
}
}
else
if (attr != null)
{
// does it reference a global attribute?
if (attr.RefName.Name != "")
{
// yes...get the type of the reference
// !!! PATH HAS NOT BEEN TESTED !!!
attr=GetGlobalAttribute(attr.RefName.Name);
if (attr != null)
{
string typename=attr.SchemaTypeName.Name;
ti=new TypeInfo(typename);
}
}
else
{
// no...get the type of the attribute
// is it of simple type defined globally?
if (attr.AttributeType is XmlSchemaSimpleType)
{
// (path has been tested)
XmlSchemaSimpleType st=GetGlobalSimpleType(attr.SchemaTypeName.Name);
if (st != null)
{
XmlSchemaSimpleTypeRestriction rest=st.Content as XmlSchemaSimpleTypeRestriction;
if (rest != null)
{
string typename=rest.BaseTypeName.Name;
ti=new TypeInfo(typename);
GetFacetInfo(ti, rest);
}
}
}
else
{
// (path has been tested)
string typename=attr.SchemaTypeName.Name;
ti=new TypeInfo(typename);
}
}
}
return ti;
}
#endregion
#region Construct GUI
private void ConstructGUI(DataSet dataSet)
{
// clear the GUI
// not implemented
Point pos=new Point(10, 10);
// get all tables in the dataset
foreach (DataTable dt in dataSet.Tables)
{
// but we're only interested in the toplevel tables
if (dt.ParentRelations.Count==0)
{
/*
* Rule 1:
* A top level table will be a top level element in the schema that
* is of a complex type. The element name will be the table name.
* What we want to identify is the complex type that the table references,
* so that we can determine the data types of the columns.
*
* Any other rules???
*/
XmlSchemaElement el=GetGlobalElement(dt.TableName);
if (el != null)
{
XmlSchemaComplexType ct;
// references a global complex type?
if (el.SchemaTypeName.Name != "")
{
string name=el.RefName.Name != "" ? el.RefName.Name : el.SchemaTypeName.Name;
ct=GetGlobalComplexType(name);
}
// contains a complex type?
else
{
ct=el.SchemaType as XmlSchemaComplexType;
}
if (ct != null)
{
Point p2=ConstructGUI(pos.X, pos.Y, dt, pnlDynForm, ct);
pos.Y+=p2.Y;
}
}
}
}
}
private Point ConstructGUI(int absx, int absy, DataTable dt, Control gbParent, XmlSchemaComplexType ct)
{
// for this data table, construct a groupbox as a container for
// record scrollbar
// table columns
// if (dt.TableName=="TaxaNotes")
// {
// MessageBox.Show(dt.TableName);
// }
GroupBox gb1=new GroupBox();
gb1.Font=new Font(gb1.Font, FontStyle.Bold);
gb1.Text=dt.TableName;
gb1.Location=new Point(absx, absy);
gb1.Parent=gbParent;
gb1.Visible=true;
tableInfo[dt]=new TableInfo();
CreateRecordNavigationBar(10, 15, gb1, dt);
int relx=10; // and indented by 10 pixels
int rely=40; // and 30 pixels from top of groupbox
// For each column in the table...
foreach (DataColumn col in dt.Columns)
{
// if it's not an internal ID...
if (col.ColumnMapping != MappingType.Hidden)
{
// *** 5/10/2003 *** Check for a simple content type.
XmlSchemaSimpleContent sc=ct.ContentModel as XmlSchemaSimpleContent;
if (sc != null)
{
CreateLabel(relx, rely, dt.TableName, gb1);
XmlSchemaSimpleContentExtension sce=sc.Content as XmlSchemaSimpleContentExtension;
if (sce != null)
{
TypeInfo ti=new TypeInfo(sce.BaseTypeName.Name);
CreateEditControl(relx+120, rely, ti, gb1, dt, col);
}
}
else
{
// display its name
CreateLabel(relx, rely, col.ColumnName, gb1);
/*
* INACCESSIBLE INFORMATION THAT WOULD HAVE BEEN REALLY USEFUL!!!
* col.XmlDataType is the data type, simple or global
* col.SimpleType contains the facets and XmlBaseType of the simple type
* These properties are INTERNAL classes! Since we have to get this
* information ourselves...
*/
TypeInfo ti=GetTypeInfo(ct, col.ColumnName);
if (ti != null)
{
/*Control editCtrl=*/CreateEditControl(relx+120, rely, ti, gb1, dt, col);
}
}
}
if (col.ColumnMapping==MappingType.Hidden)
{
// *** columns that define relationships do not need to be displayed ***
Label lbl=CreateLabel(relx, rely, col.ColumnName, gb1);
Control editCtrl=CreateEditControl(relx+120, rely, new TypeInfo("string"), gb1, dt, col);
editCtrl.Size=new Size(50, 20);
((TextBox)editCtrl).ReadOnly=true;
// These are columns that are created when the schema is loaded into a dataset.
// They are not part of the schema itself, but an artifact of the "tablization"
// of the schema.
// identify child relations
// Does this column have a relationship with a child table?
// data relations are always 1:1
if (dt.ChildRelations.Count != 0)
{
DataRelation dr=dt.ChildRelations[0];
for (int i=0; i<dr.ChildColumns.Length; i++)
{
if (dr.ChildColumns[i].ColumnName==col.ColumnName)
{
lbl.ForeColor=Color.Blue;
break;
}
}
}
// identify parent relations.
// Does this column have a relationship with a parent table?
// data relations are always 1:1
if (dt.ParentRelations.Count != 0)
{
DataRelation dr=dt.ParentRelations[0];
for (int i=0; i<dr.ParentColumns.Length; i++)
{
if (dr.ParentColumns[i].ColumnName==col.ColumnName)
{
lbl.ForeColor=Color.Red;
break;
}
}
}
}
rely+=20;
}
int rx=0; // assume no children
int choiceIdx=0;
// Get child relationships, which are displayed as indented groupboxes
foreach (DataRelation childRelation in dt.ChildRelations)
{
DataTable dt2=childRelation.ChildTable;
/* Rule 1: The data table references a global complex type.
* The table name is the element name of complex type in the
* current complex type object collection. As with the root
* table, we need to find the element, get the type, then get
* the complex type.
*
* Rule 2: If this isn't a global complex type (ct2==null), then
* the element is a local complex type!
*/
XmlSchemaElement el2=null;
XmlSchemaElement el3=null;
XmlSchemaComplexType ct2=null;
// if (dt2.TableName=="Author")
// {
// MessageBox.Show("Searching: "+dt2.TableName);
// }
// *** 5/10/2003 *** Handle complex types with "choice" schemas.
// A "choice" is represented as separate tables, each indexing the same choice list,
// and the child relation index tracks the choice items!
XmlSchemaChoice choice=ct.ContentTypeParticle as XmlSchemaChoice;
if (choice != null)
{
// for (int i=0; i<choice.Items.Count; i++)
{
XmlSchemaElement elChoice=choice.Items[choiceIdx] as XmlSchemaElement;
if (elChoice != null)
{
ct2=GetGlobalComplexType(elChoice.RefName.Name);
Point p=ConstructGUI(relx+20, rely+20, dt2, gb1, ct2);
if (p.X > rx)
{
rx=p.X; // indent level
}
rely+=p.Y+20; // vertical spacing between child tables
// break;
}
}
++choiceIdx;
continue;
}
else if (ct != null)
{
// get the local element associated with the table name in this complex type
el2=GetLocalElement(ct, dt2.TableName);
// if it exists...
if (el2 != null)
{
// get either the refname or the schema type name
string name=el2.RefName.Name != "" ? el2.RefName.Name : el2.SchemaTypeName.Name;
// find the complex type defining the element type.
ct2=GetGlobalComplexType(name);
if (ct2==null)
{
// if the complex type is not defined globally, then check if it's defnied locally
ct2=GetLocalComplexType(el2);
// if it's not defined locally, check if the element is referring to a global ELEMENT
if (ct2==null)
{
el3=GetGlobalElement(name);
}
}
}
}
if (ct2 != null)
{
Point p=ConstructGUI(relx+20, rely+20, dt2, gb1, ct2);
if (p.X > rx)
{
rx=p.X; // indent level
}
rely+=p.Y+20; // vertical spacing between child tables
}
// *** 5/10/2003 *** This handles the case where a local element references a global element!
// This structure must be created as a child node, complete with groupbox support.
else if (el3 != null)
{
// *** 5/12/2003 *** Create a tableInfo entry.
// NOTE THAT A SEPARATE GROUPBOX WITH RECORD NAVIGATION IS NOT CREATED!
tableInfo[dt2]=new TableInfo();
string name=el3.SchemaTypeName.Name;
TypeInfo ti=new TypeInfo(name);
CreateLabel(relx, rely, dt2.TableName, gb1);
CreateEditControl(relx+120, rely, ti, gb1, dt2, dt2.Columns[0]);
rely+=20; // vertical spacing between rows
}
else
{
MessageBox.Show("Not found: "+dt.TableName+"."+dt2.TableName);
}
}
// set our size based on number of child tables (indents)
gb1.Size=new Size(300+rx, rely+10);
// return the size of this groupbox, which includes all columns and child tables
return new Point(40+rx, rely+10);
}
#endregion
private Label CreateLabel(int x, int y, string name, Control parent)
{
Label lbl=new Label();
lbl.Location=new Point(x, y+2);
lbl.Size=new Size(120, 15);
lbl.Text=name;
lbl.Font=new Font(lbl.Font, FontStyle.Regular);
lbl.Visible=true;
lbl.Parent=parent;
return lbl;
}
private Control CreateRecordNavigationBar(int x, int y, Control parent, DataTable tag)
{
Panel navPanel=new Panel();
navPanel.Location=new Point(x, y);
navPanel.Size=new Size(280, 19);
navPanel.Visible=true;
navPanel.Parent=parent;
navPanel.BorderStyle=BorderStyle.FixedSingle;
CreateNavButton("<<", 0, 0, new EventHandler(FirstRecord), navPanel, tag);
CreateNavButton("<", 30, 0, new EventHandler(PrevRecord), navPanel, tag);
CreateNavButton("*", 60, 0, new EventHandler(NewRecord), navPanel, tag);
CreateNavButton(">", 90, 0, new EventHandler(NextRecord), navPanel, tag);
CreateNavButton(">>", 120, 0, new EventHandler(LastRecord), navPanel, tag);
TextBox tb=new TextBox();
tb.Location=new Point(150, 2);
tb.Size=new Size(100, 18);
tb.BorderStyle=BorderStyle.None;
tb.Font=new Font("Tahoma", 8, FontStyle.Regular);
tb.Parent=navPanel;
tb.Visible=true;
tb.Text=" record 0 of 0";
((TableInfo)tableInfo[tag]).tb=tb;
CreateNavButton("X", 250, 0, new EventHandler(DeleteRecord), navPanel, tag);
return navPanel;
}
private void CreateNavButton(string text, int x, int y, EventHandler ev, Control parent, object tag)
{
Button btnNewRecord=new Button();
btnNewRecord.Text=text;
btnNewRecord.Location=new Point(x, y);
btnNewRecord.Size=new Size(30, 17);
btnNewRecord.Parent=parent;
btnNewRecord.Visible=true;
btnNewRecord.Font=new Font("Tahoma", 8, FontStyle.Bold);
btnNewRecord.Tag=tag;
btnNewRecord.Click+=ev;
}
private Control CreateEditControl(int x, int y, TypeInfo ti, Control parent, DataTable dt, DataColumn dc)
{
Control ctrl=null;
// if the schema has an enumeration, then display as a combo box
if (ti.enumeration.Count != 0)
{
ComboBox cb=new ComboBox();
cb.Location=new Point(x, y);
cb.Size=new Size(140, 200);
cb.Font=new Font(cb.Font, FontStyle.Regular);
cb.Visible=true;
cb.Parent=parent;
foreach (object obj in ti.enumeration)
{
cb.Items.Add(obj);
}
ctrl=cb;
ctrl.DataBindings.Add("Text", dt, dc.ColumnName);
}
// if the schema has a min/max inclusive set, then display as an up-down control
else if ( (ti.minInclusive != null) && (ti.maxInclusive != null) )
{
NumericUpDown nud=new NumericUpDown();
nud.Location=new Point(x, y);
nud.Size=new Size(80, 20);
nud.Font=new Font(nud.Font, FontStyle.Regular);
nud.Visible=true;
nud.Parent=parent;
nud.Minimum=Convert.ToInt32(ti.minInclusive);
nud.Maximum=Convert.ToInt32(ti.maxInclusive);
ctrl=nud;
ctrl.DataBindings.Add("Text", dt, dc.ColumnName);
}
else
{
// use MaxLength
// use Tag???
// NumericUpDown???
switch(ti.typeName)
{
case "boolean":
{
// boolean is implemented as a checkbox
CheckBox ck1=new CheckBox();
ck1.Location=new Point(x, y);
ck1.Size=new Size(140, 20);
ck1.Font=new Font(ck1.Font, FontStyle.Regular);
ck1.Visible=true;
ck1.Text="";
ck1.Parent=parent;
ck1.AutoCheck=true;
ctrl=ck1;
Binding b=new Binding("Checked", dt, dc.ColumnName);
b.Format+=new ConvertEventHandler(TrueFalseToBool);
ctrl.DataBindings.Add(b);
break;
}
case "string":
{
// textbox
TextBox tb=new TextBox();
tb.Location=new Point(x, y);
tb.Size=new Size(140, 20);
tb.Font=new Font(tb.Font, FontStyle.Regular);
tb.Visible=true;
tb.Parent=parent;
ctrl=tb;
ctrl.DataBindings.Add("Text", dt, dc.ColumnName);
break;
}
case "decimal":
{
// right justified
TextBox tb=new TextBox();
tb.Location=new Point(x, y);
tb.Size=new Size(140, 20);
tb.Font=new Font(tb.Font, FontStyle.Regular);
tb.Visible=true;
tb.Parent=parent;
tb.TextAlign=HorizontalAlignment.Right;
ctrl=tb;
ctrl.DataBindings.Add("Text", dt, dc.ColumnName);
break;
}
case "positiveInteger":
{
// right justified
TextBox tb=new TextBox();
tb.Location=new Point(x, y);
tb.Size=new Size(140, 20);
tb.Font=new Font(tb.Font, FontStyle.Regular);
tb.Visible=true;
tb.Parent=parent;
tb.TextAlign=HorizontalAlignment.Right;
ctrl=tb;
ctrl.DataBindings.Add("Text", dt, dc.ColumnName);
break;
}
default:
{
// use a text box for the default data type
TextBox tb=new TextBox();
tb.Location=new Point(x, y);
tb.Size=new Size(140, 20);
tb.Font=new Font(tb.Font, FontStyle.Regular);
tb.Visible=true;
tb.Parent=parent;
ctrl=tb;
ctrl.DataBindings.Add("Text", dt, dc.ColumnName);
break;
}
}
}
return ctrl;
}
private void TrueFalseToBool(object sender, ConvertEventArgs args)
{
if (args.Value.GetType()==typeof(System.DBNull))
{
args.Value=false;
}
}
private void UpdateRecordCountInfo(DataTable dt)
{
// update the text box to reflect the record #
TableInfo ti=tableInfo[dt] as TableInfo;
if (dt.ParentRelations.Count==0)
{
ti.pos=BindingContext[dt].Position+1;
}
// *** 5/12/2003 *** If a child element references a global element, no navigational record is created. Is this
// a problem?
if (ti.tb != null)
{
ti.tb.Text=" record "+ti.pos.ToString()+" of "+ti.rows.ToString();
}
}
private int GetMatchingRows(DataTable dt)
{
TableInfo ti=tableInfo[dt] as TableInfo;
// get parent relationship
DataRelation parentRelation=dt.ParentRelations[0];
// get the parent table
DataTable dt2=parentRelation.ParentTable;
// get the parent column (1:1 relationship always)
DataColumn dcParent=parentRelation.ParentColumns[0];
// get the current record # of the parent
int n=BindingContext[dt2].Position;
if (n != -1)
{
// get the ID
string val=dt2.Rows[n][dcParent].ToString();
// search the child for all records where child.parentID=parent.ID
string expr=dcParent.ColumnName+"="+val;
// save the rows, as we'll use them later on when navigating the child
ti.dataRows=dt.Select(expr);
}
// return the length
if (ti.dataRows==null)
{
return 0;
}
return ti.dataRows.Length;
}
#region Navigation Bar Event Handlers
// Create a new record in the associated table.
// This function also creates new records in all child tables and
// sets the child-parent relationship key in the child tables.
private void NewRecord(object sender, EventArgs e)
{
Button btn=sender as Button;
DataTable dt=btn.Tag as DataTable;
dt.Rows.Add(dt.NewRow());
int newRow=dt.Rows.Count-1;
BindingContext[dt].Position=newRow;
TableInfo ti=tableInfo[dt] as TableInfo;
// Set the child relationship ID's to the parent!
// There will be only one parent relationship except
// for the root table.
if (dt.ParentRelations.Count != 0)
{
DataRelation parentRelation=dt.ParentRelations[0];
DataTable dt2=parentRelation.ParentTable;
int n=BindingContext[dt2].Position;
// this is always a 1:1 relationship
DataColumn dcParent=parentRelation.ParentColumns[0];
DataColumn dcChild=parentRelation.ChildColumns[0];
string val=dt2.Rows[n][dcParent].ToString();
dt.Rows[newRow][dcChild]=val;
n=GetMatchingRows(dt);
ti.pos=n;
ti.rows=n;
}
else
{
ti.pos=newRow+1;
ti.rows=newRow+1;
}
UpdateRecordCountInfo(dt);
// for each child, also create a new row in the child's table
foreach (DataRelation childRelation in dt.ChildRelations)
{
DataTable dtChild=childRelation.ChildTable;
NewRecord(dt, dtChild, childRelation);
}
}
private void NewRecord(DataTable parent, DataTable child, DataRelation dr)
{
// add the child record
child.Rows.Add(child.NewRow());
// get the last row of the parent (this is the new row)
// and the new row in the child (also the last row)
int newParentRow=parent.Rows.Count-1;
int newChildRow=child.Rows.Count-1;
// go to this record
BindingContext[child].Position=newChildRow;
// get the parent and child columns
// copy the parent ID (auto sequencing) to the child to establish
// the relationship. This is always a 1:1 relationship
DataColumn dcParent=dr.ParentColumns[0];
DataColumn dcChild=dr.ChildColumns[0];
string val=parent.Rows[newParentRow][dcParent].ToString();
child.Rows[newChildRow][dcChild]=val;
((TableInfo)tableInfo[child]).pos=1;
((TableInfo)tableInfo[child]).rows=1;
UpdateRecordCountInfo(child);
// recurse into children of this child
foreach (DataRelation childRelation in child.ChildRelations)
{
DataTable dt2=childRelation.ChildTable;
NewRecord(child, dt2, childRelation);
}
}
private void FirstRecord(object sender, EventArgs e)
{
Button btn=sender as Button;
DataTable dt=btn.Tag as DataTable;
TableInfo ti=tableInfo[dt] as TableInfo;
ti.pos=1;
FirstRecord(dt, ti);
UpdateRecordCountInfo(dt);
}
private void PrevRecord(object sender, EventArgs e)
{
Button btn=sender as Button;
DataTable dt=btn.Tag as DataTable;
TableInfo ti=tableInfo[dt] as TableInfo;
if (ti.pos > 1)
{
((TableInfo)tableInfo[dt]).pos--;
PrevRecord(dt, ti);
UpdateRecordCountInfo(dt);
}
}
private void NextRecord(object sender, EventArgs e)
{
Button btn=sender as Button;
DataTable dt=btn.Tag as DataTable;
TableInfo ti=tableInfo[dt] as TableInfo;
if (ti.pos < ti.rows)
{
((TableInfo)tableInfo[dt]).pos++;
NextRecord(dt, ti);
UpdateRecordCountInfo(dt);
}
}
private void LastRecord(object sender, EventArgs e)
{
Button btn=sender as Button;
DataTable dt=btn.Tag as DataTable;
int n=dt.Rows.Count;
TableInfo ti=tableInfo[dt] as TableInfo;
ti.pos=ti.rows;
LastRecord(dt, ti);
UpdateRecordCountInfo(dt);
}
private void DeleteRecord(object sender, EventArgs e)
{
Button btn=sender as Button;
DataTable dt=btn.Tag as DataTable;
int n=BindingContext[dt].Position;
dt.Rows.RemoveAt(n);
// if a child table...
if (dt.ParentRelations.Count != 0)
{
// ...get all rows matching the parent ID
n=GetMatchingRows(dt);
}
else
{
// ...otherwise get all rows of the root table
n=dt.Rows.Count;
}
TableInfo ti=tableInfo[dt] as TableInfo;
ti.rows=n;
// if position is now past # of rows...
if (ti.pos > ti.rows)
{
// set position to last row
ti.pos=ti.rows;
}
// update the display
UpdateRecordCountInfo(dt);
if (n != 0) // table has records?
{
// set to the next available row
if (dt.ParentRelations.Count != 0)
{
SetPositionToRow(dt, ti.dataRows[ti.pos-1]);
}
else
{
BindingContext[dt].Position=ti.pos-1;
}
}
// reset all children to record #1
ResetAllChildren(dt);
// Cascading delete automatically implemented
// by the table constraints.
}
private void FirstRecord(DataTable dt, TableInfo ti)
{
if (dt.ParentRelations.Count==0)
{
BindingContext[dt].Position=0;
}
else
{
// get the first row in the record set that matches the parent
SetPositionToRow(dt, ti.dataRows[ti.pos-1]);
}
ResetAllChildren(dt);
}
private void PrevRecord(DataTable dt, TableInfo ti)
{
if (dt.ParentRelations.Count==0)
{
BindingContext[dt].Position--;
}
else
{
// get the previous row that matches the parent ID
SetPositionToRow(dt, ti.dataRows[ti.pos-1]);
}
ResetAllChildren(dt);
}
private void NextRecord(DataTable dt, TableInfo ti)
{
if (dt.ParentRelations.Count==0)
{
BindingContext[dt].Position++;
}
else
{
// get the next row that matches the parent ID
SetPositionToRow(dt, ti.dataRows[ti.pos-1]);
}
ResetAllChildren(dt);
}
private void LastRecord(DataTable dt, TableInfo ti)
{
if (dt.ParentRelations.Count==0)
{
BindingContext[dt].Position=dt.Rows.Count-1;
}
else
{
// get the last row that matches the parent ID
SetPositionToRow(dt, ti.dataRows[ti.pos-1]);
}
ResetAllChildren(dt);
}
private void ResetAllChildren(DataTable dt)
{
// update all children of the table to match our new ID
foreach (DataRelation dr in dt.ChildRelations)
{
DataTable dtChild=dr.ChildTable;
ResetChildRecords(dtChild);
}
}
private void ResetChildRecords(DataTable dt)
{
int n=GetMatchingRows(dt);
TableInfo ti=tableInfo[dt] as TableInfo;
ti.pos=1;
ti.rows=n;
UpdateRecordCountInfo(dt);
if (n != 0)
{
SetPositionToRow(dt, ti.dataRows[0]);
}
foreach (DataRelation dr in dt.ChildRelations)
{
DataTable dtChild=dr.ChildTable;
ResetChildRecords(dtChild);
}
}
private void SetPositionToRow(DataTable dt, DataRow row)
{
for (int i=0; i<dt.Rows.Count; i++)
{
if (dt.Rows[i]==row)
{
BindingContext[dt].Position=i;
break;
}
}
}
#endregion
}
}