The most part of the questions, which I have been asked when the article "Building the Address control"(part 1 , part 2 , part 3) has been published, I can formulate as follows: how a control can be built in Visual Studio 2005 and how can be used XML as data source. In this article, I share how you can build your own Windows controls in Visual Studio 2005 using XML as data source. The examples are written using C#.
In order to reduce quantity of parts of this article we will use (as a basis) the article mentioned above. Thus, our task will be defined as follows :
On a form our control should look (see fig.1 , part 1) as Label and TextBox with the text property like that : Street NumBuilding/NumApartment, NumEntrance, City, ZIP (for example : Green Street 5/9, A, Sun City, 99999). With any trying to change text our control changes to editing mode : some special address form appears which includes a few TextBoxes (TextBoxStreet, TextBoxBuilding, etc.) and special controls for choosing/finding city (see fig.2 , part 1); the list of cities is got as (here are the differences): DataTable or DataSet or XmlDocument.
To tell the truth, there are not big differences in building a Windows user control (at least such kind as our "Address" control ) for Visual Studio 2003 and for Visual Studio 2005. Of course, there are "partial class ...." , "using System.Collections.Generic;", new controls and many new other excellent things in 2005 version. But ..., it is not exactly the theme of this article.
OK ! Now we should create a solution for building our control. The solution (by analogy) consists of three projects : the first project (Windows Control Library) named "Address" has Output Type of "Class Library" ( fig 1.);
Figure 1.
The second one, that we add to solution, named "UC_Test" has Output Type of "Windows Application" ( fig 2 , fig 3.);
Figure 2.
Figure 3.
The third project named "GetData" has Output Type of "Class Library" (fig 4.).
Figure 4.
The "Address" project includes such items as "User Control" named "Address" and "Windows Form" named "FormAddress". In fact this project will be our "independent unit" (dll). The project "UC_Test" includes only one Windows Form named "Form1". With its help we just test our "Address" control. The "GetData" project includes only one class named "GetDataHelp" that has two methods :
//The method getDataSetCities that returns dataSet with
//one dataTable "DataTableCities". The dataTable consists of
//two columns : "SYMBOL_CITY" and "CITY".
public DataSet getDataSetCities(int iRows)
and
//The method getXMLCities that returns XmlDocument for
//XML data like this:
//<?xml version="1.0" encoding = "WINDOWS-1255"?>
//<CITIES>
// <CITY_ROW ROW_NUM="0">
// <SYMBOL_CITY>000000</SYMBOL_CITY>
// <CITY>Sun City</CITY>
// </CITY_ROW>
// <CITY_ROW ROW_NUM="1">
// <SYMBOL_CITY>000001</SYMBOL_CITY>
// <CITY>awtCity_1g</CITY>
// </CITY_ROW>
//...................................
//...................................
//</CITIES>
public XmlDocument getXMLCities(int iRows)
The project "UC_Test" is "independent unit" (dll) and helps us to get needed data.
The solution is shown on fig.5.
Figure 5.
Now we are ready to add some code. The method getDataSetCities is the same (part 1) and you just do "copy - paste". In order to create and return necessary for us XmlDocument we should do following steps :
- with the help of the XmlProcessingInstruction object to create the processing instruction :
<?xml version="1.0" encoding = "WINDOWS-1255"?> ;
- with the help of the XmlNode object to create the single "Root" node <CITIES> :
<?xml version="1.0" encoding = "WINDOWS-1255"?>
<CITIES>
</CITIES> ;
- with the help of the XmlElement and XmlAttribute objects and For loop to create (as many as we want!) elements ( <CITY_ROW> with attributte ROW_NUM, <SYMBOL_CITY>, <CITY> ) and then to "fill" attribute values and inner text :
<?xml version="1.0" encoding = "WINDOWS-1255"?>
<CITIES>
<CITY_ROW ROW_NUM="0">
<SYMBOL_CITY>000000</SYMBOL_CITY>
<CITY>Sun City</CITY>
</CITY_ROW>
............................. and so on
</CITIES> ;
OK! Now add to GetDataHelp.cs the following code:
//The method getXMLCities that returns XmlDocument for
//XML data like this:
//<?xml version="1.0" encoding = "WINDOWS-1255"?>
//<CITIES>
// <CITY_ROW ROW_NUM="0">
// <SYMBOL_CITY>000000</SYMBOL_CITY>
// <CITY>Sun City</CITY>
// </CITY_ROW>
// <CITY_ROW ROW_NUM="1">
// <SYMBOL_CITY>000001</SYMBOL_CITY>
// <CITY>awtCity_1g</CITY>
// </CITY_ROW>
//................
// ..............
//</CITIES>
public XmlDocument getXMLCities(int iRows)
{
XmlDocument xmlDoc = new XmlDocument();
XmlProcessingInstruction xmlInstruction =
xmlDoc.CreateProcessingInstruction(
"xml", "version =" + "'1.0'" + " encoding=" +
"'WINDOWS-1255'" );
XmlNode nodeRoot;
XmlElement elementCITY;
XmlElement elementSYMBOL_CITY;
XmlElement elementCITY_ROW;
XmlAttribute attributeCITY_ROW;
string sSYMBOL_CITY;
string sCITY = "";
string sHelp_0 = "City";
xmlDoc.AppendChild(xmlInstruction);
nodeRoot = xmlDoc.CreateElement("CITIES");
xmlDoc.AppendChild(nodeRoot);
for (int i = 0; i < iRows; i++)
{
//---------------------------------------
if (i < 10)
{
sSYMBOL_CITY = "00000" + i.ToString();
}
else
{
if (i < 100 && i >= 10)
{
sSYMBOL_CITY = "0000" + i.ToString();
}
else
{
if (i < 1000 && i >= 100)
{
sSYMBOL_CITY = "000" + i.ToString();
}
else
{
sSYMBOL_CITY = i.ToString();
}
}
}
sCITY = sHelp_0;
sCITY = sCITY + "_" + i.ToString();
if (i % 2 == 0)
{
sCITY = sCITY + "g";
}
else if (i % 3 == 0)
{
sCITY = sCITY + "gff";
}
else if (i % 5 == 0)
{
sCITY = "abc" + sCITY;
}
else if (i % 7 == 0)
{
sCITY = "awc" + sCITY;
}
else
{
sCITY = "awt" + sCITY + "g";
}
if (i == 0)
{
sCITY = "Sun City";
}
//---------------------------------------
elementCITY_ROW = xmlDoc.CreateElement("CITY_ROW");
nodeRoot.AppendChild(elementCITY_ROW);
attributeCITY_ROW = xmlDoc.CreateAttribute("ROW_NUM");
attributeCITY_ROW.Value = i.ToString();
elementCITY_ROW.Attributes.Append(attributeCITY_ROW);
elementSYMBOL_CITY = xmlDoc.CreateElement("SYMBOL_CITY");
elementSYMBOL_CITY.InnerText = sSYMBOL_CITY;
elementCITY_ROW.AppendChild(elementSYMBOL_CITY);
elementCITY = xmlDoc.CreateElement("CITY");
elementCITY.InnerText = sCITY;
elementCITY_ROW.AppendChild(elementCITY);
}
return xmlDoc;
}
Don't forget to add to namespaces :
using System.Data;
using System.Data.SqlClient;
using System.Xml;
Now we have to build the control. We will just repeat (exactly!) the process (both the building and the code) how it is described in part 2 (for 2003 version) , and then we will do some changes.
Pay attention, that our "Address" control has property
public DataTable C_DataTableCities. The property type is DataTable.
First of all we should change this property:
//public DataTable C_DataTableCities
//{
// set
// {
// dt_Cities = value;
// }
//}
As you can see we just associate our private member dt_Cities with the public property through set accessor function. According to our task (see above) "the list of cities is got as : DataTable or DataSet or XmlDocument". Thus , to continue to use dt_Cities in the same way we have to change the property type to "more general" and "to do processing" of the value in order to assign to our dt_Cities three different data types. It is better to change the property name "C_DataTableCities" to , for example, "C_DataCities".
OK! Add to our control the following:
"forClass" region
string Title = "ADDRESS CONTROL";
instead of property C_DataTableCities the following:
//----------------------------------------------------------------
//public DataTable C_DataTableCities
//{
// set
// {
// dt_Cities = value;
// }
//}
public object C_DataCities
{
set
{
object objData = value;
try
{
if (objData.GetType() == typeof(DataTable))
{
dt_Cities = (DataTable)objData;
}
else if (objData.GetType() == typeof(DataSet))
{
DataSet ds = (DataSet)objData;
dt_Cities = ds.Tables[0];
}
else if (objData.GetType() == typeof(XmlDocument))
{
dt_Cities = getDataTableFromXML((XmlDocument)objData);
}
else
{
MessageBox.Show(
"C_DataCities property has to have type of " +
"DataTable, DataSet or XmlDocument ",Title,
MessageBoxButtons.OK, MessageBoxIcon.Stop);
}
}
catch(Exception ex)
{
MessageBox.Show("There is problem with DataCities " +
ex.Message.ToString() ,
Title, MessageBoxButtons.OK, MessageBoxIcon.Stop);
}
}
}
The function getDataTableFromXML has one parameter of the type of XmlDocument and returns DataTable. We can make such kind of "transformations" by several ways. One of them is a very elegant and simple : with the help of the XmlNodeReader object.
We can do other ways of "transformation" by more complex way but with this we will have an opportunity to "transform" any Xml. As for me, I very like use GetElementsByTagName method, SelectNodes method and XPath etc. You can use whatever you want :
//--------------------------------------------------------------
//The method that "transforms" XmlDocument parameter into
//DataTable with the help of XmlNodeReader object
//private DataTable getDataTableFromXML(XmlDocument xmlDoc)
//{
// XmlNodeReader xmlNodeRead = null;
// DataSet ds = new DataSet();
// xmlNodeRead = new XmlNodeReader(xmlDoc);
// ds.ReadXml(xmlNodeRead);
// if (xmlNodeRead != null)
// {
// xmlNodeRead.Close();
// }
// return ds.Tables[0];
//}
//---------------------------------------------------------------
//The method that "transforms" XmlDocument parameter into
//DataTable with the help of GetElementsByTagName method,
//SelectNodes method etc.
private DataTable getDataTableFromXML(XmlDocument xmlDoc)
{
DataTable dt = new DataTable("DataTableCities");
DataColumn dc_SYMBOL_CITY;
DataColumn dc_CITY;
DataColumn dc_ROW_NUM;
DataRow dRow;
DataSet ds = new DataSet();
int iRows = 0;
ds.Clear();
dc_SYMBOL_CITY = new DataColumn("SYMBOL_CITY", Type.GetType("System.String"));
dt.Columns.Add(dc_SYMBOL_CITY);
dc_CITY = new DataColumn("CITY", Type.GetType("System.String"));
dt.Columns.Add(dc_CITY);
dc_ROW_NUM = new DataColumn("ROW_NUM", Type.GetType("System.String"));
dt.Columns.Add(dc_ROW_NUM);
////-----using GetElementsByTagName method
//iRows = xmlDoc.GetElementsByTagName("CITY_ROW").Count;
////----using SelectNodes method and XPath
iRows = xmlDoc.DocumentElement.SelectNodes("CITY_ROW").Count;
for (int i = 0; i < iRows; i++)
{
dRow = dt.NewRow();
////-----using GetElementsByTagName method-------------
//dRow["SYMBOL_CITY"] = xmlDoc.GetElementsByTagName("SYMBOL_CITY")[i].InnerText;
//dRow["CITY"] = xmlDoc.GetElementsByTagName("CITY")[i].InnerText;
//dRow["ROW_NUM"] = xmlDoc.GetElementsByTagName("CITY_ROW")[i].Attributes["ROW_NUM"].Value;
////---------------------------------------------------
////----using SelectNodes method and XPath-------------
dRow["SYMBOL_CITY"] = xmlDoc.DocumentElement.SelectNodes("CITY_ROW/SYMBOL_CITY")[i].InnerText;
dRow["CITY"] = xmlDoc.DocumentElement.SelectNodes("CITY_ROW/CITY")[i].InnerText;
dRow["ROW_NUM"] = xmlDoc.DocumentElement.SelectNodes("CITY_ROW")[i].Attributes["ROW_NUM"].Value;
////---------------------------------------------------
dt.Rows.Add(dRow);
}
ds.Tables.Add(dt);
return ds.Tables[0];
}
OK! Now we will just repeat (exactly!) the process (both the building and the code) how it is described in part 3 (for 2003 version) , and then we will add some lines of code (just to test the C_DataCities of our control).
private void Form1_Load(object sender, EventArgs e)
{
GetData.GetDataHelp DT_Cities = new GetData.GetDataHelp();
address1.C_Value_Member = "SYMBOL_CITY";
address1.C_Display_Member = "CITY";
//Using DataTable
address1.C_DataCities = DT_Cities.getDataSetCities(1000).Tables[0];
//Using DataSet
//address1.C_DataCities = DT_Cities.getDataSetCities(1000);
//Using XmlDocument
//address1.C_DataCities = DT_Cities.getXMLCities(1000);
//address1.C_TextBox_Multiline = false;
}
Run the project and be convinced that all our tasks are carried out (see fig. 10, 11, 12 ,13 ; part 3).
CONCLUSION
I hope that this article and previous one (part 1, part 2, part 3) will help you to recreate the Address control or create your own control with your own requests in Visual Studio 2005 and with different type of data source, including XML.
Good luck in programming !