Is there any way in WPF to determine the optimal UI update rate, so that
No frames are skipped and
UI stays responsible.
It does not make sense to to a lot work to notify the UI that some values have changed when the render process is not able to keep pace. Indeed, I notice that when I try to update too often, the UI gets unresponsive and only a small fraction of the frames are displayed (e.g. InvalidateVisual() is called 10 times, but only one new frame is displayed). Instead, I would prefer that the UI is updated at the native rendering rate.
Up to now, I already tried with a DispatcherTimer, but even if I reduce the Priority to Background, the update method is called more often than the Rendering occurs (frames are skipped) if the update rate is too high. But this rate obviously depends on the Hardware and the number of elements on the screen. If I further reduce the Priority, the timer is not called at all (perhaps as there is a background thread running to update the values).
Alex
I just wrote a quick test to see what the frame rate I'm getting in a test .Net WPF app.
I'm seeing a rate of between 60-63 frames per second, the monitor refresh rate is 60 Hz, so I guess I'm seeing the UI updating at the native rate.
I wrote this using the CompositionTarget.Rendering event and implemented it as a Behavior and attached it to a TextBlock in a very simple MVVM style app:
XAML:
<TextBlock>
<i:Interaction.Behaviors>
<views:FrameRateBehavior />
</i:Interaction.Behaviors>
</TextBlock>
C# Code:
public sealed class FrameRateBehavior : Behavior<TextBlock>
{
private readonly Queue<long> _ticks = new Queue<long>();
public FrameRateBehavior()
{
_ticks = new Queue<long>();
}
protected override void OnAttached()
{
base.OnAttached();
CompositionTarget.Rendering += CalculateFrameRate;
}
protected override void OnDetaching()
{
base.OnDetaching();
CompositionTarget.Rendering -= CalculateFrameRate;
_ticks.Clear();
}
private void CalculateFrameRate(object sender, EventArgs e)
{
var now = DateTime.Now;
var endTime = now.Ticks;
var startTime = now.AddSeconds(-1).Ticks;
while (_ticks.Any())
{
if (_ticks.Peek() < startTime)
{
_ticks.Dequeue();
continue;
}
break;
}
_ticks.Enqueue(endTime);
var count = _ticks.Count;
AssociatedObject.Text = "FPS: " + count;
}
}
Related
I'm playing around with DataVirtualization and Async. What are some options I have for quantifying load times of a ListBox that I'm binding my virtualized data collections to?
I need a way to compare the virtualized vs non-virtualized data loading. Have been unsuccessful in locating any resources for this topic.
Should I just put a stopwatch on the ListBox_Loaded event in the code behind?
Thanks in advance!
You can use a System.Diagnostics.Stopwatch for this. Make sure that you start it before you set the ListBox.ItemsSource property and stop it as you said, in the ListBox.Loaded event:
In XAML:
<ListBox Name="ListBox" />
In code constructor:
public MainWindow()
{
InitializeComponent();
ListBox.Loaded += new RoutedEventHandler(ListBox_Loaded);
Items.AddRange(Enumerable.Range(1, 100000000));
stopwatch = new Stopwatch();
stopwatch.Start();
ListBox.ItemsSource = Items;
}
Add the handler with a break point after the call to stop the Stopwatch:
private void ListBox_Loaded(object sender, RoutedEventArgs e)
{
stopwatch.Stop();
TimeSpan elapsedTime = stopwatch.Elapsed;
}
However, unless you have millions of rows of data, or extremely complicated DataTemplates, you may not see much differences. In this simple example, these 100,000,000 numbers are processed in well under one second. Even when I added a larger DataTemplate for the integers, it still rendered them all in just over one second. Furthermore, repeatedly running this scenario will return differing results, so this is somewhat unreliable as well.
I'd like to know is there any other option to show current time without using DispatcherTimer?
Unfortunately app need's to run in slow PC's and DispatcherTimer continuously increasing memory usage. I check that in WPF Performance Profiling Tool and that's true - CPU usage with 1sec timespan is ~5-15%.
And becouse im using class to get time value and show that on label, it would be great if method to show could run without xaml-generated controls.
Thanks!
#EDIT:
DispatcherTimer dispatcherTimer = new DispatcherTimer();
dispatcherTimer.Tick += new EventHandler(dispatcherTimer_Tick);
dispatcherTimer.Interval = new TimeSpan(0, 0, 0);
dispatcherTimer.Start();
and when tick:
private void dispatcherTimer_Tick(object sender, EventArgs e)
{
lbl.Content = DateTime.Now.ToString("F");
// lbl is static label which i create once and show i several windows.
}
Your interval is zero, meaning...ouch. Why is it necessary to update the UI more than 10 times a second? No human being will be able to process the changes if they are that frequent. By default WPF only updates the screen 60 times a second, so your interval should be at least 1000/60 = 16.6 ms. However, I'd argue that something as high as 250ms could be good enough.
I have an OpenTK GLControl embedden in a WindowsFormsHost in my WPF application.
I want to continuously update and render it.
In Winforms a solution would be to attach the UpdateAndRender method to the Application.Idle event, but there is no such thing in WPF.
So what would be the best way to do (60FPS) updating of my scene and GLControl ?
You can use a System.Timers.Timer to control how often your render code is called. In your window containing the GLControl-in-WindowsFormsHost, declare a private System.Timers.Timer _timer;, then when you're ready to start the rendering loop, set the timer interval and it's event handler, then start it up, as in the following example:
private void btnLoadModel_Click(object sender, RoutedEventArgs e)
{
LoadModel(); // do whatever you need to do to prepare your scene for rendering
_timer = new System.Timers.Timer(10.0); // in milliseconds - you might have to play with this value to throttle your framerate depending on how involved your update and render code is
_timer.Elapsed += TimerElapsed;
_timer.Start();
}
private void TimerElapsed(object sender, ElapsedEventArgs e)
{
UpdateModel(); // this is where you'd do whatever you need to do to update your model per frame
// Invalidate will cause the Paint event on your GLControl to fire
_glControl.Invalidate(); // _glControl is obviously a private reference to the GLControl
}
You'll clearly need to add using System.Timers to your usings.
You can use Invalidate() for it. This causes the GLControl to redraw it's content.
If you call it at the end of Paint() you may blocking some UI rendering of the other WPF controls.
WPF provides a per frame render event: CompositionTarget.Rendering. This event is called before WPF wants to render the content. Subscribe from it and call Invalidate:
public YourConstructor()
{
//...
CompositionTarget.Rendering += CompositionTarget_Rendering;
}
void CompositionTarget_Rendering(object sender, EventArgs e)
{
_yourControl.Invalidate();
}
You need to unsubscribe if you don't use it anymore (to avoid memory leaks).
Here is a How to: Render on a Per Frame Interval Using CompositionTarget from MSDN.
I use GLControl with that method and it works fine. I did not checked how much FPS I have but it feels smooth.
You may also have a look on this: Why is Frame Rate in WPF Irregular and Not Limited To Monitor Refresh?
I know that there are several implementations here and there, but i was still not able to 'lock' on something really useful...
Whenever i set some component DataContext or ItemsSource to some big object, there is this 'render time frozen GUI' which make the app real annoying (even when using Virtualization).
I know i can iterate the object and set the items one by one and show progress, but i am looking for some other approach which can let me show some moving indication while GUI is rendering. I also prefer to have some progress bar and not only make the mouse cursor change.
Is there a decent way to achieve the followings?
Many Thanks
Zamboni example is a very good one, but still does not solve the frozen GUI problem.
As mentioned, there is no currently simple way of having something 'alive' to update a gui control while GUI is busy rendering.
I currently found some event that is 'alive and kicking' while gui is rendering, althogh it should be turned off when not needed as it can fire something like 60 times per second.
CompositionTarget.Rendering += ReportRenderProgress;
You can then implement ReportRenderProgress() anyway you like to signal you progress bar to update. Currently, i dont see any better solution available in WPF to update a progress indication while rendering so i am marking this as the answer.
This is actually a problem. You are using the GUI thread to fill the data (from object structure into GUI). The GUI thread is required both to read Windows message queue (prevent app from freezing, allow app to be moved/respond) and it is required to do any updates to the GUI.
One solution could be to slowly fill the the object structure after binding. This would have to be done from the GUI thread, so you could add DoEvents() and/or some percent indicator+forced refresh to make application seem alive.
I am interested to hear if anyone has a better solution though.
BackgroundWorker has everything you need.
EDIT
In WPF the Dispatcher is being employed automatically to invoke cross-thread method calls.
Check out Build More Responsive Apps With The Dispatcher in MSDN magazine.
I also put together some code fragments from a ViewModel that shows a BackgroundWorker updating a progress bar.
<ProgressBar
VerticalContentAlignment="Stretch" VerticalAlignment="Stretch"
HorizontalAlignment="Stretch"
Minimum="0" Maximum="100"
Value="{Binding Path=BarPosition, Mode=TwoWay}"/>
// configure the background worker...
_backgroundWorker = new BackgroundWorker();
_backgroundWorker.WorkerReportsProgress = true;
_backgroundWorker.WorkerSupportsCancellation = true;
_backgroundWorker.DoWork += new DoWorkEventHandler(_backgroundWorker_DoWork);
_backgroundWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(_backgroundWorker_RunWorkerCompleted);
_backgroundWorker.ProgressChanged += new ProgressChangedEventHandler(_backgroundWorker_ProgressChanged);
// control progress bar position
private int _barPosition = 0;
public int BarPosition
{
get { return _barPosition; }
set
{
_barPosition = value;
OnPropertyChanged("BarPosition");
}
}
// long operation
void _backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker bw = sender as BackgroundWorker;
if (bw != null)
{
int pos;
for (int i = 0; i < 100; ++i
{
// report progress here for our long running operation..
pos = i/100;
bw.ReportProgress(pos);
Thread.Sleep(1000); // fake long operation
}
}
}
// report progress,,,
void _backgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
BackgroundWorker bw = sender as BackgroundWorker;
if (bw != null)
{
BarPosition = e.ProgressPercentage;
}
}
// reset scroll bar position
void _backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
BackgroundWorker bw = sender as BackgroundWorker;
if (bw != null)
{
BarPosition = 0;
// Forcing the CommandManager to raise the RequerySuggested event to refresh UI...
CommandManager.InvalidateRequerySuggested();
}
}
I am working on creating a WPF solution which uses MVVM pattern to load searched items in a search control asynchronously. The search control which is a WPF usercontrol is created with a textbox to enter search text and search button and a hidden listbox which would be visible when it loads the searched items list in it. This user control is in turn embedded into another WPF view which has a treeview of certain items. This view has a view model in which the logic to load the searched items of the tree view would be loaded in the search control. All the while, this has been happening synchronously without the use of any Dispatcher call. But, after a change request, I would like to make this happen asynchronously in a different thread using Dispatcher.
Could anyone please let me know how to get handle of the Dispatcher of the Search control in the view model class so as to call BeginInvoke on it using MVVM pattern wherein my View model is not aware of the view? Any clue would be highly appreciated.
public ObservableCollection<Details> CatalogSearchResults { get; private set; }
private void ExecuteSearchCommand(object parameter)
{
CatalogSearchResults.Clear();
if (string.IsNullOrEmpty(parameter.ToString())) return;
searchtext = (string)parameter;
searchtext.Trim();
SetSearchResults();
}
private void SetSearchResults()
{
BackgroundWorker bw = new BackgroundWorker();
bw.DoWork += LoadResults;
bw.RunWorkerCompleted += this.LoadResultsCompleted;
bw.RunWorkerAsync();
}
private void LoadResults(object sender, DoWorkEventArgs args)
{
IsSearchInProgress = true;
foreach (var category in _rootCategory.Recurse(FindChildren))
{
if (category.CommentDetails != null)
{
//limitation - there is no direct way to add range to observable collection.
//Using linq query would result in two loops rather than one.
foreach (var node in category.Details)
{
if (node.Name.IndexOf(searchtext, StringComparison.CurrentCultureIgnoreCase) >= 0
|| node.PrecannedText.IndexOf(searchtext, StringComparison.CurrentCultureIgnoreCase) >= 0)
{
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal,
(ThreadStart)delegate { CatalogSearchResults.Add(node); });
Thread.Sleep(100);
}
}
}
}
IsSearchInProgress = false;
}
In the xaml, I am biding the Items property of the Search control to the CatalogSearchResults:
<ctrl:SearchControl x:Name="Ctrl" Grid.RowSpan="2" HorizontalAlignment="Stretch" VerticalAlignment="Top" ToolTip="Search" Command="{Binding SearchCommand}" Grid.ColumnSpan="3"
CommandParameter="{Binding Text, RelativeSource={RelativeSource Self}}"
Items ="{Binding CatalogSearchResults}" > </ctrl:SearchControl>
Thanks,
Sowmya
Here's a simple implementation showing how to use BackgroundWorker to update objects on the UI thread while DoWork is running - in this example, there's a ListBox in the UI that's bound to FilteredItems, and ItemsSource is a property of the UserControl of type IEnumerable:
FilteredItems = new ObservableCollection<object>();
BackgroundWorker bw = new BackgroundWorker();
bw.WorkerReportsProgress = true;
bw.DoWork += bw_DoWork;
bw.RunWorkerCompleted += bw_RunWorkerCompleted;
bw.ProgressChanged += bw_ProgressChanged;
bw.RunWorkerAsync();
private void bw_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker bw = (BackgroundWorker) sender;
var result = ItemsSource
.OfType<object>()
.Where(x => x.ToString().Contains(_FilterText));
foreach (object o in result)
{
// Pass each object found to bw_ProgressChanged in the UserState argument.
// This updates the UI as each item is found.
bw.ReportProgress(0, o);
}
}
void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
// FilteredItems is bound to the UI, but it's OK to update it here because
// the ProgressChanged event handler runs on the UI thread.
FilteredItems.Add(e.UserState);
}
private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Error != null)
{
MessageBox.Show(e.Error.Message);
}
}
Note that calling ReportProgress every time you find an item is pretty inefficient, as you're marshalling every item found across threads with an Invoke call. Depending on how long the filtering is actually taking, it may be better to accumulate a bunch of results and pass a List<object> to bw_ReportProgress instead of just a single object.
It depends on a lot of factors (and your description is a bit confusing), but I've given a lengthy answer here that may shed some light on the matter. Basically, using the dispatcher alone will not automatically make the code multi-threaded; you'll need some real multi-threading mechanism like BackgroundWorker or the Task Parallel Library. Depending on how you have things set up and on exactly what you do in the other thread, you may indeed need to invoke some actions on the dispatcher thread - however BackgroundWorker does this automatically in most cases so I'd go with that for simple things. The Task Parallel Library also has special handling for the dispatcher, you should find more info on that on MSDN or any TPL tutorial.
The best advice I'd give if you didn't deal heavily with multi-threading until now is to gather as much information as possible on it, because, as it has been said countless times until now, multi-threading is hard! :)
Modify as necessary. 'Items' is just an observableCollection of strings exposed from the VM
private void SetSearchResults()
{
BackgroundWorker bw = new BackgroundWorker();
bw.DoWork += LoadResults;
bw.RunWorkerCompleted += this.LoadResultsCompleted;
bw.RunWorkerAsync();
}
private void LoadResultsCompleted(object sender, RunWorkerCompletedEventArgs e)
{
}
private void LoadResults(object sender, DoWorkEventArgs args)
{
List<string> results = GetResults();
foreach (string result in results)
{
Application.Current.Dispatcher.Invoke(
DispatcherPriority.Normal, (ThreadStart)delegate { Items.Add(result); } //Dont worry about access to modified closure in this case
Thread.Sleep(100);
}
}
In XAML
<ListBox ItemsSource={Binding Items}/>
All views in the application have the same dispatcher, you can access it with Application.Current.Dispatcher.
But anyway, you don't need the dispatcher to perform operations on a worker thread. You only need it to perform actions on the UI, because UI elements can only be accessed from the UI thread. But even then, you usually don't need to explicitly manipulate the dispatcher. You can update a property of your ViewModel from the worker thread, controls bound to this property will be updated alright, because the PropertyChanged event is automatically marshalled to the UI dispatcher.
What doesn't work is modifying an bound ObservableCollection<T> from a worker thread: you need to do it from the UI thread using Dispatcher.Invoke. You can also use a specialized ObservableCollection<T> that raises event on the UI thread.