This blog post will introduce the concept of a Range that
Visiblox Charts uses to describe the limits of an axis. It will
cover the following topics:
What is a Range?
In Visiblox Charts we have a notion, described by the
IRange interface, of a value range. This is primarily
used to tell an axis how much of a value range to show. The
IRange interface is really quite simple and basically
just has a Maximum and a Minimum (and a
few methods to grow the range). There are various implementations
of IRange (or rather more specifically
IRange<T>) out of the box which provide strongly
typed versions of Maximum and Minimum.
These are: DoubleRange, DateTimeRange and
CategoryRange, which map quite nicely to the various
axes which are provided out of the box with Visiblox Charts.
Auto-Calculated Ranges
If you just create a chart, assign a chart series (e.g. a
LineSeries) to it, add some data to that chart series
via the DataSeries class and run your project you will
notice that despite never having defined axes, your chart ends up
with an X and Y axis. The reason for this is that on first load,
the chart makes a best effort at creating axes of the correct type
based on the data in the data series. Once the axes are created,
they then need to figure out how much of a value range to show,
this is done by creating a range of the appropriate type, which can
be done by calling the IAxis.CreateRange() method.
The chart then iterates over all the data points in the data
series and growing the range to include all those points. Finally a
little bit extra is added to allow for a margin. In many cases that
works pretty well and is good enough, however sometimes you might
want more control over the size of the range or to avoid the
auto-range calculation to help performance. In such cases you can
manually create a range and assign it to the
IAxis.Range property.
Setting a Fixed Range
Creating a range is pretty simple. If you know what kind of
range you need you can just construct it, for example:
//if your axis is a LinearAxis or a LogarithmicAxis
var range1 = new DoubleRange();
range1.Minimum = 0;
range1.Maximum = 10;
Chart.XAxis.Range = range1;
//if your axis is a DateTimeAxis or DiscontinousDateTimeAxis
var range2 = new DateTimeRange();
range2.Minimum = DateTime.Now;
range2.Maximum = DateTime.Now.AddDays(10);
Chart.XAxis.Range = range2;
//if your axis is a CategoryAxis
var range3 = new CategoryRange();
//for the CategoryRange it's probably better to use the Grow(IComparable) method
//so that you get the full set of categories you want
range3.Grow("Category1");
range3.Grow("Category2");
range3.Grow("Category3");
range3.Grow("Category4");
Chart.XAxis.Range = range3;
Alternatively, and probably more safely, you can just ask the
axis for a new Range. To do this just call
CreateRange() as so:
//if your axis is a LinearAxis or a LogarithmicAxis you will get a DoubleRange
var range1 = Chart.XAxis.CreateRange();
range1.Minimum = 0;
range1.Maximum = 10;
Chart.XAxis.Range = range1;
//if your axis is a DateTimeAxis or DiscontinousDateTimeAxis you will get a DateTimeRange
var range2 = Chart.XAxis.CreateRange();
range2.Minimum = DateTime.Now;
range2.Maximum = DateTime.Now.AddDays(10);
Chart.XAxis.Range = range2;
//if your axis is a CategoryAxis you will get a CategoryRange
var range3 = Chart.XAxis.CreateRange();
//for the CategoryRange it's probably better to use the Grow(IComparable) method
//so that you get the full set of categories you want
range3.Grow("Category1");
range3.Grow("Category2");
range3.Grow("Category3");
range3.Grow("Category4");
Chart.XAxis.Range = range3;
As you can see we simply assign the newly constructed range
object to the IAxis.Range property and that is then
adopted as the new range, no questions asked.
IAxis.Range vs IAxis.ActualRange
If you look closely at the IAxis interface,
however, you will see that there is actually a Range
and an ActualRange property. So what's the deal? Well
Visiblox Charts follows the same convention as a lot of the rest of
the .NET APIs in that the Range is the user settable
version and the ActualRange is what is actually
applied. What then is the difference?
- If you're setting a range, never set
ActualRange, always set Range.
ActualRange may be manipulated or replaced by the
chart at any time, but it will never touch the Range
set by the user.
- If you're interested in finding out what the range
actually is being used by the chart internally, or in
binding to range changes, bind to the
ActualRange, it
will always be more up-to-date than Range.
If you look even closer, you will see that Range
and ActualRange aren't, in fact, the same type.
Range is of type IRange, but
ActualRange is of type
IRangeWithEffectiveLimits.
IRange and IRangeWithEffectiveLimits
As we mentioned earlier the IRange interface is
really pretty simple. The IRangeWithEffectiveLimits is
a good bit more complicated. In particular it adds a new concept of
"effective" range. Where the IRange describes the axis
minimum and maximum values, the effective range describes the
currently visible range after zoom is applied. For example, if you
Range is 0 to 10, and you zoom in to show only the first half of
that range, then your Range will still be 0 to 10 but your
effective range will be 0 to 5.
As a result if you want to be notified of changes in zoom or you
want to know what is currently visible, irrespective of what zoom
is applied, use the ActualRange.EffectiveMaximum and
ActualRange.EffectiveMinimum dependency properties.
You can also safely bind to these and be notified of zoom changes
on the fly. Beware though, that these properties are updated "live"
as the zoom is happening so if you have processing intensive
operations happening here, you will slow down your chart's zoom
operation.
That is fine for almost all purposes you'll come across, but
there is one more twist to the tale of the Visiblox Charts'
Range.
Exploring Continuous and Discontinuous
Ranges
In order to smoothly zoom or pan based on a user's mouse
operations, all ranges have to be in a continuous space. With
linear ranges like the DoubleRange that's no problem
because obviously real numbers are pretty continuous. The problem
enters the picture when your value ranges aren't continuous, such
as the category range or on a discontinuous date time axis. Here we
have a problem because what if the user wants to zoom into
somewhere that isn't exactly on the category boundary. For example,
look at this image:

Here we want to zoom from somewhere halfway between the "Canada"
and the "UK" category to somewhere towards the "France" category.
The EffectiveMinimum and EffectiveMaximum
equivalent of those two would be "UK" and "France" respectively,
but it has no notion of how much of each is included. In
order to do that we need to have a notion of continuous and
discrete value ranges and a way to convert from one to the
other.
For this purpose IRangeWithEffectiveLimits has a
ContinuousEffectiveMinimum and
ContinuousEffectiveMaximum which is the mapping of the
EffectiveMinimum and EffectiveMaximum
onto some continuous range. In the case of the
CategoryRange that works out so each category is an
increment of 1 on a double range. So in the above example the
ContinousEffectiveMinimum would be something like 1.5
(i.e. just before the second category) and the
ContinuousEffectiveMaximum would be 3.7 (i.e. just
before the 4th category) or so.
To map arbitrary values between the discontinuous range and the
continuous range, there are two methods on
IRangeWithEffectiveLimits:
IComparable ToDiscreteValueRange(IComparable continuousValue);
IComparable ToContinuousValueRange(IComparable discreteValue);
Thankfully, unless you are implementing your own axis or doing
some very complex range or zoom logic, you should never need to
worry about the continuous range, but it's worth knowing that it's
there should you ever need it.
Conclusion
This article covered a basic overview of the Visiblox Charts
Range concept. By using this and associated concepts such as the
effective range and the continuous range, you are given full
control over defining the range to show as well as determining what
range is currently set and which is currently visible.
There are a few points to remember:
- Set fixed range by assigning
IAxis.Range
which is of type IRange
- Read values from
IAxis.ActualRange but
never set it
- The dependency properties
IAxis.ActualRange.EffectiveMinimum and
IAxis.ActualRange.EffectiveMaximum define the limits
of the currently visible portion of the axis
- There is a continuous and a discrete axis range and
where necessary you can switch between them using methods on
IRangeWithEffectiveLimits
Hopefully you've found this useful information. Why not download Visiblox Charts
for free and give this stuff a try now.