Tweak WPF application performance which hosts hundreds of similar controls - wpf

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

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.

My final solution is a custom Virtual Panel which supports items of dynamic height.
See http://rhnatiuk.wordpress.com/2006/12/13/implementing-a-virtualized-panel-in-wpf/ on how to create virtual panels.
My UserControls support two states:
- Initial
- Loaded
When the application is in idle the virtual Panel asks the Controls to change to the "Loaded" state. This loads the expensive UserControl.
Like that everything is lazy loaded and as soon as the user stops scrolling the visible items are loaded.
Maybe that helps others that are in the same sitaution.
TH

Try only to create the controls which are visible at the time, use lazy loading.
Maybe SnapsToDevicePixels=true can also help a little bit.

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

I remember reading something about how each instance of a UserControl loads the resource dictionary. So if you have as many of these as you describe it can take a while to load those.
Unfortunately I can't find the link I remember, but here is one that might help:
http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/c9b6aa9f-5a97-428c-8009-5cda432b820c
Another thing to try is to not use UserControls and instead use a DataTemplate to build your datagrids and buttons. Or make a custom control template for the datagrid that includes the buttons. Either one might be faster.
Hope this helps.

Related

Switch WPF UI controls dynamically

I need to develop a simple WPF application. In the UI window, There are Labels and Text Blocks towards the left and Buttons towards the right.
Figure 1
Based on a config setting (whether the user is left-handed or right-handed) I need to switch the controls, Buttons towards the left and Labels and Text Blocks towards the right.
Figure 2
Can you please recommend a good way to address this requirement?
Depends what the scope of the app is likely to be.
2 alternatives:
1)
I think it likely as an app grows that there will be more than just buttons.
I would probably build a usercontrol which encapsulates this behaviour for a label and control. The usercontrol uses a static to decide where the textblocks are positioned but would look something like the row edit control in this:
https://gallery.technet.microsoft.com/WPF-Entity-Framework-MVVM-78cdc204
Which is a usercontrol has a contentpresenter in it so you can put any control you like ( such as a button ) "in" it and set a dependency property for the label.
2)
Define 2 contentcontrol templates similar to the one used in this:
https://social.technet.microsoft.com/wiki/contents/articles/28597.aspx
Put them in separate resource dictionaries and give them the same key.
Merge into application.current.resources the appropriate resource dictionary and hence style.
Seeing as this is an app setting, this is presumably a start up thing. People don't just change their "handedness" dynamically. So you could probably use these as staticresource. If they're realistically going to change at run time then I think this would be a bit more involved because you'd need to force re render of a view.
2 Templates are probably the right and stylish solution here as #RajN said.
Also you can define a grid with 2 columns and switch the property 'Grid.Column' of each controls accordingly
Maybe not the best way, but I managed to achieve this using a grid as per your suggestions. Thank you all for your valuable feedback.
I switched the columns and changed the widths accordingly.
if (AppSettings.IsLeft)
{
parentGrid.ColumnDefinitions[0].Width = new GridLength(400, GridUnitType.Pixel);
parentGrid.ColumnDefinitions[1].Width = new GridLength(1, GridUnitType.Star);
Grid.SetColumn(buttonGrid,0);
Grid.SetRow(buttonGrid,0);
Grid.SetColumn(contentGrid,1);
Grid.SetRow(contentGrid,0);
}

Stop loading controls in a Wrap panel once it's "full"

In one of my previous apps I needed to add controls to a flowlayoutpanel in a winforms project dynamically, but I needed them to stop loading once there was no more room in the panel for them to fit.
To achieve this I wrote: https://github.com/LucasMoffitt/WordFiller/blob/master/WordFiller.Controls/WordLayoutPanel.cs
This basically just sets a property to false if an inbound control touches a rectangle I draw at the bottom of the panel.
While trying to replicate this behaviour in WPF I can't find any way in which I can force a WrapPanel to stop taking in controls if it's full.
I've attempted to override the Arrange and Measure methods but they only get called once all the controls have been added. I need to be able to stop the controls from being loaded at all.
Anyone have any ideas?
So I ended up taking in some suggestions and arrived at this:
https://github.com/LucasMoffitt/CustomWrapPanel
basically it's what I was doing to begin with just a little tidier, and has a demo app.
I encourage all contributions if anyone finds a nicer way of doing it!
You could check ActualHeight/AcxtualWidth against DesiredHeight/DesiredWidth. When DesiredHeight becomes larger than ActualHeight - the panel began overlapping.

Rotating windows in WPF

I need to create a WPF application which is maximized and which rotates amongst about 10 different screens. Each screen will take the entire area and show different content.
I already know how to maximize the window with
My question is what is best to put inside that window to achieve what I want?
Ideally I'd be able to have 10 different .xaml files and I just load one after the other to take the entire screen. I'm not sure the best approach for accomplishing this in WPF.
Thank you!
One quick way to do this is to use WPF's built in page navigation. By making your root window a NavigationWindow and each view a class derived from Page (similar to work with to a UserControl or Window) you can just set the NavigationWindow.Source to a relative URI that points to the page you want to show (like a web browser) and simply switch it as needed.
This sounds like a classic MVVM application, which is simply too much to put into detail here. Google MVVM or Model-View-ViewModel, or pick up the book Advanced MVVM by Josh Smith (widely regarded as an expert in such things).
However, this is basically what you are going to have:
One class, the ViewModel, is an abstraction of the data that you need to bind to
Your data Model
A View for each thing you want to show. A View is simply something that holds your UI, be it a DataTemplate or a UserControl. Each View is bound to the ViewModel
The Views are the things that will "rotate" (although rotate in WPF implies animation and/or transformation). How you switch between them is up to you, although it sounds almost like something that would be done with a DispatcherTimer and animation (i.e. like fading between pictures in a slideshow).
This question is really too broad for this forum - you will need to do quite a bit of research on WPF fundamentals before proceeding. Again, MVVM is a good direction to start.
EDIT: Something More Lowbrow, per OP Request
This is probably as simple was you can make it (and still create separate XAML files for each piece of content):
First, create 10 UserControls (XAML files) for the stuff you want to show.
Next, add an instance of each of these user controls to your main window. Set the Visibility of each of these to Collapsed, except the first one to show.
Put a "Next" button on the main window.
In the code-behind, handle the Click event for the Next button. In there, keep track of which UserControl is visible, by name. Set the one that is currently visible to Visibility.Collapsed, and set the next one that is supposed to be visible to Visibility.Visible.
This is certainly an ugly solution, and not very WPF-ish, but it will get the job done.

In what way a WPF Wrap panel is slower that we need virtual wrap panel

I hear a lot about the wrap panel being slower to load things and hence we need a virtualising panel.
Can somebody give me a small wrap panel sample where it can be proven it is slower to load etc that it needs a virtualising panel please.
I set a wrap panel as a panel control for a listbox, and added 10000 string objects to it, and it was not a problem. I am sure my sample was silly, maybe i have to write a business object and create a larger data template to see this problem in action.
Kindly show me a sample that proves wrap panel without virtualisation is slower.
Thanks.
I think the performance issue depends mainly on the number of visual objects in your tree.
The default ListBoxItem template consists of a low number of elements (a border and a textblock i think). If you have a template that creates a complex visualization of lets say 100 visual elements per item you get a fairly large amount of visuals depending on your item count.
This is the reason why the normal panel is slower at load time, because it has to create all the objects at startup whereas the virtualising version only creates visuals for the visible items and disposes no longer displayed visuals.
In addition this has also implications on memory usage
I recently needed this functionality when making a insert symbol form. Using a listbox with normal wrap panel as the items panel - load time would take up to 5 seconds.

Windows Forms Application Performance

My app has many controls on its surface, and more are added dynamically at runtime.
Although i am using tabs to limit the number of controls shown, and double-buffering too, it still flickers and stutters when it has to redraw (resize, maximize, etc).
What are your tips and tricks to improve WinForms app performance?
I know of two things you can do but they don't always apply to all situations.
You're going to get better performance if you're using absolute positioning for each control (myNewlyCreatedButton.Location.X/Y) as opposed to using a flow layout panel or a table layout panel. WinForms has to do a lot less math trying to figure out where controls should be placed.
If there is a single operation in which you're adding/removing/modifying a lot of controls, call "SuspendLayout()" on the container of the affected controls (whether it is a panel or the whole form), and when you're done with your work call "ResumeLayout()" on the same panel. If you don't, the form will have to do a layout pass each and every time you add/remove/modify a control, which cost a lot more time. see: http://msdn.microsoft.com/en-us/library/system.windows.forms.control.suspendlayout(VS.80).aspx
Although, I'm not sure how these approaches could apply when resizing a window.
Although more general than some of the other tips, here is mine:
When using a large number of "items", try to avoid creating a control for each one of them, rather reuse the controls. For example if you have 10 000 items, each corresponding to a button, it is very easy to (programatically) create a 10 000 buttons and wire up their event handlers, such that when you enter in the event handler, you know exactly which element you must work on. However it is much more efficient if you create, lets say, 500 buttons (because you know that only 500 buttons will be visible on the screen at any one time) and introduce a "mapping layer" between the buttons and the items, which dynamically reassigns the buttons to different items every time the user does something which would result in changing the set of buttons which should be visible (like moving a scrollbar for example).
Although, I'm not sure how these approaches could apply when resizing a window.
Handle the ResizeBegin and ResizeEnd events to call SuspendLayout() and ResumeLayout(). These events are only on the System.Windows.Form class (although I wish they were also on Control).
Are you making good use of SuspendLayout() and ResumeLayout()?
http://msdn.microsoft.com/en-us/library/system.windows.forms.control.suspendlayout(VS.80).aspx

Resources