Link to home
Start Free TrialLog in
Avatar of stuengelman
stuengelmanFlag for United States of America

asked on

Need Help Choosing Events To Place Commands to Speed Up Paint Operations on .NET TabControl

Hello,

I am building a Windows PC application in Visual Studio 2008 using VB and the .NET 3.5 Framework.

Most of my main application form is covered by a TabControl.  The clickable tabs themselves are hidden, but they are programmatically clicked via Main Menu commands and Main Toolbar buttons.  This programmatic clicking causes different content (controls, tables, charts, etc.) to appear within the the main application form just under the Main Toolbar.

Most of my tabs populate slowly, especially those populated with UserControls associated with records on database tables.  I think paint speed would be accelerated significantly if I could invoke the SuspendLayout/ResumeLayout commands whenever a programmatic click of a TabControl tab occurs.

My question is: which events of the TabControl object should I place the commands in?  Ideally, the SuspendLayout would be invoked before tab content painting begins, and ResumeLayout would be invoked just after tab content painting ends.

Thank you, Stu Engelman
Avatar of Nasir Razzaq
Nasir Razzaq
Flag of United Kingdom of Great Britain and Northern Ireland image

SuspendLayout/ResumeLayout are used when you want to stop the layout logic so that you can add all the controls that you want to add and then resume the layout logic at the end. This is so that painting/layout is not done with every control that you add.

If you want to use these, you would use these in code that is adding the controls to the tabs. So suspectlayout before adding controls and resume layout after adding controls.
Avatar of stuengelman

ASKER

Hi CC,

I follow what you are saying.  The issue is that there are alot of tabs.  I need events at the TabControl level so the Suspend/Resume logic is "generalized".

Thanks, Stu
Do you load controls programmatically?
Hi CC,

Yes, that is part of the issue.  The slowest tabs to paint are the ones that instantiate UserControls related to records on a DB table in a programmatic manner.  But other tabs with relatively more static content paint slowly too.

Essentially, what I'm looking for are events at the TabControl level to place the Suspend/Resume Layout logic (i.e., whenever a tab click occurs).  This saves me the effort of having to place the logic at the tab level (there are alot of tabs).

Stu

Does the slowness occur when clicking a tab first time or does it occur everytime?
Hi CC,

It occurs every time.

Stu
I dont think you can do much then. If you stop the layout logic, the tabs wont paint and you obviously dont want that.
Hi CC,

I think the solution depends on the sequence of events that fire when a tab is clicked.  I need a custom handler for an event that fires before the Paint Event to place the SuspendLayout command, and another custom handler for an event that fires after the Paint Event to place the ResumeLayout command.  The question is: which TabControl events do I use?

Stu
If you suspend the layout just before paint, you tab wont be painted and you wont see any controls. Is that what you want? Check the invalidating event.
Hi CC,

That's the whole idea.  I want to place the SuspendLayout command in an event at the TabControl level before tab painting begins, and place the ResumeLayout command in an an event at the TabControl level after painting ends.

I need to use TabControl level events so the commands apply to all tabs.

Stu
You will either need to write custom events propagated from tab level controls or the other solution that can solve your problem is to hide the tab control while performing this operation and show something like "Please wait" (Which looks professional) and then unhide the control once ready.

-Rahul
Hi Rahul,

I prefer to avoid the tab invisibility approach, as the whole system is tab driven, and getting a "Please Wait" every time someone switches tabs will be annoying to users.

I could, in theory, place SuspendLayout/ResumeLayout in each Sub that populates each tab, but I'm hoping to avoid this due to the numbr of tabs.

My "ideal solution" would be to understand what the sequence of events is when a tab is clicked.  The idea is to choose a suitable event before Paint to place SuspendLayout, and a suitable event after Paint to place ResumeLayout.  The question is: what are the two best events to use?  I'd like these two events to be at the TabControl level, not the tab level, as I don't wish to have to code the event handlers separately for each tab.

Thanks, Stu
Hello All,

I haven't received any input yet on TabControl events to target, so I tried putting logic directly into the handler for one of my tabs when invoked via the Click event.

My basic logic is:

object.Visible = False
object.SuspendLayout()
[main logic of Sub]
object.ResumeLayout(True)
object.Visible = True

In the above, "object" was tested using both the TabControl, and the applicable tab, as "object".  In neither case did any speeding up occur.

Any suggestions?  This approach follows the second option given in Rahul's 10/31/11 04:43 AM post.  The tab does not hide while it slowly repaints.  It seems my extra logic is not actually doing anything.

Thanks, Stu
Hi LearnedOne,

Thanks for your input.  The TabControl.Selecting Event only addresses where to place the SuspendLayout command; I still have the issue of where to place the ResumeLayout command.

At this stage I'm less concerned about the efficiency of finding TabControl events to place the commands than I am about simply finding commands that will solve my problem.  It's not that big a deal to place the commands in the individual tab click handlers themselves, as long as I knew what to put there.  Per my earlier post, I'm currently using:

object.Visible = False
object.SuspendLayout()
[main logic of Sub]
object.ResumeLayout(True)
object.Visible = True

In the above, "object" was tested using both the TabControl, and the applicable tab, as "object".  In neither case did any speeding up occur.  Object visibility was not affected at all, and the same slow painting occured.  In other words, the above logic does absolutely nothing to improve performance.

The big issue is occuring in the step "[main logic of Sub]".  This logic for most tabs reads a DB table, and renders a UserControl for each record in a DB table (typically input controls for the table fields, and Alter/Delete buttons).  Even with very few table records (say, 4 or 5), the process is very slow and annoying.  Neither the Visibility nor Layout commands impact performance in any manner; it's as if VB is completely ignoring the extra commands.

I need some way to drastically speed up painting of the UserControls, and possibly speed up their disposal as well (e.g., when the Refresh button on the tab is pressed).

Thanks, Stu
SelectedIndexChanged Event
http://msdn.microsoft.com/en-us/library/system.windows.forms.tabcontrol.selectedindexchanged.aspx

This event occurs when SelectedIndex is changed, after the tab is visible.

SuspendLayout/ResumeLayout only disables layout events temporarily, and does nothing for slow painting performance.  If you need to address that, you might look into SetRedraw (sending WM_SETREDRAW to a control).

Here is a proof-of-concept reference:

http://social.msdn.microsoft.com/Forums/en/csharpgeneral/thread/d5444fa4-26e0-4ecc-9a33-a590c55531af
private const int WM_SETREDRAW = 0x000B;

        private const int WM_USER = 0x400;

        private const int EM_GETEVENTMASK = (WM_USER + 59);

        private const int EM_SETEVENTMASK = (WM_USER + 69);


        [DllImport("user32", CharSet = CharSet.Auto)]
        private extern static IntPtr SendMessage(IntPtr hWnd, int msg, int wParam, IntPtr lParam);


        public static void SetRedraw(bool shouldDraw, Control control,
            ref IntPtr eventPtr)
        {
            if (shouldDraw)
            {
                SendMessage(control.Handle, EM_SETEVENTMASK, 0, eventPtr);
                SendMessage(control.Handle, WM_SETREDRAW, 1, IntPtr.Zero);
                control.Invalidate();
                control.Refresh();
            }
            else
            {
                SendMessage(control.Handle, WM_SETREDRAW, 0, IntPtr.Zero);
                eventPtr = SendMessage(control.Handle, EM_GETEVENTMASK, 0, IntPtr.Zero);
            }
        }

Open in new window

LearnedOne,

Thank you.  Will test your approach.

Stu
LearnedOne,

Would it be much trouble to provide a VB version of your code?

Thanks, Stu
ASKER CERTIFIED SOLUTION
Avatar of Bob Learned
Bob Learned
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
LearnedOne,

Thanks so much.  Will test it out.

Stu