Building an Interactive Chart Analysis Page

[Previous] [Next]

The Historical Charts page in this chapter's sample solution illustrates a few ways you can add some useful analysis features by writing code that interacts with the Chart control. This section will discuss the two most prominent techniques: displaying information about points on the chart with chart tips and using a Range Slider control to zoom in on a large axis.

Implementing Chart Tips

In Microsoft Excel charting, when you position your mouse cursor over a data point, a small, tooltip-like window appears that shows you the series, category, and data point value. Although the Chart control does not yet implement this feature, you can add it using a scriptlet developed by Jason Cahill, the ever-talented program manager for the Chart control. The source for the scriptlet (Charttips.scp) is included in the Scripts directory on the companion CD, so you can modify it to include any information you want.

A scriptlet is a small piece of reusable HTML and script that you can include in a web page much like you do a COM control. Scriptlets are documented in the MSDN libraries and work in Internet Explorer version 4.0 and higher. Although I will not describe in detail how to create a scriptlet in this book, I will show you some of the important parts of the chart tips scriptlet. First, let's look at how to use the scriptlet in an HTML page:

 <!-------------------------------------- BEGIN CHART TIPS ----------------> <!-- TODO: Add one handler, below, for each Chart control ----------------> <script language=vbscript for=csHistorical event="MouseMove(e)"> ChartTips.HandleChartTips ChartTips, csHistorical, e.x, e.y </script> <!-- Generic chart tip code (do not modify) ------------------------------> <object  id=ChartTips width=0 height=0  style="position:absolute;display:none" type="text/x-scriptlet"  data="../Scripts/Charttips.scp"> </object> <script language=vbscript for=document event="onmouseover()"> ChartTips.ClearChartTips ChartTips </script> <script language=vbscript for=document event="onmouseout()"> ChartTips.ClearChartTips ChartTips </script> <!--------------------------------------- END CHART TIPS -----------------> 

This HTML fragment adds the <object> tag for the scriptlet itself, specifying that its code (the less-than-intuitively-named "data" attribute) is in the Charttips.scp file in the Scripts directory. It also sets the object's position style to "absolute," meaning that it can be positioned using two-dimensional coordinates and can lay on top of another element (in this case, the Chart control). It then sets up a couple of event handlers to clear (hide) the chart tip when the mouse pointer is no longer over the Chart control. You must add a handler for the Chart control's MouseMove event, calling the HandleChartTips method. This allows the chart tip scriptlet to determine what chart element the mouse pointer is over and to display the appropriate content. Note that you can write your own additional code in the MouseMove event if you want. Just remember to call the HandleChartTips method in your handler at some point.

To see this chart tip in action, click the Historical Charts link on the left frame of the solution and move your mouse pointer over a data point. Figure 6-1 depicts what you will see.

click to view at full size.

Figure 6-1. Chart tips.

Now let's look at how Jason implemented the chart tips scriptlet. The scriptlet file contains this HTML fragment at the top:

 <body leftmargin=0px topmargin=0px> <table border=0 cellspacing=0 cellpadding=0>     <tr>         <!-- Style info removed for readability -->         <td id=tdTip nowrap bgcolor=infobackground style=... ></td>     </tr> </table> 

This is the viewable portion of the scriptlet, and it is a simple table cell formatted using a style attribute so that it looks like the chart tips in Excel. Since the scriptlet is just HTML, you can change this formatting to anything you want. Immediately following the HTML, you will see two public methods:

 Sub public_HandleChartTips(tip, csChart, x, y)     Set hTip = tip     ClearChartTips     tdTip.InnerText = GetTipText(csChart, x, y)     tip.Style.Left = (x + 10 + csChart.OffsetLeft) & "px"     tip.Style.Top = (y + 20 + csChart.OffsetTop) & "px"     tip.Width  = tdTip.offsetWidth & "px"     tip.Height = tdTip.offsetHeight & "px"     hChartTipTimer = SetTimeout("ShowChartTip", 500, "vbs") End Sub Sub public_ClearChartTips(tip)     Set hTip = tip     ClearChartTips End Sub 

We called these methods from the event handlers back in our HTML page. The first one sets up the tip to show the appropriate content (returned from the GetTipText method) and sizes the tip so that it is only big enough to display its content. HTML table cells provide an easy way to determine how big a tip must be because table cells automatically size to fit their content if you do not specify a particular width or height value. Jason also uses a timer to delay showing the tip for half a second (500 milliseconds), which is commonly done when implementing tip windows.

The second method hides the chart tip by calling ClearChartTips, which simply sets the tip's display attribute to "none." If an element's display attribute is set to "none," Internet Explorer will not display the element and will not reserve room for it when laying out the page.

The GetTipText method uses the Chart control's RangeFromPoint method to get the charting element at the specified X and Y coordinates and uses the TypeName function to determine what kind of object the method returned. This should sound familiar—we discussed this technique in Chapter 3. You can modify this function to return alternate text for the tip. For example, you might want to include other calculated values in the tip, such as the average for all points in the series or the current point's percentage contribution to the entire series or to all data points in the chart.

Zooming with a Range Slider Control

When plotting historical data, you can run into a situation in which you have many time values to display on the category axis but not enough room to display the data clearly. Instead of summarizing the data values to a higher level of aggregation or including only part of the data, you can provide a simple mechanism for a user to zoom in on an axis and show only part of the more granular data at a time. The user can still scroll left and right to see other time periods or can zoom out slightly to see more data at once.

I implemented this technique in the Historical Charts page. To follow the discussion in this section, it is best if you run the page and try using the Range Slider control to zoom in on part of the data. When you first load the page, the chart will look fairly crowded, as it does in Figure 6-2.

click to view at full size.

Figure 6-2. The Historical Charts page when first opened.

When you zoom in using the Range Slider control, the chart should be more readable as it is in Figure 6-3.

click to view at full size.

Figure 6-3. The Historical Charts page after zooming in.

The Range Slider control was also implemented by Jason and was developed using Visual Basic 6. I will not detail how Jason constructed this control because it has no direct relation to the Chart control. It simply takes a minimum and maximum value and returns the current scroll thumb minimum and maximum set by the user. The source code for this control is included on the companion CD in the SliderSource directory under the Chap06 directory. To include the Range Slider control in the HTML page, I used the following HTML fragment:

 <!-- Range Slider control --> <object classid="clsid:4A3C4CD7-F6AB-11D2-82A2-00A0C90565FE" id=RngSldr style="width:100%;height:20px"  codebase="RangeSliders.CAB#version=1,0,0,20"> </object> 

You can add the Range Slider control just as you would add one of the Office Web Components. You include an <object> tag in your HTML page indicating the COM class ID for the control. (Many HTML editors, including Microsoft FrontPage 2000 and Microsoft Visual InterDev, insert this for you.) This fragment also includes a codebase attribute in the <object> tag, referring to a CAB file in the Chap06 directory. The first time you run this page, Internet Explorer will detect that the Range Slider control is not yet installed on your system and will automatically download the CAB file and install it (after asking you if it's all right to do so).

Beware of Using Scaling.Maximum During Window_onLoad

When I first tried to implement this example, I attempted to use the category axis's Scaling.Maximum property to get the number of categories in the chart. This seems like a logical technique, and it does work most of the time. However, if you ask for this property during the Window_onLoad event (or any code called from that event handler), you might get back 0 or an uninitialized value. Sometimes it will work—typically when the page is first loaded. However, upon returning to the page later, you will get 0 or an uninitialized value and your code will fail.

This occurs because the container (in this case, Internet Explorer) might not have created a window for the Chart control yet. Until it does, the Chart control does not know what the scaling maximum and minimum should be because it doesn't know what size window the container will create. (Containers can give the control any size window they please.) However, the control does know how many points exist per series, and from there, you can determine the number of categories.

To initialize the Range Slider control, set its FilterMax and ThumbMax properties to match the number of categories in the chart. The following code, called from the BindChart function, accomplishes this:

 Sub InitRangeSlider(cht)     ' Local variables     Dim c           ' Constants object          m_fIgnoreSliderChange = True          ' Initialize the Range Slider control     RngSldr.FilterMax = cht.SeriesCollection(0).Points.Count     RngSldr.ThumbMax = RngSldr.FilterMax     RngSldr.FilterMin = 1     RngSldr.ThumbMin = 1          m_fIgnoreSliderChange = False      End Sub 'InitRangeSlider() 

This code asks the first series for the number of data points it has, which naturally returns the number of categories, as each series has a point for each category. Even if the first series did not have a value for certain categories, the Chart control still reports the total number of points, including the empty ones—so you can use this to determine how many categories your chart has.

All that's left to do is catch the Change event raised from the Range Slider control and adjust the category axis scaling to show only part of the axis. This has the visual effect of zooming in as the chart redraws, showing a smaller segment of data in the same physical space. Let's look at the code in the Range Slider control's Change event handler:

 Sub RngSldr_Change     ' Local variables     Dim ax          ' Temporary WCAxis object     Dim scl         ' Temporary WCScaling object     Dim ser         ' Temporary WCSeries object     Dim c           ' Constants object          If not m_fIgnoreSliderChange Then         Set c = csHistorical.Constants                   ' Get the scaling for the category axis         Set ax = csHistorical.Charts(0). _             Axes(csHistorical.Constants.chAxisPositionBottom)         Set scl = ax.Scaling         Set ser = ax.Parent.SeriesCollection(0)              ' Adjust it based on the Range Slider control's current settings         scl.Minimum = RngSldr.ThumbMin         scl.Maximum = RngSldr.ThumbMax          window.status = "Filtering for Categories " & _         ser.Points(RngSldr.ThumbMin - 1).GetValue(c.chDimCategories) & _         " through " & _         ser.Points(RngSldr.ThumbMax - 1).GetValue(c.chDimCategories)                  SetTickLabelSpacing ax     End If 'm_fIgnoreSliderChange      End Sub 'RngSldr_Change() 

Use the CacheSize Property When Binding to Data and Zooming

If you look closely at the HistoricalCharts.htm file, you'll see the use of a property in the LoadChartDataSources function called CacheSize:

 Set cds = cspace.ChartDataSources.Add() Set cds.DataSource = dsc cds.DataMember = "ChartData" & ct cds.CacheSize = 400 

You can use the CacheSize property to tell the Chart control to cache a certain number of data rows in its own internal structures. Doing so will increase the chart rendering speed quite a bit. So any time you perform effects such as zooming in, animating, or requiring the Chart control to redraw frequently, set this property to a number equal to or greater than the number of data rows in your resultset. Although it creates an extra copy of data in memory, the performance gain can be worthwhile if you need the chart to render quickly.

If you look in the object browser or the programming help for the Chart control, you will not see this property by default. This is because it is a hidden property. You can see hidden properties by checking the Show Hidden Members item on the Visual Basic or Visual InterDev object browser's context menu. Periodically, the OWC developers implement properties or methods we feel are stable and functional, but our testing team doesn't have time to fully test them. If we think a property or method will be genuinely useful, we will mark it as hidden—developers are free to use it at their own risk. So far, I have not had any problems using the CacheSize property, but it is not an "officially" supported feature.

This code gets a reference to the category axis (on the bottom for a line chart) and sets the axis's Scaling.Maximum and Scaling.Minimum properties to match the ThumbMin and ThumbMax properties of the Range Slider control. These properties return the current minimum and maximum values for the filtered range on the Range Slider control. Every time the user drags an edge of the control's thumb or the thumb itself, this event fires many times, updating the filter values accordingly.

To inform the user of what date range the chart is currently showing, I use the window.Status property to update Internet Explorer's status bar text. To get the actual category names (in this case, the date values) for the minimum and maximum categories, I use the Points collection of the first series to get the minimum and maximum data points. I then use the GetValue method of these objects to get the category name for the data point.



Programming Microsoft Office 2000 Web Components
Programming Microsoft Office 2000 Web Components (Microsoft Progamming Series)
ISBN: 073560794X
EAN: 2147483647
Year: 1999
Pages: 111
Authors: Dave Stearns

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net