Stock Tracker Custom Control in C# .NET


Introduction:

This article describes the construction of a custom control used to check stock prices as made available through a public web service. The article includes the source code for this custom control and well as a demonstration site used to test the control.

stocktracker1.gif

Figure 1:  Stock Tracker Custom Controls in Use

Getting Started:

The files included with this project include a web control library project and a demonstration web site. In order to get started, open the included zip file and install the two projects onto your file system. Open IIS and create a virtual directory for the web application. Open the solution into Visual 2005 and make any changes necessary to bring both projects into the solution. Once properly configured, your solution explorer should show these projects, references, and files:

Image2.gif

Figure 2:  Solution Explorer with Web App and Control Library

In examining the solution, note that the "StockTracker" control library contains only a single control and that control is called "StockDog". This project also includes a web reference that points to the http://www.webservicex.net site; this public site supports the web service used to capture the stock prices.

The web application contains only a single web page (default.aspx) and includes a reference to the "StockTracker" DLL.

The web application serves as a container used to test the custom control; the default.aspx page contains three separate instances of the custom control. Each of these three instances is directed to retrieve stock information from three separate stocks.

The Code:  StockDog

The "StockDog" custom control is constructed to retrieve the information from the web service upon initialization and to use that information to populate a collection of local member variables; each of the local member variables is set to contain one of the values collected from the web service.

The web service returns the requested data in the form of an XML string; this string is parsed to obtain the individual values used to populate the member variables. These member variables are in turn used to display the stock information when the page is rendered.

In examining the code, note that, aside from the default imports, only the System.XML class has been added. The class itself inherits from the WebControl class.

using System;

using System.Collections.Generic;

using System.ComponentModel;

using System.Text;

using System.Web;

using System.Web.UI;

using System.Web.UI.WebControls;

using System.Xml;

 

[DefaultProperty("StockTicker"), ToolboxData("<{0}:StockDog runat=server></{0}:StockDog>")]

public class StockDog : WebControl

Following the class declaration, a region entitled "Declarations" is created and within that region are the declarations for all of the private member variables used within the control.

#region "Declarations"

 

private net.webservicex.www.StockQuote mStocks;

private XmlDocument mXmlDoc;

private string mStockTicker;

private string mLast;

private string mDate;

private string mTime;

private string mChange;

private string mOpen;

private string mHigh;

private string mLow;

private string mVolume;

private string mMktCap;

private string mPrevClose;

private string mPercentChng;

private string mAnnRange;

private string mEarns;

private string mPE;

private string mName;

 

#endregion

After the variable declarations, there is another region defined (Methods) and within that region is the code used to capture the data from the web service, and to read the XML string returned by that service and use the values to populate the member variables. The init handler calls a subroutine called "Fetch" each time the control is initialized.  Fetch accepts a single argument in the form of a string bearing the stock symbol (e.g., MSFT for Microsoft).

Inside Fetch, a new XML document is created.  The mStocks string variable is used to capture the XML string returned from the web service's GetQuote web method. GetQuote accepts the stock symbol as an argument. The returned string is loaded as XML into the XML document.

After the data is returned and placed inside the XML document, the next windy bit of code locates each specific node and captures the value associated with that node which is in turn used to populate the appropriate local member variable.

The code contained in the Methods region is as follows:

#region "Methods"

 

private void StockDog_Init(object sender, System.EventArgs e)

{ 

    Fetch(StockTicker); 

} 

 

public void Fetch(string strStock)

{ 

    mXmlDoc = new XmlDocument();

    mStocks = new net.webservicex.www.StockQuote();

    string strQuote = mStocks.GetQuote(strStock);

    mXmlDoc.LoadXml(strQuote);

 

    // Set all of the private member variables to

    // contain the values from the xml string returned

    // from the web service 

    // Last

    XmlNodeList lastNodes;

    XmlNode lastNode;

    lastNodes = mXmlDoc.GetElementsByTagName("Last"); 

    foreach (XmlNode tempLoopVar_lastNode in lastNodes)

    {

        lastNode = tempLoopVar_lastNode;

        mLast = lastNode.ChildNodes[0].Value;

    }

 

    // Date

    XmlNodeList dateNodes;

    XmlNode dateNode;

    dateNodes = mXmlDoc.GetElementsByTagName("Date"); 

    foreach (XmlNode tempLoopVar_dateNode in dateNodes)

    {

        dateNode = tempLoopVar_dateNode;

        mDate = dateNode.ChildNodes[0].Value;

    }

 

    // Time

    XmlNodeList timeNodes;

    XmlNode timeNode;

    timeNodes = mXmlDoc.GetElementsByTagName("Time");

    foreach (XmlNode tempLoopVar_timeNode in timeNodes)

    {

        timeNode = tempLoopVar_timeNode;

        mTime = timeNode.ChildNodes[0].Value;

    }

 

    // Change

    XmlNodeList changeNodes;

    XmlNode changeNode;

    changeNodes = mXmlDoc.GetElementsByTagName("Change"); 

    foreach (XmlNode tempLoopVar_changeNode in changeNodes)

    {

        changeNode = tempLoopVar_changeNode;

        mChange = changeNode.ChildNodes[0].Value;

    }

 

    // Open

    XmlNodeList openNodes;

    XmlNode openNode;

    openNodes = mXmlDoc.GetElementsByTagName("Open"); 

    foreach (XmlNode tempLoopVar_openNode in openNodes)

    {

        openNode = tempLoopVar_openNode;

        mOpen = openNode.ChildNodes[0].Value;

    }

 

    // High

    XmlNodeList highNodes;

    XmlNode highNode;

    highNodes = mXmlDoc.GetElementsByTagName("High"); 

    foreach (XmlNode tempLoopVar_highNode in highNodes)

    {

        highNode = tempLoopVar_highNode;

        mHigh = highNode.ChildNodes[0].Value;

    }

 

    // Low

    XmlNodeList lowNodes;

    XmlNode lowNode;

    lowNodes = mXmlDoc.GetElementsByTagName("Low"); 

    foreach (XmlNode tempLoopVar_lowNode in lowNodes)

    {

        lowNode = tempLoopVar_lowNode;

        mLow = lowNode.ChildNodes[0].Value;

    }

 

    // Volume

    XmlNodeList volumeNodes;

    XmlNode volumeNode;

    volumeNodes = mXmlDoc.GetElementsByTagName("Volume"); 

    foreach (XmlNode tempLoopVar_volumeNode in volumeNodes)

    {

        volumeNode = tempLoopVar_volumeNode;

        mVolume = volumeNode.ChildNodes[0].Value;

    }

 

    // MrkCap

    XmlNodeList MrkCapNodes;

    XmlNode MrkCapNode;

    MrkCapNodes = mXmlDoc.GetElementsByTagName("MktCap"); 

    foreach (XmlNode tempLoopVar_MrkCapNode in MrkCapNodes)

    {

        MrkCapNode = tempLoopVar_MrkCapNode;

        mMktCap = MrkCapNode.ChildNodes[0].Value;

    }

 

    // PrevClose

    XmlNodeList prevCloseNodes;

    XmlNode prevCloseNode;

    prevCloseNodes = mXmlDoc.GetElementsByTagName("PreviousClose"); 

    foreach (XmlNode tempLoopVar_prevCloseNode in prevCloseNodes)

    {

        prevCloseNode = tempLoopVar_prevCloseNode;

        mPrevClose = prevCloseNode.ChildNodes[0].Value;

    }

 

    // mPercentChng

    XmlNodeList percentChngNodes;

    XmlNode percentChngNode;

    percentChngNodes = mXmlDoc.GetElementsByTagName("PercentageChange"); 

    foreach (XmlNode tempLoopVar_percentChngNode in percentChngNodes)

    {

        percentChngNode = tempLoopVar_percentChngNode;

        mPercentChng = percentChngNode.ChildNodes[0].Value;

    }

 

    // mAnnRange

    XmlNodeList annRangeNodes;

    XmlNode annRangeNode;

    annRangeNodes = mXmlDoc.GetElementsByTagName("AnnRange"); 

    foreach (XmlNode tempLoopVar_annRangeNode in annRangeNodes)

    {

        annRangeNode = tempLoopVar_annRangeNode;

        mAnnRange = annRangeNode.ChildNodes[0].Value;

    }

 

    // mEarns

    XmlNodeList earnsNodes;

    XmlNode earnsNode;

    earnsNodes = mXmlDoc.GetElementsByTagName("Earns"); 

    foreach (XmlNode tempLoopVar_earnsNode in earnsNodes)

    {

        earnsNode = tempLoopVar_earnsNode;

        mEarns = earnsNode.ChildNodes[0].Value;

    }

 

    // mPE

    XmlNodeList PENodes;

    XmlNode PENode;

    PENodes = mXmlDoc.GetElementsByTagName("P-E"); 

    foreach (XmlNode tempLoopVar_PENode in PENodes)

    {

        PENode = tempLoopVar_PENode;

        mPE = PENode.ChildNodes[0].Value;

    }

 

    // mName

    XmlNodeList nameNodes;

    XmlNode nameNode;

    nameNodes = mXmlDoc.GetElementsByTagName("Name"); 

    foreach (XmlNode tempLoopVar_nameNode in nameNodes)

    {

        nameNode = tempLoopVar_nameNode;

        mName = nameNode.ChildNodes[0].Value;

    } 

}

 

#endregion

The next region defined in the code is called "Properties"; as you likely guessed, this section contains the properties used by the control. In this case, aside from what was passed down through the inheritance of the WebControl class, the only property to define is a string value used to contain the stock symbol. The properties region and its single property are defined as follows:

#region "Properties"

 

[Category("Stock Symbol") ]

[ Browsable(true) ]

[ Description("Enter the stock ticker value.")]

public string StockTicker

{

    get

    {

        return mStockTicker;

    }

    set

    {

        mStockTicker = value;

    }

}

 

#endregion

The attributes of category, browsable, and description are used to provide design time support for the custom control. The category and description text will be displayed in the IDE's property editor whenever this control is selected by the developer using the control.

Having captured the values from the XML string returned from the web service, and having populated the local member variables using the values contained in the XML, the only remaining step is to render the control. A region entitled, "Rendering" follows and it contains the code necessary to render the control on the page.

The code used to render the control is pretty simple; the HtmlTextWriter is used to define a table  and set up its characteristics (cell padding and border in this example), each row of the table contains two cells, a label and its associated value are placed into each of those two cells. Once all of the data has been written into the table, the ending tag is rendered and the control is complete.

Naturally, you can change the configuration of the table or remove some of the data returned from the web service by making changes in the definition of the HTML as defined through the HtmlTextWriter. The RenderContents subroutine is overridden and the HTML is formatted within this subroutine through the use of the HtmlTextWriter. 

#region "Rendering"

 

protected override void RenderContents(HtmlTextWriter output)

{ 

    output.AddAttribute(HtmlTextWriterAttribute.Cellpadding, "3");

    output.AddAttribute(HtmlTextWriterAttribute.Border, "1");

    output.RenderBeginTag(HtmlTextWriterTag.Table);

 

    output.RenderBeginTag(HtmlTextWriterTag.Tr);

 

    output.AddAttribute(HtmlTextWriterAttribute.Align, "left");

    output.RenderBeginTag(HtmlTextWriterTag.Td);

    output.Write("<b>Stock: </b>");

    output.RenderEndTag();

    output.RenderBeginTag(HtmlTextWriterTag.Td);

    output.Write(StockTicker);

    output.RenderEndTag();

 

    output.RenderEndTag();

    output.RenderBeginTag(HtmlTextWriterTag.Tr);

 

    output.AddAttribute(HtmlTextWriterAttribute.Align, "left");

    output.RenderBeginTag(HtmlTextWriterTag.Td);

    output.Write("<b>Company: </b>");

    output.RenderEndTag();

    output.RenderBeginTag(HtmlTextWriterTag.Td);

    output.Write(mName);

    output.RenderEndTag();

 

    output.RenderEndTag();

    output.RenderBeginTag(HtmlTextWriterTag.Tr);

 

    output.AddAttribute(HtmlTextWriterAttribute.Align, "left");

    output.RenderBeginTag(HtmlTextWriterTag.Td);

    output.Write("<b>Last: </b>");

    output.RenderEndTag();

    output.RenderBeginTag(HtmlTextWriterTag.Td);

    output.Write(mLast);

    output.RenderEndTag();

 

    output.RenderEndTag();

    output.RenderBeginTag(HtmlTextWriterTag.Tr);

 

    output.AddAttribute(HtmlTextWriterAttribute.Align, "left");

    output.RenderBeginTag(HtmlTextWriterTag.Td);

    output.Write("<b>Date: </b>");

    output.RenderEndTag();

    output.RenderBeginTag(HtmlTextWriterTag.Td);

    output.Write(mDate);

    output.RenderEndTag();

   

    output.RenderEndTag();

    output.RenderBeginTag(HtmlTextWriterTag.Tr);

  

    output.AddAttribute(HtmlTextWriterAttribute.Align, "left");

    output.RenderBeginTag(HtmlTextWriterTag.Td);

    output.Write("<b>Time: </b>");

    output.RenderEndTag();

    output.RenderBeginTag(HtmlTextWriterTag.Td);

    output.Write(mTime);

    output.RenderEndTag();

 

    output.RenderEndTag();

    output.RenderBeginTag(HtmlTextWriterTag.Tr);

 

    output.AddAttribute(HtmlTextWriterAttribute.Align, "left");

    output.RenderBeginTag(HtmlTextWriterTag.Td);

    output.Write("<b>Change: </b>");

    output.RenderEndTag();

    output.RenderBeginTag(HtmlTextWriterTag.Td);

    output.Write(mChange);

    output.RenderEndTag();

 

    output.RenderEndTag();

    output.RenderBeginTag(HtmlTextWriterTag.Tr);

 

    output.AddAttribute(HtmlTextWriterAttribute.Align, "left");

    output.RenderBeginTag(HtmlTextWriterTag.Td);

    output.Write("<b>Open: </b>");

    output.RenderEndTag();

    output.RenderBeginTag(HtmlTextWriterTag.Td);

    output.Write(mOpen);

    output.RenderEndTag();

 

    output.RenderEndTag();

    output.RenderBeginTag(HtmlTextWriterTag.Tr);

 

    output.AddAttribute(HtmlTextWriterAttribute.Align, "left");

    output.RenderBeginTag(HtmlTextWriterTag.Td);

    output.Write("<b>High: </b>");

    output.RenderEndTag();

    output.RenderBeginTag(HtmlTextWriterTag.Td);

    output.Write(mHigh);

    output.RenderEndTag();

 

    output.RenderEndTag();

    output.RenderBeginTag(HtmlTextWriterTag.Tr);

 

    output.AddAttribute(HtmlTextWriterAttribute.Align, "left");

    output.RenderBeginTag(HtmlTextWriterTag.Td);

    output.Write("<b>Low: </b>");

    output.RenderEndTag();

    output.RenderBeginTag(HtmlTextWriterTag.Td);

    output.Write(mLow);

    output.RenderEndTag();

 

    output.RenderEndTag();

    output.RenderBeginTag(HtmlTextWriterTag.Tr);

 

    output.AddAttribute(HtmlTextWriterAttribute.Align, "left");

    output.RenderBeginTag(HtmlTextWriterTag.Td);

    output.Write("<b>Volume: </b>");

    output.RenderEndTag();

    output.RenderBeginTag(HtmlTextWriterTag.Td);

    output.Write(mVolume);

    output.RenderEndTag();

 

    output.RenderEndTag();

    output.RenderBeginTag(HtmlTextWriterTag.Tr);

 

    output.AddAttribute(HtmlTextWriterAttribute.Align, "left");

    output.RenderBeginTag(HtmlTextWriterTag.Td);

    output.Write("<b>Market Cap: </b>");

    output.RenderEndTag();

    output.RenderBeginTag(HtmlTextWriterTag.Td);

    output.Write(mMktCap);

    output.RenderEndTag();

 

    output.RenderEndTag();

    output.RenderBeginTag(HtmlTextWriterTag.Tr);

 

    output.AddAttribute(HtmlTextWriterAttribute.Align, "left");

    output.RenderBeginTag(HtmlTextWriterTag.Td);

    output.Write("<b>Previous Close: </b>");

    output.RenderEndTag();

    output.RenderBeginTag(HtmlTextWriterTag.Td);

    output.Write(mPrevClose);

    output.RenderEndTag();

 

    output.RenderEndTag();

    output.RenderBeginTag(HtmlTextWriterTag.Tr);

 

    output.AddAttribute(HtmlTextWriterAttribute.Align, "left");

    output.RenderBeginTag(HtmlTextWriterTag.Td);

    output.Write("<b>Percent Change: </b>");

    output.RenderEndTag();

    output.RenderBeginTag(HtmlTextWriterTag.Td);

    output.Write(mPercentChng);

    output.RenderEndTag();

 

    output.RenderEndTag();

    output.RenderBeginTag(HtmlTextWriterTag.Tr);

 

    output.AddAttribute(HtmlTextWriterAttribute.Align, "left");

    output.RenderBeginTag(HtmlTextWriterTag.Td);

    output.Write("<b>Annual Range: </b>");

    output.RenderEndTag();

    output.RenderBeginTag(HtmlTextWriterTag.Td);

    output.Write(mAnnRange);

    output.RenderEndTag();

 

    output.RenderEndTag();

    output.RenderBeginTag(HtmlTextWriterTag.Tr);

 

    output.AddAttribute(HtmlTextWriterAttribute.Align, "left");

    output.RenderBeginTag(HtmlTextWriterTag.Td);

    output.Write("<b>Earnings: </b>");

    output.RenderEndTag();

    output.RenderBeginTag(HtmlTextWriterTag.Td);

    output.Write(mEarns);

    output.RenderEndTag();

  

    output.RenderEndTag();

    output.RenderBeginTag(HtmlTextWriterTag.Tr);

 

    output.AddAttribute(HtmlTextWriterAttribute.Align, "left");

    output.RenderBeginTag(HtmlTextWriterTag.Td);

    output.Write("<b>P-E: </b>");

    output.RenderEndTag();

    output.RenderBeginTag(HtmlTextWriterTag.Td);

    output.Write(mPE);

    output.RenderEndTag();

 

    output.RenderEndTag();

 

    output.RenderEndTag(); 

}

 

#endregion

The Code:  The Demo Site's Default Page

The default.aspx page contained within the demo site serves only a test container for the control. The page contains a panel with a centered table. The table contains three columns and one row. A single custom control is added to each of the three columns. Each of the custom controls has its StockTicker property set through property editor in the IDE. Beneath the table is a button; this button carries the label "Update" and the button only serves to force a post back which in turn will force the update of each of the custom controls.

stocktracker3.gif

Figure 3:  Setting the StockTicker Property at Design Time

Summary

This project was intended to describe a useful, easy to build custom control. While this demonstration was limited to describing the StockTracker custom control, the same approach applied herein would work with a variety of other custom controls.

NOTE: THIS ARTICLE IS CONVERTED FROM VB.NET TO C# USING A CONVERSION TOOL. ORIGINAL ARTICLE CAN BE FOUND ON VB.NET Heaven (http://www.vbdotnetheaven.com/). 


Similar Articles