WPF Real Time Chart Performance using PathGeometry - wpf

I'm trying to implement a chart that can handle real time data, which comes in every 1 ms. I poll for 50ms of data so that I'm not trying to redraw the screen every single ms. I am using a PathGeometry on a Canvas to add line segments. I always see the framerate steadily tick downwards, as the redraws grow slower and slower. I did not think that a Line with roughly 10,000 points would be so difficult for my computer to render. Is there something I'm doing wrong? Or is there some other design philosophy that may be more adept at handling real time data in WPF?
In the ViewModel I have:
public PointCollection LinePoints;
In the View I listen to this collection being changed and add line segments:
_viewModel.LinePoints.Changed += LinePoints_Changed;
void LinePoints_Changed(object sender, System.EventArgs e)
{
while (_viewModel.LinePoints.Count - 1 > pathFigure.Segments.Count)
{
// pathFigure is part of the PathGeometry on my canvas
pathFigure.Segments.Add(new LineSegment(_viewModel.LinePoints[pathFigure.Segments.Count], true));
}
}
For simulation purposes I am injecting points using a BackgroundWorker:
void addPointsWorker_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker bw = sender as BackgroundWorker;
DateTime startTime = DateTime.Now;
int numPointsAdded = 0;
while (!bw.CancellationPending && (DateTime.Now - startTime).Seconds < 10)
{
List<Point> points = new List<Point>();
for (int i = 0; i < 50; i++)
{
Math.Sin(numPointsAdded++/(500*Math.PI))));
}
System.Threading.Thread.Sleep(50);
bw.ReportProgress(0, points);
}
}
public void addPointsWorker_ProgressChanged(List<Point> pointsToAdd)
{
foreach(Point point in pointsToAdd)
{
// triggers CollectionChanged which will add the line segments
ViewModel.LinePoints.Add(point);
}
}
Along with slowdown I also experience UI unresponsiveness. I figured it's because I'm making too many ReportProgress calls and filling up the message pump, but if I could resolve the slow rendering I think this other issue would go away as well. I am open to any suggestions!

I have two suggestions:
Make sure your collection only contains the points that will be rendered to the screen. An easy way to do this would be to use a queue and remove from the front as more points are added. I doubt that all 10,000 points need to be rendered at once.
If this is still not getting you the performance you need, you may want to use a lower-level drawing mechanism. StreamGeometry would be the first thing to try. After that, WriteableBitmap would provide the best performance possible in WPF.

After a bit of time tweaking things, I've found that Charlie's is the best solution. I also discovered that instead of using a LineSegment every time a point is added, using a PolyLineSegment with the 50 or so points of data I'm already queuing up does wonders for performance.
However, it doesn't delay the inevitable slowdown that occurs. But using a combination of PolyLineSegment with only the last several hundred or even thousand points has given me the best performance while showing the most amount of data possible.

Related

Update Image container when local image source file changes

I am buliding a GUI around an algorithm. For this purpose I have a Window component which contains a Image component inside it.
The content inside the Image component (imgHolder) is given by a BitmapImage object (_image), which for the purpose of this example is initialized as such:
_image = new BitmapImage();
_image.BeginInit();
_image.CacheOption = BitmapCacheOption.OnLoad;
_image.CreateOptions = BitmapCreateOptions.IgnoreImageCache;
_image.UriSource = new Uri("c:\\a1.bmp");
_image.EndInit();
imgHolder.Source = _image;
The main program containing the Window will perform a call to one of the algorithm's functions from within the Matlab DLL file. This will run one iteration of the algorithm, by the end of which it will write the results to the local bmp file from above ("c:\a1.bmp"). Consequently the underlying data for the image has been updated and I would like for this to be reflected in the imgHolder component. For this purpose I am simply duplicating the code above whenever I return from the function.
The problem with this approach is that the algorithm will have around 100 iterations per run so this would have to happen 100 times. It just doesn't seem right. I would be creating 100 different bitmap images all sourced from the same file.
Researching this I found out that one could use memorystream instead but I wonder whether this would be a drastical improvement or not as it seems that I would still have to create 100 images as the algorithm executes. Is it not possible to directly modify the underlying buffer somehow? I hear that GetPixel and SetPixel are not very efficient as they set a lock each time they are called. Is it possible perhaps to do this using the same _image object? If this was not a local file, I could've used a no cache option and it should've worked "by itself". Having a no cache option with the file being local did not work because of the lock.
Does anyone have a better idea?
Thanks!
I haven't quite understood if you do have access to the bitmap's raw data or not, or if it is always written to file. Since you mention SetPixel i guess you have.
So you could perhaps use a WritableBitmap as _image.Source and cyclically update its content by one of its WritePixels methods.
I just tried the following example which updates the image on every mouse move with a simple pattern.
XAML:
<Window ...>
<Grid>
<Image Name="image" />
</Grid>
</Window>
Code:
public partial class MainWindow : Window
{
private WriteableBitmap bitmap = new WriteableBitmap(100, 100, 96d, 96d, PixelFormats.Rgb24, null);
private byte[] buffer = new byte[30000];
public MainWindow()
{
InitializeComponent();
image.Source = bitmap;
}
private void OnMouseMove(object sender, MouseEventArgs e)
{
Point pos = e.GetPosition(image);
int offset = (int)pos.X + (int)pos.Y;
for (int i = 0; i < buffer.Length; i++)
{
buffer[i] = (byte)(i + offset);
}
bitmap.WritePixels(new Int32Rect(0, 0, 100, 100), buffer, 300, 0);
}
}

Start and Back Button pressed in rapid succession WP7

I asked this question in a similar post but there have been significant updates since then, but still no results so I will try to re-ask the question with the updated information.
Basically I have a pivot view with 4 pivot items. If I create the scenario where I hit the windows key then rapidly press the back key my application will reopen without reconstructing (this is the expected outcome). The functionality of the application is there. I can press application bar buttons etc.
What doesn't work is the pivot items are frozen. If I was on Pivot item A and I press the start and back button quickly I come back to Pivot Item A. If I try to switch Pivot Items, the screen does not update, its "frozen" on Pivot Item A BUT the functionality of Pivot Item B is there. (I know this because the application bar Icons for Pivot Item B are now showing).
I have read many articles on proper tombstoning scenarios and how to approach this problem. My data IS being tombstoned correctly, and upon reactivation the tombstoned data works. No objects are null so I don't have any exceptions being thrown at me.
I check to see if I need to reload the Main ViewModel (I don't need to in this case so the UI elements being created initially are not being re created).
What does fix the problem however is if the application is reconstructed. Lets say I go to the marketplace from my app, let it finish loading and press back, My application will be refreshed and working fine since it properly deactivated and reconstructed istelf. I don't rely on constructors doing all the work so I am not missing any key elements not being set when they aren't fired in the windows/back button scenario.
Does anyone have any idea why my screen would not be updating?
constructor/loaded event/on navigated to event
public MainPage()
{
InitializeComponent();
this.Loaded += new RoutedEventHandler(MainPage_Loaded);
}
private void MainPage_Loaded(object sender, RoutedEventArgs e)
{
if (App.firstTimeLoading == true)
{
App.firstTimeLoading = false;
}
BuildApplicationBar();
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
this.DataContext = App.ViewModel;
App.viewIdentifier = StringResource.MainPageView;
if (!App.ViewModel.IsDataLoaded)
{
App.ViewModel.LoadData();
String bookTitle;
App.Parser.appBookInfoDict.TryGetValue(CPlayerInventoryKeys.kInventoryKeyTitleShortTitle, out bookTitle);
PivotBackground.Title = bookTitle.ToUpper();
CreatePivotItems();
}
if (App.playerController.chapterPlayer.Source == null)
App.restoreStateClass.RestoreState();
//applies the proper background image
if (App.isDarkTheme)
{
BitmapImage bitmapImage = new BitmapImage(new Uri(StringResource.PanoramaBlackImage, UriKind.Relative));
BackgroundImage.ImageSource = bitmapImage;
BackgroundImage.Opacity = .85;
}
else
{
BitmapImage bitmapImage = new BitmapImage(new Uri(StringResource.PanoramaWhiteImage, UriKind.Relative));
BackgroundImage.ImageSource = bitmapImage;
BackgroundImage.Opacity = .5;
}
if (App.firstTimeLoading == false && PivotBackground.SelectedItem != SuggestedPivotItem)
BuildApplicationBar();
else if (PivotBackground.SelectedItem == SuggestedPivotItem)
{
BuildMarketPlaceApplicationBar();
}
base.OnNavigatedTo(e);
}
I found the answer. Since I had a media element open (play/paused) and I was implementing the "non tombstoned" method of hitting windows key and back button very quickly, the media element source was corrupt. Even though I reset this source, apparently it can be ignored and not function properly. All I had to do was add a line of code to the Application Deactivated handler.
private void Application_Deactivated(object sender, DeactivatedEventArgs e)
{
App.MainAudioPlayer.Source = null; //(only showing line added)
}
The behavior you are describing seems to be solely related to the way you are manipulating data internally and constructing your layout. I tested this both in the emulator and on a couple of physical devices, both producing normal output (even when bound to a view model).
Try creating a new Pivot-based application (without all your data - just using the default template) and see if the problem persists. Also worth mentioning - are you testing on a device or in the emulator?
Are you using transitions from the toolkit?
Are they defined in XAML?
If so that could be the issue. There's a bug which is fixed in the next version.
The solution for now is to remove the transitions or define them in code.

Binding to time-dependent properties

Some time ago i wrote a small widget-like application which was supposed to keep track of tasks, each task had a deadline specified as a DateTime, now if you want to display how much time is left until the deadline you might want to bind to a "virtual" (*curses the virtual keyword*) property like this:
public TimeSpan TimeLeft
{
get { return Deadline - DateTime.Now; }
}
Obviously in theory this property changes every tick and you want to update your UI every now and then (e.g. by periodically pumping out a PropertyChanged event for that property).
Back when i wrote the widget i refreshed the whole task list every minute, but this is hardly ideal since if the user interacts with some item (e.g. by typing in a TextBox which binds to a Comments-property) that will be harshly interupted and updates to the source get lost.
So what might be the best approach to updating the UI if you have time-dependent properties like this?
(I don't use that application anymore by the way, just thought this was a very interesting question)
A timer is the only way I can think of. Since this is an interesting question, I'll put my .02 in. I would encapsulate it doing something like this:
public class CountdownViewModel : INotifyPropertyChanged
{
Func<TimeSpan> calc;
DispatcherTimer timer;
public CountdownViewModel(DateTime deadline)
: this(() => deadline - DateTime.Now)
{
}
public CountdownViewModel(Func<TimeSpan> calculator)
{
calc = calculator;
timer = new DispatcherTimer();
timer.Interval = TimeSpan.FromSeconds(1);
timer.Tick += timer_Tick;
timer.Start();
}
void timer_Tick(object sender, EventArgs e)
{
var temp = PropertyChanged;
if (temp != null)
{
temp(this, new PropertyChangedEventArgs("CurrentValue"));
}
}
public TimeSpan CurrentValue
{
get
{
var result = calc();
if (result < TimeSpan.Zero)
{
return TimeSpan.Zero;
}
return result;
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
public class MyViewModel
{
public CountdownViewModel DeadlineCountdown { get; private set; }
public DateTime Deadline { get; set; }
public MyViewModel()
{
Deadline = DateTime.Now.AddSeconds(200);
DeadlineCountdown = new CountdownViewModel(Deadline);
}
}
Then you could bind to DeadlineCountdown.CurrentValue directly, or create a CountdownView. You could move the timer to the CountdownView, if you wanted. You could use a static timer so they all update at the same time.
Edit
If Deadline is going to change, you would have to construct the countdown like this:
DeadlineCountdown = new CountdownViewModel(() => this.Deadline - DateTime.Now);
I think what you said in your first paragraph after the code sample is the only reasonable way to make this work in WPF. Set up a timer that Just calls PropertyChanged for the TimeLeft property. The interval would vary based upon your scenario (if you're talking a weekly task list, you probably only need to update it ever 5 minutes or so. If you're talking a task list for the next 30 minutes, you may need to update it every minute or 30 seconds or something.
That method would avoid the problems you mentioned with the refresh option since only the TimeLeft bindings would be affected. If you had millions of these tasks, I guess the performance penalty would be pretty significant. But if you only had a few dozen or something, updating those bindings every 30 seconds or so would be be a pretty insignificant issue, right?
Every possibility that I can think of uses either Timers or Animations. The animations would be way too "heavy" as you add tasks to the list. And of the Timer scenarios, the one above seems to be the cleanest, simplest and most practical. Probably just comes down to whether it even works or not for your specific scenario.
I read your accepted answer, but I was just wondering... why not just disable the bindings for that specific task while in 'Edit' mode so you wouldn't be interrupted? Then simply re-enable that binding when you're either done, or you cancel your edit? That way even if your timer updated every second, who cares?
As for how to disable them without detaching them (and thus resetting their value), simply define a boolean flag, then in all the DPs that you want to interrupt, check for that flag in the validation logic. If the flag is true and the DependencyObject that it applies to is the one you're editing, block the change to the DP.
Anyway, this just popped into my head. Haven't actually tested it but it should be an easy thing to try.

Why is Frame Rate in WPF Irregular and Not Limited To Monitor Refresh?

I'm measuring the time between frames in a simple WPF animation. Perforator says the app performs at ~60fps, so I expected the time between frames to be ~16.6ms with little deviation.
public MainWindow()
{
...
CompositionTarget.Rendering += Rendering;
}
List<long> FrameDurations = new List<long>();
private long PreviousFrameTime = 0;
private void Rendering(object o, EventArgs args)
{
FrameDurations.Add(DateTime.Now.Ticks - PreviousFrameTime);
PreviousFrameTime = DateTime.Now.Ticks;
}
Two things surprised me:
Time between frames is fairly irregular
Time between frames is ~8ms. I had expected that the monitor's refresh rate would set a lower bound on time between frames (ie. 60Hz = 16.6ms between each frame, and anything faster is pointless).
Y - Time between frames in ticks (10,000 ticks = 1ms)
X - Frame count
Possible confounding factors
Timer inaccuracy
If CompositionTarget.Rendering doesn't actually correlate to the drawing of a single frame
The project I'm using: SimpleWindow.zip
===Edit
Markus pointed out I could be using RenderingEventArgs.RenderingTime.Ticks instead of DateTime.Now.Ticks. I repeated the run and got very different results. The only difference is timing method:
DateTime.Now.Ticks
RenderingEventArgs.RenderingTime.Ticks
Data from RenderingEventArgs produced data much closer the expected 16.6ms/frame, and it is consistent.
I'm not sure why DateTime.Now and RenderingEventArgs would produce such very different data.
Assuming RenderingEventArgs is producing correct times, it's still a bit disconcerting that those times are not the expected 16.6ms.
If the display is updating every 16.6ms and WPF is updating every 14.9ms, we can expect a race condition that would result in tearing. That is to say, roughly every 10th frame WPF will be trying to write its image while the display is trying to read the image.
I raised this question with the WPF team and here is a summary of the response I was given:
Calculating the framerate from the UI
thread is difficult. WPF decouples
the UI thread from the render thread.
The UI thread will render:
Whenever something is marked as dirty and we drain down to Render
priority. This can happen more often
than the refresh rate.
If an animation is pending (or if someone hooked the
CompositionTarget.Rendering event) we
will render on the UI thread after
every present from the render thread.
This involves advancing the timing
tree so animations calculate their new
values.
Because of this, the
CompositionTarget.Rendering event can
be raised multiple times per “frame”.
We report the intended “frame time” in
the RenderingEventArgs, and
applications should only do
“per-frame” work when the reported
frame time changes.
Note that the UI thread is doing many
things, so it is not reliable to
assume the CompositionTarget.Rendering
event handler runs at a reliable
cadence. The model we use (decoupling
the two threads) means that the UI
thread can be a little behind, since
it is calculating animations for a
future frame time.
Special thanks to Dwayne Need for explaining this to me.
First - 'Christopher Bennage's-Answer has a good explanation and provides a hint to a solution:
"Only do “per-frame” work when the reported frame time changes"
This is a little bit hard, since the RenderingEventArgs are hidden as a normal EventArgs and a cast has to be done.
To make this a little bit easyer, a convinient solution can be found in "EVAN'S CODE CLUNKERS"
http://evanl.wordpress.com/2009/12/06/efficient-optimal-per-frame-eventing-in-wpf/
I took his code and modified it a bit. Now just take my snipped, add the class to your Project, use CompositionTargetEx were you used CompositionTarget and you are fine :)
public static class CompositionTargetEx {
private static TimeSpan _last = TimeSpan.Zero;
private static event EventHandler<RenderingEventArgs> _FrameUpdating;
public static event EventHandler<RenderingEventArgs> Rendering {
add {
if (_FrameUpdating == null)
CompositionTarget.Rendering += CompositionTarget_Rendering;
_FrameUpdating += value;
}
remove {
_FrameUpdating -= value;
if (_FrameUpdating == null)
CompositionTarget.Rendering -= CompositionTarget_Rendering;
}
}
static void CompositionTarget_Rendering(object sender, EventArgs e) {
RenderingEventArgs args = (RenderingEventArgs)e;
if (args.RenderingTime == _last)
return;
_last = args.RenderingTime; _FrameUpdating(sender, args);
}
}
WPF is not designed to be a constant frame rate rendering system. WPF renders to screen when the elements in the screen are marked as changed. The rendering system runs as a message loop so there is no way to ensure that a frame will be rendered at specific intervals.

How to remove items from list x seconds after adding them

I need to remove items from list few seconds after i added them. I have now an ObservableCollection to which I add some messages. I need them to be deleted let's say 5 seconds after they were added. I tried to create a function responsible for adding the items and setting a timer:
public void AddInfoItem(string info)
{
infoList.Add(info);
Timer newTimer = new Timer(5000);
newTimer.Elapsed += new ElapsedEventHandler(this.TimerFunction);
newTimer.Enabled = true;
newTimer.Start();
}
public void TimerFunction(Object sender, EventArgs e)
{
infoList.Clear();
}
I didn't even send any parameters which item should be removed cause the second function raised an exception. Can somebody describe a proper solution to add item and delete it after some time?
Sory for not writing it earlier. The exception is
this type of collectionview does not support changes to its sourcecollection from a thread different from the dispatecher thread
Rather removing items from the list on a timer, why not store an expiration time with each item when it is added, and ignore or remove expired items only when you need to retrieve an item or iterate the list?
If working in WPF use DispatcherTimer. I usually use something like this:
public static void Delay(int milliseconds, Action action)
{
var t = new DispatcherTimer() { Interval = TimeSpan.FromMilliseconds(milliseconds) };
t.Tick += (o, e) => { t.Stop(); action.Invoke(); };
t.Start();
}
DispatcherTimer will make sure the event is invoked on the same thread so you don't run into threading issues. Another alternative is to make the collection you're binding to thread safe. But indeed, knowing what kind of exception you received instead of guessing would be easier.
Also, if you add and remove lots of items in quick succession or require your timing to be precise you'd need to consider something else; DispatcherTimer is not very precise and does carry some overhead, so a lot of instances of it will consume some resources.
Sounds like a job for Reactive Extensions. Here's a link to 101 Rx Samples to show you how to get going.
Basically what you'll want to do (partly pseudo code)
Observable
.FromEvent<NotifyCollectionChangedEventArgs>(infoList, "CollectionChanged")
.Where(args => args.Action == NotifyCollectionChangedAction.Add)
.Delay(TimeSpan.FromSeconds(5))
.ObserveOnDispatcher()
.Subscribe(args => infoList.Remove(args.NewItems));
No need to deal with the timers and no leaks as long as you dispose of the IDisposable returned by the Subscribe method when you're done with it.
Edit:
Shameless self plug - I did a blog post with a working console app example. The one difference you'll have is you'll want to keep the ObserverOnDispatcher() call in your WPF app or you'll get some threading errors.
Piling onto Bryan's answer above, here's how you do it via ReactiveXaml's ReactiveCollection, an extension of ObservableCollection:
theCollection.ItemsAdded
.Delay(TimeSpan.FromSeconds(5))
.Subscribe(theCollection.Remove);
make sure that your garbage collector didn't dispose of your infoList. Try GC.KeepAlive(infoList)

Resources