I'm dynamically creating and adding controls to a GroupBox.
The GroupBox contains a single panel (Parent Panel) which fills the GroupBox.
Within this Parent Panel, i have multiple child panels.
It all looks and works as expected until i add 108 child panels to the Parent Panel. When this amount (or more) of child panels are added, each of the child panels suddenly becomes ugly (random black borders appear around some of the child panels, scrolling to the bottom shows black "blobs" everywhere). I can even see my windows desktop suddenly start showing on it!!!
Is there a limit to the number of panels/controls that can be added to the a GroupBox?
My spec is windows 10 Pro, visual studio 2017, .NET 4.7, 16gig ram.
I've tried all sorts of permutations (assumed the 108th entry was corrupt and skipped it, tried adding child panels in reverse order).
It seems like its not what is being added to the panel but the number of items. When i split the child panels over 2 groupBoxes, it works fine. But i want one groupBox to have all the data though.
Yes, there is a limit, the limit is for the entire session of the application. every control is a GDI Object, the limit is very far and if you pass it something is wrong with the architecture of your application and an exception will be thrown. instead of using controls use System.Drawing.Graphics class when you can in order save resources.
from MSDN (GDI Objects
):
There is a theoretical limit of 65,536 GDI handles per session.
However, the maximum number of GDI handles that can be opened per
session is usually lower, since it is affected by available memory.
Note that you can check how many GDI objects you create using windows task manager, add this columns:
Turns out that the parent panel was a custom panel that had a custom way of rendering child controls. It was attempting to auto calculate the panel heights in a weird way hence the weird output. However I've discovered another issue related to panels which I'll post
Related
I'm trying to create popout windows (like the dockable panes in Visual Studio). I need to create splitter controls to allow resizing of the controls when they're attached. I'm basing my design around the MFC docking panes and I'm trying not to hardcode docking regions.
Ideally I'd not like to have a fixed amount of sliders that are created hidden, but rather a variable amount depending on the location of each attached window. My current thoughts:
Use SubtractRect() to subtract the RECT of the attached window from that of the effective client area.
Create a slider for each edge of the control that is not on the edge of the main window's effective client area.
However when using CreateWindow() ideally a control ID would be passed in which is usually hard-coded in Resource.h, which then makes dynamic creation of controls tricky. Not to mention recalculating window sizes based on the location of the slider(s).
I'd thought about allocating an ID range to use for dynamic controls so I could start off with #define IDC_SPLITTER1 100 for example, then increment from this value for each splitter created. I've seen _APS_NEXT_RESOURCE_VALUE mentioned which I believe is used in MFC, but only for the IDE?
Sorry if this question is a bit wish-washy, I'm really struggling to get my head around this.
Thanks.
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.
Good afternoon. I'm creating a custom WPF layout control that needs to omit children from the measure/arrange process if they won't fit in the available area. I'm testing with Blend.
I have tried overriding GetVisualChild(n) and VisualChildCount to try and allow the runtime to only recognize certain elements as my desired children. This seems to work okay, but the big issue I'm having is this:
I drop some children into my layout control from Blend. I then shrink the control where some children won't fit inside my panel. The issue is that the children that are no longer in view remain rendered on the Blend design surface. I have tried invalidating their visual, calling "RemoveVisualChildI()".. but nothing seems to want to make the rendered visuals disappear.
Breaking into the GetViasualChild(...) method, I am witnessing Blend and the runtime are only asking for the actual visible children (fully contained inside my layout panel). So, it seems to me that the problem is tied to the child elements thinking that they still need to be rendered..or that the container panel just doesn't know it needs re-rendering.
Any clues? I can try and provide more detail if it's needed, but my basic need is the ability to selectively hide/show (omit from the measure/arrange process) certain children of a custom panel.
Thanks!
- Sean
Since nobody has responded, I figured I would share the solution. The visual tree is established through the AddVisualChild(...) method on the Visual base class. The Panel takes care of this for you with it's Children collection. To solve this, all I needed to do was create an instance of a UIElementCollection, and let it do the dirty work. When that class is created, it is passed a visual parent and an optional logical parent. When elements are added to that collection, the parent / child relationship gets established automatically.
Hope this helps the next person!
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.
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