Introduction
The WPFGraph
library allows production of scatter graphs (and in later versions other graph types) which can be customised by the user at runtime. The library allows dynamic loading of custom point and line renderers and automatically generates a customisation dialog to allow the end user a graphical user interface for configuration of dynamically loaded content.
Background
"A scatter graph or scatter diagram is a type of display that contains one or more scatter plots each of which use Cartesian coordinates to display values for two variables for a set of data. The data is displayed as a collection of points, each having the value of one variable determining the position on the horizontal axis and the value of the other variable determining the position on the vertical axis.
A scatter plot only specifies dependent or independent variables when a variable exists that is under the control of the experimenter. If a parameter exists that is systematically incremented and/or decremented by the experimenter, it is called the control parameter or independent variable and is customarily plotted along the horizontal axis. The measured or dependent variable is customarily plotted along the vertical axis. If no dependent variable exists, either type of variable can be plotted on either axis and a scatter plot will illustrate only the degree of correlation (not causation) between two variables.
A scatter plot can suggest various kinds of correlations between variables with a certain confidence level. Correlations may be positive (rising), negative (falling), or null (uncorrelated). If the pattern of dots slopes from lower left to upper right, it suggests a positive correlation between the variables being studied. If the pattern of dots slopes from upper left to lower right, it suggests a negative correlation. A line of best fit can be drawn in order to study the correlation between the variables. An equation for the correlation between the variables can be determined by established best-fit procedures. For a linear correlation, the best-fit procedure is known as linear regression and is guaranteed to generate a correct solution in a finite time. Unfortunately, no universal best-fit procedure is guaranteed to generate a correct solution for arbitrary relationships.
One of the most powerful aspects of a scatter plot, however, is its ability to show nonlinear relationships between variables. Furthermore, if the data is represented by a mixture model of simple relationships, these relationships will be visually evident as superimposed patterns.
For example, to display values for "lung capacity" (first variable) and how long that person could hold his breath (second variable), a researcher would choose a group of people to study, then measure each one's lung capacity (first variable) and how long that person could hold his breath (second variable). The researcher would then plot the data in a scatter plot, assigning "lung capacity" to the horizontal axis, and "time holding breath" to the vertical axis. A person with a lung capacity of 400 cc who held his breath for 21.7 seconds would be represented by a single dot on the scatter plot at the point (400, 21.7) in the Cartesian coordinates. The scatter plot of all the people in the study would enable the researcher to obtain a visual comparison of the two variables in the data set, and help to determine what kind of relationship there might be between the two variables."
From Wikipedia -> Scatterplot.
Using the Code
The WPFScatterGraph UserControl
forms the core functionality and visual hook. The WPFScatterGraph
may be added as per any WPF control, remembering to include the relevant namespace as below;
<xmlns:graph="clr-namespace:DNBSoft.WPF.WPFGraph;assembly=WPFGraph">
<graph:WPFScatterGraph />
A WPFScatterGraph
is comprised of WPFGraphSeries
(representing a line of data, accessed via the .Series
property of WPFScatterGraph
), which is formed of WPFGraphDataPoint
s (an X and Y co-ordinate, accessed via the .Points
property of WPFGraphSeries
).
For example a graph showing a line of the formula y = 2 * x
may be constructed via the following...
WPFGraphSeries series = new WPFGraphSeries();
for (int i = 0; i < 16; i++) {
WPFGraphDataPoint f = new WPFGraphDataPoint();
f.X = i * 2;
f.Y = i;
series.Points.Add(f);
}
graphDisplay.Series.Add(series);
... where graphDisplay
is a created WPFScatterGraph
. When adding large numbers of points to a series, it is strongly recommended to add the points prior to adding the series to the graph to improve performance.
Graph Options
All graph options are available via the following properties of WPFScatterGraph
:
AxisBrush
- Set
s/Get
s the brush used to render the axis (both horizontal and vertical)
AxisThickness
- Set
s/Get
s the thickness of the line used for the axis (both horizontal and vertical)
AxisVerticalWidth
- Set
s/Get
s the distance between the vertical y axis and the left of the control
AxisHorizontalHeight
- Set
s/Get
s the distance between the horizontal x axis and the bottom of the control
AxisFontSize
- Set
s/Get
s the size of the font used to render the titles on the x and y axis
AxisTitleTickBrush
- Set
s/Get
s the brush used to render axis ticks (both horizontal and vertical)
AxisTitleTickThickness
- Set
s/Get
s the thickness of axis ticks (both horizontal and vertical)
MinXRange
- Set
s/Get
s the minimum value shown on the x axis (see also MinYRange
); values outside this range are not rendered
MaxXRange
- Set
s/Get
s the maximum value shown on the x axis (see also MaxYRange
); values outside this range are not rendered
IntervalXRange
- Set
s/Get
s the tick interval for the x axis (see also IntervalYRange
); does not affect rendering
XAxisTitle
- Set
s/Get
s the title on the X axis (see also YAxisTitle
)
Formatting
Both entire series and individual data points may be custom rendered via the .PointRenderer
and .LineRenderer
methods of both WPFGraphSeries
and WPFGraphDataPoint
; the properties take IWPFGraphPointRenderer
and IWPFGraphLineRenderer
objects respectively, however WPFGraphDataPoint
s may also be set to null
indicating the rendering system should use the series renderer and not a point specific renderer. Typically the WPFGraphDataPoint
renderers are only used to illustrate individual points and not to set the styling on an entire series (although it is possible to do this). The "Format A Data Series..." dialog (shown in Figure 3 above) currently only supports setting of the line and point renderers for the entire series and not individual data points. The dialog is accessible by right clicking anywhere on the graph space itself (not including axis or axis label space. The "Format A Data Series..." dialog should be relatively intuitive and therefore isn't discussed in detail herein, however will include a help button in later versions.
Custom Renderers
The WPFGraph
library supports the use of custom renderers for both line renderers and point renderers, but rather than require recompiling of the library to include new renderers supports custom renderers via dynamic loading and reflection. To implement a custom renderer the steps are:
- Create a new DLL project
- Create objects implementing
IWPFGraphPointRenderer
and / or IWPFGraphLineRenderer
- Compile to DLL
- Place DLL in the same directory as the application (future versions any sub-directory)
Implementing the interfaces are relatively straightforward, however they are discussed below for completeness. Furthermore since many point renderers and line renderers will have a common base two helper classes are provided, WPFGraphLineRenderers.LineBase
and WPFGraphLineRenderers.PointBase
(hereafter LineBase
and PointBase
). While neither of these classes implement the interface
s of custom renderers, inheriting from these classes provides a basic custom render's required methods.
-
void render(WPFRenderParameters parameters, WPFGraphDataPoint p);
Render the point at parameters.transpose(WPFGraphEnumerations.Axis axis, p.X), parameters.transpose(WPFGraphEnumerations.Axis axis, p.Y)
. The WPFRenderParameters.transpose
method automatically converts from unscaled to scaled co-ordinates.
-
event WPFGraphDelegates.RendererChangedEventDelegate RendererChanged;
Fired whenever any custom renderer parameter has changed. RendererChangedEventArgs
contains the property altered, the old value, and the new value.
-
List<WPFGraphConfigurationParameter> getConfigurationParameters();
Returns a list of WPFGraphConfigurationParameter
objects, indicating which properties may be modified and the types they accept (currently only double
and SolidColorBrush
are supported, however other types will be added in the next release). When using LineBase
or PointBase base.getConfigurationParameters()
should be called and any custom properties added to the list before returning.
-
void setValue(String parameter, object value);
Set
s the property of name parameter to the value given. When using LineBase
or PointBase base.setValue()
should be called after testing to see if the property of name parameter is part of the custom renderer. It is preferable to set bounds on values but not throw an exception should the bounds be exceeded; e.g. for line thickness rather than throw an exception when a value of -1
is set, rather clip the value to the bounds of 0
to double.MaxValue
.
-
void getValue(String parameter, object value);
Get
s the value of the property of the name given. When using LineBase
or PointBase base.getValue()
should be called after testing to see if the property of name parameter is part of the custom renderer.
Points of Interest
This is another example of something which should be standard in .NET but sadly isn't.
The color picker is not my code but rather a sample color picker as produced by Microsoft (all rights reserved), however it should be noted that it isn't very good and will be replaced eventually when I have time! The color picker currently only supports SolidColorBrush
es and so manually setting a non-solid brush in code will cause the picker to crash.
Future Work
- Other graph types
- More default point and line renderers
Known Bugs
- Some graph options do not automatically update graph, this can be fixed via calling
IWPFGraph.Refresh()
.
History
- Version 1.0.0.0 - First build, supports scatter graphs (scatter plots)
Additional Licensing Notes
Please feel free to use this in your work, however please be aware that a modified Code Project Open License (CPOL) is in use; basically it is the same as the standard license except that this code must not be used for commercial or not-for-profit commercial use without prior authorisation. Please see license.txt or license.pdf in the included source and demo files.