WPF performance problem with CommandManager - wpf

We've created a new, quite complex, WPF application from the ground up and have run into a performance problem as the number of commands registered with the CommandManager increase. We're using simple lightweight commands in our MVVM implementation, however the third party controls we're using (Infragistics) do not, and call CommandManager.RegisterClassCommandBinding liberally to add RoutedCommands. The performance problem manifests itself as a perceived sluggishness in the UI when responding to user input, for example tabbing between controls is slow, text input is 'jerky' and popup animation is 'clunky'. When the app is first fired up the UI is snappy. As more screens containing Infragistics grids are opened the performance deteriorates.
Internally, the CommandManager has a private field named _requerySuggestedHandlers, which is a List< WeakReference>. I've used reflection to get a reference to this collection, and I've noticed that when I call .Clear(), the responsiveness of the UI improves back to its initial state. Obviously I don't want to go round clearing collections that I know little about, especially using reflection (!) but I did it to see if it would cure the performance problems, and voila it did.
Normally, this situation would clean itself up after a certain amount of time passes. However, the collection of WeakReferences (_requerySuggestedHandlers) will only get trimmed once a garbage collection is initiated, which is non-deterministic. Because of this, when we close down windows containing grids (Infragistics XamDataGrid), the CanExecute property for 'dead' grid commands continue to be evaluated unnecessarily, long after the window is closed. This also means that if we close down a number of windows, the performance is still sluggish until a garbage collect is initiated. I understand that this can happen on allocation, and I've seen that myself because if I open a further window this causes the initial memory (from the disposed Windows) to be collected and performance returns to normal.
So, given the above, here are my questions:
How, and from where, does CommandManager.InvalidateRequerySuggested() get called? I haven't found any documentation on MSDN that explains this in any great detail. I hooked up to the CommandManager.RequerySuggested event and it looks like it's being called whenever controls lose focus.
Is is possible to suppress CommandManager.InvalidateRequerySuggested() being called in response to user input?
Has anyone else run into this issue, and if so, how have you avoided it?
Thanks!

This sounds like one of the rare cases where deterministically calling GC.Collect() is the right thing to do. The ordinary argument against it is that the garbage collector is smarter than you are. But when you're dealing with WeakReference objects, you enter territory where you may know something that the garbage collector doesn't. Kicking off garbage collection is certainly better than clearing _requerySuggestedHandlers - among other things, it won't do anything to the WeakReference objects that point to controls that are still alive.
I'd choose this over trying to figure out how to suppress RequerySuggested, since that would screw up the behavior of those commands that you still care about.

Related

How to optimize TreeNode name change?

I have a TreeView which holds approximately 100,000 TreeNodes or even more, I have optimized everything related to loading or unloading them on deserialization process but now I'm stuck with an issue I can't overcome.
Its important to mention I decided not to use the LabelEdit default event given by the control since its pretty tricky to make it work as I wanted to, Its widely known that there are a lot of "problems" with this particular event which have pushed many devs to implement their own custom TreeViews.
In my case I am using a ContextMenu which has a Rename option, this brings a textbox right in front of the TreeNode and then I just simply change the TreeNode.Text property to whatever the user input was in the TextBox keydown event, once we trigger this event, the whole GUI freezes for a couple of seconds (4-5), I'm not doing any Depth search over the TreeNodeCollection or anything, I am directly accessing the TreeNode and modifying the property...
So, any thoughts on what could be wrong here? I already tried BeginUpdate / SuspendLayout / or even a custom solution found here How do I suspend painting for a control and its children? and nothing seems to help.
The first thing that comes to mind is that when the text is changed on the node, it must be redrawing the entire treeview.
In this situation, suspendlayout will not help, as the control isn't laying its contents.
I think beginupdate stops the drawing when nodes are being added to the list, but the text changed might bypass this.
Have you considered not using the keydown, and just updating the text once the user has dismissed the textbox? (i.e. done editing). Not ideal, but will limit the performance hit to once, instead of every key stroke?

Scheduling UI tasks for after the visual tree has settled down

In XAML-based apps, quite often I need to manipulate on-screen elements, resize or move stuff around. The situation arises usually for very custom UI controls.
The timing of stuff loading and data-binding etc. can give rise to irritating timing issues.
Traditionally, my colleagues and I have worked around these problems by 're-dispatching' work to the UI thread so it runs later, when dependent properties are in the 'final' states.
Is there a better way to do this? What strategies have you found that work?
The LayoutUpdated event can be very noisy, fine-grained, and deregistering requires forgoing a Lambda for a method and thus not being able to access enclosed variables from the outer logic - its a bit of a 'mare.
Edit
I'll give a tangible example. A custom control draws an outline around a face when doing facial recognition, so we're talking totally custom stuff here, nothing XAML does out the box.
The image needs to be scaled and sized and the paths and geometries scaled and sized so its all in alignment.
When programmatically changing the heights and widths of elements, the impact is not immediate, its only once the UI thread is relinquished back to the XAML framework does the rendering subsystems rearrange everything.
Thus, logic that depends upon the changed values needs to run after the framework has rearranged everything, and so this work needs scheduling to occur later on the UI thread, hence resorting to dispatching. It smells.
Many events and virtuals are called at times when requisite data is not yet available. Sometimes, work needs to be done upon data arrival (i.e. property change notification) which is does not typically trigger the XAML layout events. In this case, we resort to dispatcher hacks.
What about using the Loaded event?:
Loaded += LoadedHandler;
...
private void LoadedHandler(object sender, RoutedEventArgs e)
{
// Do something here when controls have loaded
}
I discovered that in WPF, this dependency property metadata argument exists to help with the problems I'm talking about.
FrameworkPropertyMetadataOptions.AffectsRender
The enum doesn't exist in Windows Store applications, presumably since its designers think the InvalidateArrange method is a suitable alternative.
Possibly, we should be a) invalidating the arrangement within our DP change handlers b) making these arrangements over multiple passes c) tolerating incorrect layout in-between passes.
Still, the using lambdas with the dispatcher is nice since variables used in calculations can be referenced from inside the closure.

Memory leak and performance issue with WPF's TreeView

I'm running into a problem with the WPF TreeView control.
I think I ran into a memory leak issue with this control and also some performance issues.
I've prepared a simple demo solution where you can see these problems.
Download link: http://www.custom-projects.com/TreeViewMemoryAndPerformanceIssue.zip
I'm creating the tree based on some domain objects. The objects are wrapped in view models.
The number of levels is not restricted, but currently we have a maximum of 3 levels.
So, each view model can have children.
When you click on the up/down buttons of the UpDown control and don't release the mouse button you will see, that the update speed of the int value will get slower and slower and the memory consumption constantly rises.
What I'm doing: When you click on the up/down button the value is sent to the view model via data binding. In the setter I'm raising a event. Our application consists of different view models and if someone is changing data in one of them, the others are notified through these DataChanged events.
For simplicity, my demo solutions just consists of the NavigationViewModel. So it listens
for the DataChanged event and if fired, the tree is rendered.
Because we don't have a list which will always be the same (and just rows are added or removed), I'm not using a ObservableCollection. We always have to regenerate the list based on the objects the user has added/created.
Anyways, I'm adding these view models to a list and raise the NotifyPropertyChanged event
so that WPF updates the tree. Works well but the more the list is updated, the slower the application gets (and memory goes up).
I checked, that the item view models are garbage collected and they are, so I don't see
something wrong on my side. I also did some performance profiling. It looks, that the
issue is on the WPF side, because my code does not slow down. The Application.Run method
execution time rises... Strange thing.
Does anyone has an idea, why the memory is going up and never gets released and why the
performance starts to decrease the more often the TreeView updates itself?
I would appreciate any help or comment on this issue.
Thanks,
Christian
I profiled your test application using ANTS Memory Profiler and you can see that your classes 'NavigationItemBaseViewModel' and the array "NavigationItemBaseViewModel[]" are still held in memory by references, and this is getting worse with each increment.
If you slowly increment and allow the update to happen, then the references are broken and objects disposed. All good.
However, if you increment fast/continuously then you see that your references are not broken, thus the arrays are kept in memory.
The increments get slower each time because your application is having to update a lot of these view models, at increment #58 I had 172 arrays holding between them 517 NavigationItemBaseViewModel's.
Where with "normal" functionality you only have 4 arrays and 13 NavigationItemBaseViewModel's.
Hope that helps, I would recommend you profile your memory if you cannot figure out your logic where new are creating new arrays. Typically it is best to reuse arrays.
Profiler I used is here: http://www.red-gate.com/products/dotnet-development/ants-memory-profiler/index2
Hope that helps.
I was investigating lots of memory leaks in WPF and i find this tool very useful: http://www.jetbrains.com/profiler/ It has trial period of 10 days (i've just checked) so i hope you will be able to find your problem.

Is it possible to refresh a CollectionViewSource too often? Having some strange issues occur

I've got these ListViews bound to a couple of CollectionViewSources. Those are in turn bound to ObservableCollection lists which have plenty of data that updates quite often (it monitors a 32 player game server and displays player metadata and events). I wouldn't ever call Refresh() on the view if it weren't for the apparent necessity to make sure it is continually sorted by Score and to ensure added/removed players are updated on the UI.
Now the issue I've run into is that too many refreshes to the Views will cause them to lose the ability to open a context menu. You can right-click all you want but it will not appear until you destroy the form and re-instantiate it (an unacceptable workaround).
Has anybody ever seen this behavior, and know how to get around it? Initially I was Refreshing as often as it felt it needed to -- this caused the issue almost immediately. I reduced these Refreshes to every 5 seconds and now it happens but not quite as often.
We have had several problems with the CollectionViewSource. It causes lots of performance issues after openening the same window for appr. 5 times. It might be because we were using an LLBLGen collection as items source, but I will never use the CollectionViewSource again.
Now, I wouldn't refresh the view manually. Your ObservableCollection is responsible for updating the data. Try removing the CollectionViewSource for now (so you will lose the sorting ability), and see if the problem still occurs. If it disappears, you will need another way to sort your collection.

WPF command not executable

I have a button on a toolbar that has its command set to "MyControl.Print" (for example).
In the control the command is added to the command bindings including both the Execute and CanExecute.
The control is within a window with other controls docked appropriately.
I am finding that for the Print button to be enabled I have to "select" MyControl first which does not provide a good user experience and indeed causes various "bugs" being raised and lots of confusion.
Is there a way that I can ensure that the button is enabled whether or not the control has been "selected"?
Since the CanExecute doesn't fire, I think you might be looking at the major downside to RoutedCommands - the way they tunnel and bubble can leave a highly composed interface unable to have commands arrive anywhere useful. For this reason we ended up moving to DelegateCommands from (I think) the Microsoft CAG. Not any of the other stuff, just the commands. Works a lot better, and isn't tied in to the interface so tightly.
Oh, the other response raises a good point. I assumed you meant that to ever print, your MyControl needed to have keyboard focus. Is it only the first time and after that it works?
I recommend http://msdn.microsoft.com/en-us/library/ff921126(PandP.20).aspx as a pretty good starting point. You don't have to worry too much about the IActiveAware up front, since you're hoping for this command to be available all the time (or at least let its availablity be determined by CanExecute).
CommandManager.InvalidateRequerySuggested will force the command manager to re-call all of your CanExecute methods and should disable the button. Perhaps call that onload?

Resources