[Okta Webinar] Learn how to a build a cloud-first strategyRegister Now

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 390
  • Last Modified:

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
0
stuengelman
Asked:
stuengelman
  • 11
  • 5
  • 3
  • +1
1 Solution
 
CodeCruiserCommented:
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.
0
 
stuengelmanAuthor Commented:
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
0
 
CodeCruiserCommented:
Do you load controls programmatically?
0
What does it mean to be "Always On"?

Is your cloud always on? With an Always On cloud you won't have to worry about downtime for maintenance or software application code updates, ensuring that your bottom line isn't affected.

 
stuengelmanAuthor Commented:
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

0
 
CodeCruiserCommented:
Does the slowness occur when clicking a tab first time or does it occur everytime?
0
 
stuengelmanAuthor Commented:
Hi CC,

It occurs every time.

Stu
0
 
CodeCruiserCommented:
I dont think you can do much then. If you stop the layout logic, the tabs wont paint and you obviously dont want that.
0
 
stuengelmanAuthor Commented:
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
0
 
CodeCruiserCommented:
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.
0
 
stuengelmanAuthor Commented:
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
0
 
Rahul_GadeCommented:
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
0
 
stuengelmanAuthor Commented:
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
0
 
stuengelmanAuthor Commented:
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
0
 
Bob LearnedCommented:
Look at the Selecting event.

TabControl.Selecting Event
http://msdn.microsoft.com/en-us/library/system.windows.forms.tabcontrol.selecting.aspx
0
 
stuengelmanAuthor Commented:
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
0
 
Bob LearnedCommented:
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

0
 
stuengelmanAuthor Commented:
LearnedOne,

Thank you.  Will test your approach.

Stu
0
 
stuengelmanAuthor Commented:
LearnedOne,

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

Thanks, Stu
0
 
Bob LearnedCommented:
With a converter, like Telerik has, you can convert your own code:

http://converter.telerik.com/
Private Const WM_SETREDRAW As Integer = &Hb

Private Const WM_USER As Integer = &H400

Private Const EM_GETEVENTMASK As Integer = (WM_USER + 59)

Private Const EM_SETEVENTMASK As Integer = (WM_USER + 69)


<DllImport("user32", CharSet := CharSet.Auto)> _
Private Shared Function SendMessage(hWnd As IntPtr, msg As Integer, wParam As Integer, lParam As IntPtr) As IntPtr
End Function


Public Shared Sub SetRedraw(shouldDraw As Boolean, control As Control, ByRef eventPtr As IntPtr)
	If shouldDraw Then
		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)
	End If
End Sub

Open in new window

0
 
stuengelmanAuthor Commented:
LearnedOne,

Thanks so much.  Will test it out.

Stu
0

Featured Post

NFR key for Veeam Backup for Microsoft Office 365

Veeam is happy to provide a free NFR license (for 1 year, up to 10 users). This license allows for the non‑production use of Veeam Backup for Microsoft Office 365 in your home lab without any feature limitations.

  • 11
  • 5
  • 3
  • +1
Tackle projects and never again get stuck behind a technical roadblock.
Join Now