What are the side effects of Control.Controls.Add in Winforms? - c#

I am trying to dynamically create a rather large TableLayoutPanel for an application I'm working on. As I've read from other questions, the TableLayoutPanel class suffers from significant performance problems when handling large and changing contents; I have, however, come too far to switch to something else just now.
I have been able to get around most of the issues by suspending layout before adding controls to the individual cells. One problem remains - when the table is first shown, it appears to take a little time before the background colors (just white) and everything else ready to be shown - like just a little less than a second of ugliness. I have tried doing all layout on startup, so that no calculations remain when the control is finally added, but the first drawing of it remains ugly. The weird thing is that when the control is shown again later (I keep it in memory instead of recreating it all the time) after having been removed from the main form for a while, it appears right away without any layouting. This leads me to believe that something happens when I first add the control to a child control of the main form, something I haven't reproduced in my own layouting - the question is, what? In order to force layout of the control right after its initialization, but before it is first shown, I do the following:
table.SuspendLayout();
// Create a whole lot of controls here and add them to the TableLayoutPanel
table.ResumeLayout();
table.Size = reportControl.Panel2.ClientSize;
table.PerformLayout();
table.SuspendLayout();
// Add the TableLayoutPanel to the main form here
Shouldn't that be enough? Unless I am very much mistaken, I do not touch the TableLayoutPanel between this code block and when it is added to the main form. Still, when it is first added, it looks way different than the second time. Oh, and the TableLayoutPanel is double buffered, so that's not it I think.

The extra thing that happens when you call Controls.Add() is that the native Windows window gets created. Plus the windows for all the controls you put in the panel. Which tends to be slow because TLP is often stuffed to the gills with controls. This doesn't happen when you Remove() the control and don't Dispose() it.
You can create the window early by calling CreateControl(). It will take just as long, but maybe isn't as noticeable.

Related

Resizing columns not refreshing properly

I made some change, I don't know what, and now when I try to resize columns on my DataGridView it just moves the line the control doesn't redraw properly. Basically parts of the control just don't redraw at all until I do something like move another window over them to force a redraw.
The only thing I can think of is that I tried suspending and resuming layout to fix another issue. It didn't work so I removed those lines of code. Don't see how that could affect it since those lines of code are not even there anymore though?
Not even sure how to debug this further.
I added a call to Refresh() the control in its OnColumnDisplayIndexChanged event and that seems to have fixed it.
A strange, but not problematic, side effect is that I can now see the columns moving for a half a second as the application programmatically sets the column widths after loading data. I guess this makes sense, however in the past it would only display after the code had updated the widths. But oh well, that's not a big deal.

How to initially place WPF controls on dynamic fullscreen application

Alright, so I'm trying to figure out the best way to accomplish this for my rather unique case. I have a fullscreen WPF application where controls are added to a Grid dynamically and the user is given the option to freely move/resize them. The controls can be individually moved or resized anywhere on the parent Grid but I have prevented them from overlapping via their MouseMove events. They also cannot be moved outside of their container. Since the app will be running on machines with different monitor sizes, I need to take that into consideration when making the original layout. I have an initial layout that I would like to use, but it would seem that I'm out of options:
I first tried using rows and columns just to (initially) place the controls that are added. This method places them correctly but becomes a problem when I need to move or resize the elements because the control is already assigned to a particular row and column. I could try to use this method and then remove any rows/columns after placing the controls, but I don't think that would work well.
I've also considered a Viewbox, but that's not practical in my case for fairly obvious reasons (as it merely resizes the controls to fit the screen). I'd prefer not to use this because I would only need it to standardize my initial layout. That's it. I also don't want to mess up any text that will be displayed on the window.
So yeah, this is more of a "best practice" question because any solution I can think of would not look very professional or elegant. Feel free to ask any questions if you need clarification.
Edit: As an additional note, I'd prefer to stick with a Grid as opposed to a Canvas as my container.
Edit 2: Just to be clear, I would not need the specific (inital) layout after the first launch. When the program exits, the layout (Margins, Width & Heights, etc. for each element) is saved to a file to use for the next launch.
I've developed a number of kiosk/interactive applications using WPF. If you are trying to show the element transitions (while moving), then it might be best to use a parent Canvas and bind the Canvas.Left and Canvas.Top properties. You can mimic the grid alignment, using a Canvas, if you put in place mechanisms (e.g. Manipulation/Mouse events, converters) to make sure that the Canvas attached properties adjust to the row/column offsets.
It is not uncommon at all to use the Viewbox to mitigate display differences (and your use case is not "rather unique"). You set the Viewbox to a target resolution (e.g. 1920x1080) and allow the control to fill the available space. The other alternative would be to dynamically apply a content template based on the application window size/ratio.
After looking at multiple options, I've decided to just use a calculation to (sort of) simulate the behavior of rows/columns. Because my application is fullscreen, I can take my SystemParameters.PrimaryScreenWidth and SystemParameters.PrimaryScreenHeight to orient my layout. Using a combination of universal padding (static values) and ratio-based calculations (dynamic values), I can smoothly set my initial layout.
For instance, I'm dividing the width of my monitor by 6 (rounded up to avoid decimals) and using that (minus half the control's width) as the control's Margin.Left property, centering it on a 'column' of sorts.
Honestly, my initial layout is fairly simple right now, so we'll see if this will suffice going forward. Thanks to everyone who contributed to the question, and sorry if I was unclear on what I was asking.

Avoid reload/redraw all components when swapping TabPages

I have multiple TabPages on a TabControl. Some of these TabPages contain up to 100 Panels in a FlowLayoutPanel. Each of these Panels also contain a bunch of different controls...
The issue is simple: when I swap between TabPages, the one containing lots of controls take ages (1-2 seconds) to load.
I would like them not to lose "focus", to keep the components in memory or something when left for another tab, so that they don't have to reload everything when we come back on it.
Is it even possible? Or is it the drawing itself that take all this time? I do not do operations when changing tabs. Just displaying them.
Clarification edits:
Hum, maybe I was not clear enough. The problem is not how the stuff is drawn when being loaded. I have fixed this issue my own way (working with selected tab events). My problem is the time taken by the page to actually load. When I enter the TabPage containing lots of controls (in FlowLayoutPanel), it will load for about 1-2 sec THEN be displayed. I would like that to be almost instant, all controls being already available or something. But maybe it is not possible? Maybe it is the time taken to draw all those controls that is long?
What's weird is that it takes less time to actually generate all those panels (100 in less than 0.5) for the first time, than to redisplay them when leaving/entering the page after.
The displaying is also instant when coming from Minimized state (the form itself). I would like that to be the same when changing form state than swapping between tabs.
This is known issue with FlowLayoutPanel when having many controls inside. It tries to layout all of the controls and each time control is positioned part of the screen is redrawn.
To reduce this, try to enable double buffering on your form or FlowLayoutPanel control. This allows to do all rendering in memory first and swap buffers once when it is done.
Add this code:
public static void SetDoubleBuffered(System.Windows.Forms.Control c)
{
//Taxes: Remote Desktop Connection and painting
//http://blogs.msdn.com/oldnewthing/archive/2006/01/03/508694.aspx
if (System.Windows.Forms.SystemInformation.TerminalServerSession)
return;
System.Reflection.PropertyInfo aProp =
typeof(System.Windows.Forms.Control).GetProperty(
"DoubleBuffered",
System.Reflection.BindingFlags.NonPublic |
System.Reflection.BindingFlags.Instance);
aProp.SetValue(c, true, null);
}
Then just call this on your form or control.
SetDoubleBuffered(mainForm);
SetDoubleBuffered(myFlowPanel);

Drawing Custom Control above all other controls (previously added)

I have a UserControl that adds other UserControls, but I want the "latest" control added to be topmost so it's above the others. Because the controls should be overlapping eachother. Like a card game. So I add 5 controls, the first one should have the least priority the latest the most priority - most visible.
Any ideas?
Or do I have to override the Paint method for the "container" control? And Control.CreateGraphics() and draw it?
Consider BringToFront and SendToBack methods of the Control class.
Check out answers to these questions too
How to set Z-order of a Control using WinForms
Bring Winforms control to front
Just use userControl1.BringToFront() when you add the new control.
Note however, that won't prevent the user from "tabbing" into the controls that are underneath it. For that, you need to disable or make invisible the other controls.
In Windows Forms, the order in which controls are added to their parents' Controls collection determines the order of their rendering.
This means that either you handle the addition of child controls in code and use the appropriate insert positions, or you move the controls around in the designer (which, unfortunately, often means dangerous hand edits to the *.Designer.cs file).
I recommend that you go for the first approach, which is the only feasible method for larger WinForms projects anyway, and make the control insertion logic explicit in your code. The good news, by the way, is that there is no need to tinker with paint handlers, so your worry about hacks like using CreateGraphics() is unjustified and dispelled :)

How do I get two panels to share a parent's width in Devexpress

This sounds simple to me but I'm not sure if there's a best/suggested way to do it.
I'd like my UI to have a panel docked along the top, split into two panels, left and right, that always share the width of the parent equally. There'll be a minimum overall width so nothing gets squashed, but on resizing I'd like the two panels' widths to always be equal.
I thought of using a split container control but it doesn't do what I want in this case as I can't disable manual resizing.
My current idea is just to override the onResize method (forgot the exact name), and just manually set the two widths to parent.width/2, but it seems a bit roundabout, and potentially slow if it's calling onResize for every pixel's worth of movement.
Is there a better way to do this, or a control/layout that handles this for me?
Disclaimer: I'm using an older version of DevExpress, 10.1.4. It's not my decision and I don't think I can get the team to upgrade at the moment. Using C# on .Net platform 3.5.
I would drop a couple of panels onto the form and set their Size within the form's ResizeEnd event handler. This looks to be the best solution from my point of view.
You can use the XtraLayoutControl to achieve this, but that might be overkill.
Otherwise, you'd have to do it manually.

Categories