Avatar of TDDatATS
TDDatATS
 asked on

Mdi forms, show performance tuning causing a weired issue

Hey guys,

I have a strange problem here. The base of the problem is that showing forms in an MDI form is awfully slow when the forms that need to be shown have a fair amount of controls that need showing. And that's without even taking into account the time for loading data into those controls.

Basically my solution was to set the baseform minimized and set the WindowState back to normal after calling the form.Show() method. This increases the speed significantly which I could visually see and measure after I let loose a StopWatch on the designer generated form with many controls.

Now, overall this approach seems to work, except for the fact that for some reason these same forms seem to be reacting very slow to the first Tab key.

Basically it takes like 2 seconds or something before the code even reaches the KeyDown event when pressing tab. I even tried with ProcessCmdKey, but even that takes a few seconds to reach, which is kind of strange I think.

I've tried all sorts of things, but it does seem like it somehow has something to do with starting the form minimized and setting it back to normal afterwards because when I don't do that, it works fine, but of course, then the showing of the form is slow, so it's bad either way.
Strangely enough, I'm currently having this problem only with designer generated forms, I also use this hierarchy to generate forms dynamically and put them in the same mdi region and they do not suffer this problem. Can there be any difference between forms created at runtime vs forms created at design-time?

My production forms have their own distinct look'n feel, but I tried with a regular form not inheriting any custom baseforms and the problem still persisted.
Different designer generated forms also suffer various slowdown times, many controls in the first tabpage seems to generate little slowdown before the tab command reaches ProcessCmdKey while others take awfully long before tab reaches ProcessCmdKey.
Slow: http://thekillinggoku.centelia.net/images/Slow.png
Fast: http://thekillinggoku.centelia.net/images/Fast.png
Also, I tried by removing the mdiparent assignation, but the problem persisted.

Basically, it's a strange situation, so I'm very curious what can cause something like this since I can't seem to find the problem. I tried recreating the problem in a new forms app, but couldn't seem to get it to have the same problem there.
The forms are called using reflection

So to recap -> designer generated form starts minimized, is set back to normal state and then the first tab takes ages to reach any key event. Without starting minimized, this problem doesn't occur.

Well, it's an expansive explanation, but I hope somebody can shed some light on this strange issue.
// ProcessCmdKey hierarchy from top form to baseform, problem is that the first one is only triggerred after quite a while.
// Start, received ProcessCmdKey (FrmBuyerManagement) with command: Tab
// FrmTabForm.ProcessCmdKey: 00:00:00.0008803
// Start, received ProcessCmdKey (FrmTabForm) with command: Tab
// FrmTabForm.ProcessCmdKey: 00:00:00.0019888
// Start, received ProcessCmdKey (FrmBase) with command: Tab
// FrmTabForm.ProcessCmdKey: 00:00:00.0050462
 
// This is the right execution, except that the first ProcessCmdKey is only executed after 
// about 2 seconds AFTER I press the tab key.
 
// Call stack before the first ProcessCmdKey 
>	AFISH.exe!AFISH.Forms.Relations.FrmBuyerManagement.ProcessCmdKey(ref System.Windows.Forms.Message msg = {msg=0x100 (WM_KEYDOWN) hwnd=0x180c76 wparam=0x9 lparam=0xf0001 result=0x0}, System.Windows.Forms.Keys keyData = LButton | Back) Line 200	C#
 	System.Windows.Forms.dll!System.Windows.Forms.Control.ProcessCmdKey(ref System.Windows.Forms.Message msg, System.Windows.Forms.Keys keyData) + 0x96 bytes	
 	System.Windows.Forms.dll!System.Windows.Forms.Control.ProcessCmdKey(ref System.Windows.Forms.Message msg, System.Windows.Forms.Keys keyData) + 0x96 bytes	
 	System.Windows.Forms.dll!System.Windows.Forms.Control.ProcessCmdKey(ref System.Windows.Forms.Message msg, System.Windows.Forms.Keys keyData) + 0x96 bytes	
 	System.Windows.Forms.dll!System.Windows.Forms.ContainerControl.ProcessCmdKey(ref System.Windows.Forms.Message msg = {msg=0x100 (WM_KEYDOWN) hwnd=0x180c76 wparam=0x9 lparam=0xf0001 result=0x0}, System.Windows.Forms.Keys keyData = LButton | Back) + 0x15 bytes	
 	System.Windows.Forms.dll!System.Windows.Forms.Control.ProcessCmdKey(ref System.Windows.Forms.Message msg, System.Windows.Forms.Keys keyData) + 0x96 bytes	
 	System.Windows.Forms.dll!System.Windows.Forms.Control.ProcessCmdKey(ref System.Windows.Forms.Message msg, System.Windows.Forms.Keys keyData) + 0x96 bytes	
 	System.Windows.Forms.dll!System.Windows.Forms.Control.ProcessCmdKey(ref System.Windows.Forms.Message msg, System.Windows.Forms.Keys keyData) + 0x96 bytes	
 	System.Windows.Forms.dll!System.Windows.Forms.Control.PreProcessMessage(ref System.Windows.Forms.Message msg) + 0x90 bytes	
 	System.Windows.Forms.dll!System.Windows.Forms.Control.PreProcessControlMessageInternal(System.Windows.Forms.Control target = {CustomControls.LookupComboBox}, ref System.Windows.Forms.Message msg = {msg=0x100 (WM_KEYDOWN) hwnd=0x180c76 wparam=0x9 lparam=0xf0001 result=0x0}) + 0x101 bytes	
 	System.Windows.Forms.dll!System.Windows.Forms.Application.ThreadContext.PreTranslateMessage(ref System.Windows.Forms.NativeMethods.MSG msg = {System.Windows.Forms.NativeMethods.MSG}) + 0xf6 bytes	
 	System.Windows.Forms.dll!System.Windows.Forms.Application.ThreadContext.System.Windows.Forms.UnsafeNativeMethods.IMsoComponent.FPreTranslateMessage(ref System.Windows.Forms.NativeMethods.MSG msg) + 0x5 bytes	
 	System.Windows.Forms.dll!System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(int dwComponentID, int reason = -1, int pvLoopData = 0) + 0x22e bytes	
 	System.Windows.Forms.dll!System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(int reason = -1, System.Windows.Forms.ApplicationContext context = {System.Windows.Forms.ApplicationContext}) + 0x177 bytes	
 	System.Windows.Forms.dll!System.Windows.Forms.Application.ThreadContext.RunMessageLoop(int reason, System.Windows.Forms.ApplicationContext context) + 0x61 bytes	
 	System.Windows.Forms.dll!System.Windows.Forms.Application.Run(System.Windows.Forms.Form mainForm) + 0x31 bytes	
 
// Calling the form:
/// <summary>
        /// Uitvoeren van menu item
        /// </summary>
        public void ExecuteEvent()
        {
            if (!string.IsNullOrEmpty(FormName))
            {
                Form baseForm = null;
 
                // Enkel als er is xml data is de formgenerator aanroepen
                if (FormXml != null)
                {
                    // Ja formgenerator gebruiken
                    GenerateForm generator = new GenerateForm();
 
                    // Tonen van form
                    baseForm = generator.Generate(FormName, FormXml, Global.MainForm);
 
                    // Formname gebruiken als title als er geen titel is ingesteld
                    if (string.IsNullOrEmpty(baseForm.Text))
                    {
                        baseForm.Text = Translator.Translate(FormName);
                    }
                }
                else if (!string.IsNullOrEmpty(FormName))
                {
                    // Er is geen formxml data
                    // Via reflection juiste form openen
                    // Assembly.GetExecutingAssembly().GetName().Name + "."
                    baseForm = (Form)Assembly.GetExecutingAssembly().CreateInstance("AFISH.Forms." + FormName);
                }
 
                baseForm.MdiParent = Global.MainForm;
                baseForm.FormClosing += form_FormClosing;
                baseForm.Show();
                baseForm.WindowState = FormWindowState.Normal;
                Global.MainForm.MdiChildren[Global.MainForm.MdiChildren.Count() - 1].LayoutMdi(MdiLayout.Cascade);
                // Force the form to be active
                //baseForm.Focus();
            }
        }
 
        /// <summary>
        /// Form closing event
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void form_FormClosing(object sender, FormClosingEventArgs e)
        {
            // Focus the treeview when a form is being closed
            if (!e.Cancel)
            {
                ((Form)sender).Hide();
                ((FrmMain)Global.MainForm).FocusTreeView();
            }
        }
 
        /// <summary>
        /// Form closed event
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void frmForm_FormClosed(object sender, FormClosedEventArgs e)
        {
            // Focus the treeview control when the form is closed
            ((FrmMain)Global.MainForm).FocusTreeView();
        }

Open in new window

.NET Programming

Avatar of undefined
Last Comment
TDDatATS

8/22/2022 - Mon
Bob Learned

I don't understand the included text.  Is that the output of your running application, or a profiling app?
TDDatATS

ASKER
Well, I thought the comments said it all, but the first few lines are the order in which the code enters the ProcessCmdKey override from the top form to the base form (just printlines in ProcessCmdKey override). Basically, showing that once it gets to the first one, things move normally, but it takes ages for it to reach the first one once I tab for the first time, which is kinda the problem.

The second is like I wrote in comment, the call stack at the time I set a breakpoint to the first ProcessCmdKey, so basically that's supposed to be what's being done before reaching the ProcessCmdKey. Which could be where it takes so long? I dunno bout that.

Third section of the added text is the code which handles calling these forms where you can see they're being called with reflection, but I've tried doing the same in a small test app, but it didn't generate the same defunct behavior.

Hope this gives you enough information.

Greetz
Bob Learned

You have to remember that not everyone is on board with what you are trying to achieve, plus I am more of a visual learner, and I sometimes struggle with pulling information from words.  There is also the concept of too much information to process.  If you don't understand how Experts-Exchange works, we are just volunteers who work out in the community, with all the askers, and we have regular jobs.  I try to make time to help out, but I try to limit my involvement.  This means that I don't spend a lot of time pouring through questions, code, ... I think that you will have a better experience with E-E if you understand what you are dealing with, and what we, as the "experts", have to go on.  Imagine coming into a situation where you don't have any history, knowledge, and how you go about gathering information, and then you will understand where I am coming from.

With all that said, here are my thoughts:

1) You have MDI forms, which typically are harder to work with than regular forms.
2) Do you know anything about LockWindowUpdate?
3) Attaching a .png screen shot to the question might be a great direction to show what the weirdness is like.
This is the best money I have ever spent. I cannot not tell you how many times these folks have saved my bacon. I learn so much from the contributors.
rwheeler23
TDDatATS

ASKER
1) I tried commenting out the mdi parent assignment, but the problem still persisted. May be relevant.

2) Have not yet worked with this, would it be a possible replacement for starting the form minimized to optimize showing speed?

3) Well, I added picture links to the original post to give you guys a general idea what the forms look like.
Unfortunately I can't add pictures that 'show' the weirdness, because there's nothing visually weired going on. The form just freezes and no longer reacts until the tab key has been processed, meaning until it reaches ProcessCmdKey (or any other key handler I could assign), once the tab command has arrived things move on as usual.
It's just that very first given 'tab' command that takes quite a while to reach the form, meaning that even though the form may show fast, if the user has to wait 2 seconds when he presses the tab key for the first time, then that's even worse because then the user'd have to wait again when he thought he could start working.
Bob Learned

Do you have anything like pseudo-code, UML sequence diagrams, or anything that would explain the sequence of events that are happening?
TDDatATS

ASKER
Sequence of events would be the following

- Application start
- Login form is displayed to validate the user
- After this the main form is displayed (the mdi parent container)
- This form has a treeview built dynamically from a database
- Each treenode gets assigned tags with various data including the contents of an XML field also stored in the DB (the dynamic forms are generated using the data in this xml file)
- Of course not all forms are dynamic, so when there's no xml data, we open a designed form, this is done in a custom business object stored in the node's tag which contains a method called ExecutEvent, which is what was attached to my first post.
- Opening designer forms is done via reflection through the name of the form stored in the database and also assigned to the menuItem's tag.
- Once the form is created the mdi parent is assigned and the form is shown minimized (design forms are set to FormWindowState = Minimized)
- After having called Form.Show() on the created form this form is then set back to FormWindowState.Normal.
- Form displays in the mdi area of the main form and awaits commands from the user.
- User presses tab, but the form freezes a while before the tab command reaches the first key handler like mentioned above.
⚡ FREE TRIAL OFFER
Try out a week of full access for free.
Find out why thousands trust the EE community with their toughest problems.
Bob Learned

>>User presses tab, but the form freezes a while before the tab command reaches the first key handler like mentioned above.

How much code is executed after the user presses <Tab>?
TDDatATS

ASKER
Basically, nothing at all. At least no managed code. Just default tab functionality to jump to next input control.
Even if I wanted to execute code when catching the <tab> key, I need to actually have it reach a key event handler first (or have it reach ProcessCmdKey which would intercept even before any handler) before I can do anything with it. And that's what seems to take the system so long to do.
ASKER CERTIFIED SOLUTION
Bob Learned

THIS SOLUTION ONLY AVAILABLE TO MEMBERS.
View this solution by signing up for a free trial.
Members can start a 7-Day free trial and enjoy unlimited access to the platform.
See Pricing Options
Start Free Trial
GET A PERSONALIZED SOLUTION
Ask your own question & get feedback from real experts
Find out why thousands trust the EE community with their toughest problems.
TDDatATS

ASKER
Hi Guys,

with profiling applications we"ve found out that the components of DevExpress caused a lot of hassles. Using the normal WinForms controls gives us a much better performance. Thanks for all the help.

Tom
I started with Experts Exchange in 2004 and it's been a mainstay of my professional computing life since. It helped me launch a career as a programmer / Oracle data analyst
William Peck