Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / ASP.NET

Track Stock Prices with a Custom Control

4.31/5 (6 votes)
2 Nov 20065 min read 1   454  
This article describes the construction of a custom control used to check stock prices as made available through a public web service.

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 as well as a demonstration site used to test the control.

Image 1

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 Studio 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:

Image 2

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 this 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.

VB
Imports System
Imports System.Collections.Generic
Imports System.ComponentModel
Imports System.Text
Imports System.Web
Imports System.Web.UI
Imports System.Web.UI.WebControls
Imports System.Xml


<DefaultProperty("StockTicker"), _
     ToolboxData("<{0}:StockDog " & _ 
     "runat=server></{0}:StockDog>")> _
Public Class StockDog
    Inherits 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.

VB
#Region "Declarations"
     Private mStocks As net.webservicex.www.StockQuote
    Private mXmlDoc As XmlDocument
    Private mStockTicker As String
    Private mLast As String
    Private mDate As String
    Private mTime As String
    Private mChange As String
    Private mOpen As String
    Private mHigh As String
    Private mLow As String
    Private mVolume As String
    Private mMktCap As String
    Private mPrevClose As String
    Private mPercentChng As String
    Private mAnnRange As String
    Private mEarns As String
    Private mPE As String
    Private mName As String

#End Region

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:

VB
#Region "Methods"
     Private Sub StockDog_Init(ByVal sender As Object, _
            ByVal e As System.EventArgs) Handles Me.Init

        Fetch(StockTicker)

    End Sub


    Public Sub Fetch(ByVal strStock As String)

        mXmlDoc = New XmlDocument()
        mStocks = New net.webservicex.www.StockQuote
        Dim strQuote As String = 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 
        Dim lastNodes As XmlNodeList
        Dim lastNode As XmlNode
        lastNodes = mXmlDoc.GetElementsByTagName("Last")

        For Each lastNode In lastNodes
            mLast = lastNode.ChildNodes(0).Value
        Next

        ' Date
        Dim dateNodes As XmlNodeList
        Dim dateNode As XmlNode
        dateNodes = mXmlDoc.GetElementsByTagName("Date")

        For Each dateNode In dateNodes
            mDate = dateNode.ChildNodes(0).Value
        Next

        ' Time
        Dim timeNodes As XmlNodeList
        Dim timeNode As XmlNode
        timeNodes = mXmlDoc.GetElementsByTagName("Time")

        For Each timeNode In timeNodes
            mTime = timeNode.ChildNodes(0).Value
        Next

        ' Change
        Dim changeNodes As XmlNodeList
        Dim changeNode As XmlNode
        changeNodes = mXmlDoc.GetElementsByTagName("Change")

        For Each changeNode In changeNodes
            mChange = changeNode.ChildNodes(0).Value
        Next

        ' Open
        Dim openNodes As XmlNodeList
        Dim openNode As XmlNode
        openNodes = mXmlDoc.GetElementsByTagName("Open")

        For Each openNode In openNodes
            mOpen = openNode.ChildNodes(0).Value
        Next

        ' High
        Dim highNodes As XmlNodeList
        Dim highNode As XmlNode
        highNodes = mXmlDoc.GetElementsByTagName("High")

        For Each highNode In highNodes
            mHigh = highNode.ChildNodes(0).Value
        Next

        ' Low
        Dim lowNodes As XmlNodeList
        Dim lowNode As XmlNode
        lowNodes = mXmlDoc.GetElementsByTagName("Low")

        For Each lowNode In lowNodes
            mLow = lowNode.ChildNodes(0).Value
        Next

        ' Volume
        Dim volumeNodes As XmlNodeList
        Dim volumeNode As XmlNode
        volumeNodes = mXmlDoc.GetElementsByTagName("Volume")

        For Each volumeNode In volumeNodes
            mVolume = volumeNode.ChildNodes(0).Value
        Next

        ' MrkCap
        Dim MrkCapNodes As XmlNodeList
        Dim MrkCapNode As XmlNode
        MrkCapNodes = mXmlDoc.GetElementsByTagName("MktCap")

        For Each MrkCapNode In MrkCapNodes
            mMktCap = MrkCapNode.ChildNodes(0).Value
        Next

        ' PrevClose
        Dim prevCloseNodes As XmlNodeList
        Dim prevCloseNode As XmlNode
        prevCloseNodes = _
            mXmlDoc.GetElementsByTagName("PreviousClose")

        For Each prevCloseNode In prevCloseNodes
            mPrevClose = prevCloseNode.ChildNodes(0).Value
        Next

        ' mPercentChng
        Dim percentChngNodes As XmlNodeList
        Dim percentChngNode As XmlNode
        percentChngNodes = _
           mXmlDoc.GetElementsByTagName("PercentageChange")

        For Each percentChngNode In percentChngNodes
            mPercentChng = percentChngNode.ChildNodes(0).Value
        Next

        ' mAnnRange
        Dim annRangeNodes As XmlNodeList
        Dim annRangeNode As XmlNode
        annRangeNodes = mXmlDoc.GetElementsByTagName("AnnRange")

        For Each annRangeNode In annRangeNodes
            mAnnRange = annRangeNode.ChildNodes(0).Value
        Next

        ' mEarns
        Dim earnsNodes As XmlNodeList
        Dim earnsNode As XmlNode
        earnsNodes = mXmlDoc.GetElementsByTagName("Earns")

        For Each earnsNode In earnsNodes
            mEarns = earnsNode.ChildNodes(0).Value
        Next

        ' mPE
        Dim PENodes As XmlNodeList
        Dim PENode As XmlNode
        PENodes = mXmlDoc.GetElementsByTagName("P-E")

        For Each PENode In PENodes
            mPE = PENode.ChildNodes(0).Value
        Next

        ' mName
        Dim nameNodes As XmlNodeList
        Dim nameNode As XmlNode
        nameNodes = mXmlDoc.GetElementsByTagName("Name")

        For Each nameNode In nameNodes
            mName = nameNode.ChildNodes(0).Value
        Next

    End Sub

#End Region

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:

VB
#Region "Properties"
     <Category("Stock Symbol")> _
    <Browsable(True)> _
    <Description("Enter the stock ticker value.")> _
    Public Property StockTicker() As String
        Get
            Return mStockTicker
        End Get
        Set(ByVal value As String)
            mStockTicker = value
        End Set
    End Property

#End Region

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.

VB
#Region "Rendering"
     Protected Overrides Sub RenderContents(ByVal output As HtmlTextWriter)

        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()

    End Sub

#End Region

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.

Image 3

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.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here