We have a timeline feature of our software. It shows rows of little blobs which indicate data at different points of time. The code is c#, .Net 4 and WPF. To show a blob, we are currently creating a border (giving it a brush) and adding it to a grid. This grid is then added to a parent grid which forms the rows.
This is fine when the timeline shows relatively small numbers of blobs, but when the data is very busy, rendering gets slow, mem usage goes up, and the UI gets unresponsive. (Not surprisingly).
I know we're not doing this in the best way - my question is what is the best way to display graphics like this in wpf?
EDIT: Thank you for the replies below - I will investigate these options. However each requires a large amount of recoding, and my question here is more simple; Is there some control or effect I can use to replace Border which is a lower resource cost?
For data visualization like this I really like DynamicDataDisplay. The component is currently only being maintained/developed for Silverlight but there is an open source (in link) version for WPF which is really a great library.
The library uses IObservable
collections and supports DateTime
axis values.
There is a built-in marker graph type (example below).
You can use custom markers, it supports zooming and scrolling, lots of other nice features, etc. It is also very fast, even with quite large sets of data.
Since the datasets are IObservable
the chart component responds dynamically to changes in the underlying dataset so your graph is updated any time the data collection is updated.
EDIT
Here is an example :
uses :
using System.ComponentModel;
using Microsoft.Research.DynamicDataDisplay;
using Microsoft.Research.DynamicDataDisplay.Charts;
using Microsoft.Research.DynamicDataDisplay.DataSources;
using Microsoft.Research.DynamicDataDisplay.PointMarkers;
main :
public Window1()
{
InitializeComponent();
//
const int N = 100;
List<double> x = new List<double>();
List<double> y = new List<double>();
DateTimeAxis dtAxis = new DateTimeAxis();
_plotter.HorizontalAxis = dtAxis;
Random rand = new Random();
for (int i = 0; i < N; i++)
{ //generate some random data
x.Add(dtAxis.ConvertToDouble(DateTime.Now.AddDays(i)));
y.Add(rand.Next(N));
}
EnumerableDataSource<double> gX = new EnumerableDataSource<double>(x);
EnumerableDataSource<double> gY = new EnumerableDataSource<double>(y);
_MarkerGraph.DataSource = new CompositeDataSource(gX,gY);
//no scaling - identity mapping
gX.XMapping = xx => xx;
gY.YMapping = yy => yy;
CirclePointMarker mkr = new CirclePointMarker();
mkr.Fill = new SolidColorBrush(Colors.Red);
mkr.Pen = new Pen(new SolidColorBrush(Colors.Black),2.0);
_MarkerGraph.Marker = mkr;
}
XAML:
<Window x:Class="WpfApplication1.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d3="clr-namespace:Microsoft.Research.DynamicDataDisplay;assembly=DynamicDataDisplay"
Title="Window1" Height="481" Width="651">
<Grid>
<d3:ChartPlotter Name="_plotter">
<d3:MarkerPointsGraph Name="_MarkerGraph"/>
</d3:ChartPlotter>
</Grid>
Output :
The DateTime axis scales beautifully, adjusting ticks for days,months, hours, seconds...whatever is appropriate. Easy as pie!
If you do use this package, I highly recommend downloading the source and adding it as a second project to your solution. The documentation is very poor for D3 and the having the source in your project is helpful to figure out how things work. It also makes it very easy to add/extend things if you need to. Be sure to reference your in-solution project if you do this and not the compiled DLL.
Note also that a lot of the available documentation and online examples are targeted for the maintained Silverlight component and not for the open WPF component. Many of the components are different between these two versions so you have to dig through the WPF component to see what's there. The above example is for the v0.3 WPF component.
If you want to draw lots of constantly changing visuals, the fastest way WPF has to offer is to override OnRender
and draw into the DrawingContext
.
If the visuals are mostly static, but can change sometimes, the fastest way is to create a DrawingVisual
for each one, which can then be modified as necessary.
More details in this question, the linked article and the answers.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With