Building Control in Visual Studio 2005 with XML as Data Source.

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.);

001.GIF 

Figure 1.

The second one, that we add to solution, named "UC_Test" has Output Type of "Windows Application" ( fig 2 , fig 3.);

002.GIF 

Figure 2.

003.GIF

Figure 3. 

The third project named "GetData" has Output Type of  "Class Library" (fig 4.).

004.GIF

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.

005.GIF

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 !