7

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?

Sugrue
  • 3,629
  • 5
  • 35
  • 53
  • You shouldn't be creating elements programatically and adding them to the grid like that. You should create a listItem datatemplate for your blobs and then bind a collection of blobs to a listView. – Alain Apr 16 '12 at 15:53
  • It would be very hard to get smooth scrolling that way, no? I'd have to either add all the data to the collection (which would crash) or dynamically repopulate the collection, which would probably not look smooth. – Sugrue Apr 16 '12 at 15:56

3 Answers3

4

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 : Program runs...

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.

J...
  • 30,968
  • 6
  • 66
  • 143
  • Thanks for that. Interesting, and I might use it some day. I'd prefer not to have to radically recode our current system though. It is 99% working, I'd just like to make the actually rendering more efficient. – Sugrue Apr 16 '12 at 16:02
  • @Sugrue I think that to make the rendering more efficient you will probably need to change your approach anyway. Drawing all of your elements to a single canvas will probably be necessary to get the desired performance and that is a shift away from the grid-in-grid approach you are using. As long as your data is already sorted out it is actually very quick to just drop a chart component in and link it together. – J... Apr 16 '12 at 16:09
  • @Sugrue : added an example to show how easy this is! Really, this component is just a very efficient data rendering engine, which is exactly the problem you are trying to solve. Aside from a few lines to convert the data it should be a mostly drop-in solution. – J... Apr 16 '12 at 16:54
  • Thanks for this. It looks interesting. It would take some work to restyle it to look like our current UI. – Sugrue Apr 17 '12 at 09:33
  • @Sugrue Yes, naturally. If you're definitely going down the roll-your-own route then romkyns has the right idea with drawing into the DrawingContext. – J... Apr 17 '12 at 10:29
3

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.

Community
  • 1
  • 1
Roman Starkov
  • 59,298
  • 38
  • 251
  • 324
0

in my opinion, drawingcontext or drawingvisual a bit slowly and complex

so wpf's bitmap image very fast, you can try, you can see more fast than drawingcontext or drawingvisual

implementation code;

public BitmapImage yuklenenResim;
public OpenFileDialog selectImage;

yuklenenResim = new BitmapImage();
yuklenenResim.BeginInit();
yuklenenResim.UriSource = new Uri(selectImage.FileName);
yuklenenResim.CacheOption = BitmapCacheOption.OnDemand;
yuklenenResim.EndInit();

/// imgSource is Image on wpf xaml side
imgSource.Source = yuklenenResim;
eakcn94
  • 21
  • 2