views:

57

answers:

4

Hello,

We just ported our WinForms application to WPF. However, performance decreased dramatically.

We have a User Interface which consists of about 200 UserControl. Each UserControl is defined by a DataGrid (= 10 columns and 3-15 rows) as well as a Panel which hosts about 10 Buttons.

They are all hosted in a ScrollViewer.

(Please don't recommend to change the UI. I don't have any influence on that. The customer wants to be able to scroll to any of those UserControls.)

Since we ported the whole application to WPF the startup time increased by 100%. Using WinForms we experienced startup times of 15sec whereas now, we are struggeling with 30s.

Do you have any recommandations or ideas how to improve the loading time of a UI which consists of identical UserControl where simply each UserControl is bound to a different ViewModel? (Maybe some fast cloning of the UserControl instances or sth similar?)

I am using static Resources whereever possible. I avoid Grids and Auto Sizing whereever possible.

Hope someone can share some thoughts on that one.

Thanks, TH

A: 

Try only to create the controls which are visible at the time, use lazy loading.

Maybe SnapsToDevicePixels=true can also help a little bit.

codymanix
+1  A: 

First find out what is responsible for the time.

Maybe it's the controls, and maybe not. Often it's data structure.

I use the random-pause method.

Mike Dunlavey
I profiled the application already. 40% of the time is lost anywhere in the WPF. Do you know any non-sampling profiler that can tell me what methods of the .NET Framework consume the time?
TwinHabit
@TwinHabit: The thing to do is run it under the debugger, and while it's being slow, hit the pause button, and study the call stack. Take the time to understand why it's doing whatever it's doing. Do this 5-10 times. It will show you what the problem is, guaranteed.
Mike Dunlavey
@TwinHabit: What you need to know is which lines in your code are on the stack most of the time and why. Examples: If it's painting, maybe you only need to suspend painting until all controls are up. If it's building big data structure and getting it all cross-linked, that's a different problem. Maybe both are happening. Maybe something else entirely.
Mike Dunlavey
I followed your advice. Unfortunately the problem really is in WPFs layouting / rendering of my UserControls. I loaded all ViewModels / Resources in advance and later added the whole picture to the View. My performance profiler is telling me the same: None of my line of codes are executed during this slow phase. So I have to find a way to speed WPFs layouting / rendering etc. up. Does anyone know any common pit falls?For example:Is it possible to tell the ListBox that it should first load every UC into video memory before it is displayed?Or is it possible to deep clone UCs or anything similar?
TwinHabit
@TwinHabit: Hmmm... Ok, I'm getting out of my depth here, but in plain old Win32, there's a way to suspend painting until all controls are in place, for example by hiding the top window. For layout, I can easily see that getting out of hand. I really hate to guess, but maybe turning off layout would make a difference.
Mike Dunlavey
I just figured that the most expensive work that is being done is the layouting. MeasureOverride and ArrangeOverride of each of my UserControls is called 5 times! Usually only one call to each method should be enough, as soon as every child has loaded its ViewModel and therefore knows how much space they require... Damn...
TwinHabit
I just tried to replace the ListBox which hosts all my UserControl with a Canvas. I thought, as I can calculate the absolute width and height of my UserControls I can speed the layouting up by simply setting each UserControls width/height manually and add the Control to the Canvas settings its Canvas.Top property. I got quite shocked that this _increased_ performance by another 50%!! How can that possibly be any slower than what the non_virtual ListBox is doing? I think I have to go with the Canvas solution anyways because the ListBox recreates each control when the ItemsCollection is resorted
TwinHabit
A: 

Guys, I thought about the following implementation. If anyone has concerns please let me know:

I will implement my own virtualizing "StackPanel" which supports smooth scrolling.

For the moment we assume that the height of my UserControls is fixed. One page could possibly hold 5 UserControls.

I will then go ahead and cache the preceding as well as the proceeding page:

In memory I will always hold 15 UserControls. The content of the ScrollViewer is a Canvas. Locations of my UserControls are adjusted by setting Canvas.Top. Let's say the current situation is the following:

User has scrolled to page 2. That means UserControl 5-9 is visible. Now the user scrolls down. As soon as UserControl 5 becomes invisible I take the UC of the top (in this case UserControl 0), change its ViewModel and adjust its Canvas.Top so that it now is the Control which is at the End of the ControlCollection. If the user scrolls any further I take UC 1, change its ViewModel and adjust its Canvas.Top. And so on.

Furthermore I will set Canvas.Height manually so that the ScrollViewer represents the ScrollBars in a correct way.

I hope my explanation is understandable :)

What do you think?

BR, TH

TwinHabit