Click here to Skip to main content
15,885,546 members
Please Sign up or sign in to vote.
4.00/5 (1 vote)
See more:
The Problem
I am creating a C# WPF application to create a simple line chart that plots points as the data arrives from a stream reader. It will update about once a second. I cannot get the graph to display until all of the data has been processed. In XAML I create a window, grid and then a canvas.
My code uses an Observable collection to collect the data points. Each time I add a point the collection changed event is fired and I create a polyline from the collection. I clear the canvas children property and add the polyline.
void Obdatapoints_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
GraphCanvas.Children.Clear();
pl = CreateNewPolyLine(Obdatapoints);
GraphCanvas.Children.Add(pl);

}

I thought adding the children would cause the screen to redraw. It does not. It does draw only after all of the points are received.
How can I get the screen to redraw on each collection changed?

It has been suggested to me that I use the native WPF chart control to do this task. I have not found any documentation that shows how to use the control for a chart that is continuously updated. I will have about 1260 updates once each second or so. Will the WPF chart control support continuous updating and display the chart after each update.
Posted
Comments
Pete O'Hanlon 23-Jun-11 12:34pm    
Without seeing the full implementation, it's impossible to say why it's not being redrawn.
mmckillop 23-Jun-11 13:35pm    
I would be pleased to provide all code and data. What is the best way to do so on this forum
Pete O'Hanlon 23-Jun-11 13:44pm    
If you could provide a DropBox solution, I'll d/l it and have a look (and I'll also run your data through the WPF charting control).
mmckillop 23-Jun-11 13:57pm    
Pete
Please excuse my lack of info but what is a DropBox? I will be happy to provide all as soon as I know how
Thanks for your help
Mike
mmckillop 23-Jun-11 14:09pm    
I now have a Dropbox. Slick. What email address do you want me to add to allow you to share the files?

1 solution

Right - I've had a look at your code. The issue you're facing here is that you are doing everything on the UI thread - and you shouldn't do that. What I did was create a BackgroundWorker in which I read the data in and updated the ObservableCollection. I then marshalled the data back onto the UI thread in the collection changed event.

Here's your code changed as I mentioned:
C#
using System;
using System.Windows;
using System.Windows.Media;
using System.Windows.Shapes;
using System.IO;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows.Threading;
using System.Collections.Specialized;
///// this is the real realtime graph 
namespace RealTimeGraph
{
 /// <summary>
 /// Interaction logic for MainWindow.xaml
 /// </summary>
 public partial class MainWindow : Window
 {
  public string FullNamePath = @"C:\KarlSample\RealTimeGraph\michael-forks20101201-200256";
  //public string FullNamePath = @"C:\SessionDataTest\michael-forks20101201-200256A";
  private double xmin = 0;
  private double xmax = 1600;
  private double ymin = 850;
  private double ymax = 950;
  private BackgroundWorker _bw = new BackgroundWorker();
  Polyline pl;
  ObservableCollection<Point> Obdatapoints = new ObservableCollection<Point>();
  public MainWindow()
  {
   InitializeComponent();
   _bw = new BackgroundWorker();
   _bw.DoWork += new DoWorkEventHandler(_bw_DoWork);
  }

  void _bw_DoWork(object sender, DoWorkEventArgs e)
  {
   int count = 0;
   string wrkString = null;
   double[] values = new double[4];
   StreamReader sr = new StreamReader(FullNamePath);
   while ((wrkString = sr.ReadLine()) != null)
   {
    System.Threading.Thread.Sleep(500);

    if (wrkString.StartsWith("\""))
    {
     wrkString = sr.ReadLine();
    }
    else
    {
     values = ParseTextStream(wrkString);
     Point pt = new Point(count, values[0]);
     Obdatapoints.Add(pt);
     count++;
    }
   }
  }

  void Obdatapoints_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
  {
   //GraphCanvas.Children.Clear();
   this.Dispatcher.Invoke( DispatcherPriority.DataBind, new Action(()=>{
    pl = CreateNewPolyLine(Obdatapoints);//(NormalizePoint(pt)) in the CreatePolyLineMethod
    GraphCanvas.Children.Add(pl);
   })); 
  }

  public double[] ParseTextStream(string wrkString)
  {
   // MessageBox.Show(wrkString);
   string[] stringSeparators = new string[] { "," };
   string[] sresult = new string[10];
   double[] result = new double[4];
   sresult = wrkString.Split(stringSeparators, StringSplitOptions.None);
   result[0] = Double.Parse(sresult[1]); //EDM
   result[1] = Double.Parse(sresult[2]);  //HR
   result[2] = Double.Parse(sresult[7]);  //BVP
   result[3] = Double.Parse(sresult[8]); //HRV
   return result;
  }

  private Point NormalizePoint(Point pt)
  {
   Point result = new Point();
   result.X = ((pt.X - xmin) * GraphCanvas.Width / (xmax - xmin));
   result.Y = GraphCanvas.Height - (pt.Y - ymin) * GraphCanvas.Height / (ymax - ymin);
   return result;
  }

  public void chartGrid_SizeChanged(Object sender, SizeChangedEventArgs e)
  {
   if (pl == null) return;
   GraphCanvas.Width = graphGrid.ActualWidth;
   GraphCanvas.Height = graphGrid.ActualHeight;
   GraphCanvas.Children.Clear();
   GraphCanvas.Children.Add(pl);
   // AddChart();
  }

  public Polyline CreateNewPolyLine(ObservableCollection<Point> obdpts)
  {
   Polyline pltemp = new Polyline();
   pltemp.Stroke = Brushes.Red;
   pltemp.StrokeThickness = .5;
   foreach (Point pt in obdpts)
   {
    pltemp.Points.Add(NormalizePoint(pt));  //(NormalizePoint(pt))
   }
   return pltemp;

  }

  private void Window_Loaded(object sender, RoutedEventArgs e)
  {
   Obdatapoints.CollectionChanged += new NotifyCollectionChangedEventHandler(Obdatapoints_CollectionChanged);
   _bw.RunWorkerAsync();
  }
 }
}
 
Share this answer
 
Comments
mmckillop 23-Jun-11 18:01pm    
I copied the code in and updated the file path to my folder, It compiles with no errors but only produces a blank screen. Something is not quite right.
Pete O'Hanlon 23-Jun-11 18:32pm    
I uploaded the files back to DropBox - you should be able to get them from there. They are running perfectly well on my machine.
Pete O'Hanlon 24-Jun-11 2:13am    
If you try to get the files now. I've reuploaded them into the folder you put them in.
mmckillop 24-Jun-11 9:32am    
Thank you for your considerable efforts I tried to execute them both from the DropBox and after downloading them but I still get a blank screen. I did uncomment the original path statement to point to the text file on my system. I noticed that the XAML had been changed a bit to add info for Blend. I do not have Blend could that be the problem? I put a new folder named PetesNewFolder would you be kind enough to upload one more time. The original folder was layered with two sets of downloads.
Thanks again for your many efforts
Pete O'Hanlon 24-Jun-11 9:59am    
I am travelling right now so I won't get to the site until later on tonight. When you are watching, are you looking at the top left? Increase the thread timeout as well for your testing.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900