Floating Tool Window disabled after layout is loaded

Docking/MDI for Windows Forms Forum

Posted 14 years ago by Crispin Horsfield - Software developer, Caz Limited
Avatar
In the first instance I created a set of three Tool Windows programatically which I arranged (including one floating) and then saved this layout. This reload worked fine.

I've now added code for Document Windows and changed the DockManager DockManagerMdiStyle to Tabbed. That all seemed to work except that on closing the form I got an error: "GDI+ is not properly initialized". This I cured with an explicit Me.Dispose on the form closing method. I haven't a clue as to whether this error is a result of UIStudio or something else.

Now I'm getting some bizarre behaviour: when the application is started, the layout is loaded (including the floating ToolWindow) but clicking on the floating ToolWindow produces no result: no GotFocus fired; no GotFocus event on the inner UserControl. If I then programatically dock the ToolWindow (via a menu event) then the ToolWindow is enabled again and works properly.

Anyone got any ideas what the problem may be?

TIA

Crispin

Comments (18)

Posted 14 years ago by Actipro Software Support - Cleveland, OH, USA
Avatar
Haven't heard of that happening. Could you email us a project that duplicates it, or even tell us how to modify our sample project code to repro it? That way we can debug what's going on.


Actipro Software Support

Posted 14 years ago by Boyd - Automated Software Testing Consultant / Developer, Patterson Consulting, LLC
Avatar
I've actually experienced a similar problem, but I haven't been able to track it down completely. It tends to deal with floating windows, and I just assumed it was part of some issues I've been having with ActiveX controls.

When I would right-click on the floating title bar caption, the menu options were mostly disabled. It appears that DockManager still thought another ToolWindow was active. Clicking around between the various ToolWindows would eventually get everything in sync.

I'm still trying to isolate how to repro this.
Posted 14 years ago by Crispin Horsfield - Software developer, Caz Limited
Avatar
The project in which I'm having the floating ToolWindow problem is quite complex, so I'm trying to reduce it to a simpler one. Isolating the offending code is turning out to be not an easy task.

I did make a bit of progress on the GDI+ problem (which may or may not be associated with the floating ToolWindow enabled state). Rather than having to call Me.Dispose in the exit method (ie disposing the entire form), it's possible to explicitly dispose of the ToolWindow eg:
objTooWindow.Dispose()
I don't regard the above as a solution, but a patch if you've got to get a job done. However, it's almost certain to bite you on the bum at some future point.

Crispin
Posted 14 years ago by Crispin Horsfield - Software developer, Caz Limited
Avatar
As requested I've created a test project which can be found here. I couldn't find any way of uploading it. This location will only be available for a while.

'Out of the box' the application as compiled is freezing out the treeview ToolWindow. It can be re-docked from the menu bar. If it's allowed to float again, then it allows interaction while floating. Then if you close the project (the layout is automatically saved) and re-open it, the floating window is effectively disabled.

Notes
This applet is cut down from a larger application.

The layout is held in a file in the Application Startup folder and is called DockingLayout.xml. If you delete (or rename) the DockingLayout.xml file in the bin folder, it is recreated when the applet closes. Without this file in place, the default load behaviour is to dock all ToolWindows.

The project references a simple DLL that I use for positioning/sizing forms which I've used for ages. You can swap this for your own arrangements.


GDI+
Note the use of the explicit disposal.



Crispin
Posted 14 years ago by Actipro Software Support - Cleveland, OH, USA
Avatar
Crispin,

For the Form locking issue, I think it's a .NET framework issue and may be different that what Boyd was describing. I did this change... in the project you made available, I commented out all the code you had in your _objFormData_evtLoadStateEvent Sub and replaced it with this code:

Dim f As Form = New Form()
f.Owner = Me
f.Show()
All it does is create a regular Form. When I run this, that Form is locked as well. Since our floating tool windows are parented by a Form we create, we are running into the same issue.

You need to load your layout probably a little later in the lifetime of the parent Form. Use the test code above to help determine where.

For the GDI+ error, I did some looking. It's blowing up on a UIControl.CreateGraphics() call, which is really just calling Control.CreateGraphics(). The issue here is that none of the controls have been flagged as either disposing or disposed. However for some reason GDI+ is blowing up.

I'm not sure what we could change in our code here because all the Dispose-related flags indicate that the control is still active. We already are checking those to avoid the CreateGraphics call if the control is being disposed.

I'd recommend this... instead of calling Dispose on your Form or on each tool window, call it on the DockManager instead. Calling DockManager.Dispose clears the entire layout and removes the tool windows. That seemed to get everything working here.


Actipro Software Support

Posted 14 years ago by Crispin Horsfield - Software developer, Caz Limited
Avatar
Thanks for your help.

In response to your 'little later in the lifetime of the parent form', I tried a Me.Show in front of the layout loading statement to no avail. Since the loading of the layout is the last thing I'm invoking, I'm at a bit of a loss to know where to put this layout load command.

I did try and put it on a menu button instead (ie the form has definitely loaded) and that works fine. Do you know of any events that fire when the form has finished painting? Or any other event that indicates that the form has completed its loading?

I've implemented the DockWindow.Dispose() method, but I hope one day to work out what's really going on here.

Crispin
Posted 14 years ago by Actipro Software Support - Cleveland, OH, USA
Avatar
It seems silly that we can't even make a simple Form during the parent Form's Load event and get it to work correctly. Silly Microsoft.

I'd suggest using the Activated event. Make a form-level flag that says whether the initialization has occurred. When Activated fires, if initialization hasn't occurred, load the layout and then set the flag.

I believe I've done that successfully.

[Modified at 05/25/2005 04:55 PM]


Actipro Software Support

Posted 14 years ago by Crispin Horsfield - Software developer, Caz Limited
Avatar
I think I've finally done it! Rather than use the Activate event (which wasn't working for me), I put the load layout call in the Paint event - which comes after the Activate event.

In most applications I find it necessary to have a boolean value to indicate that the form is loading (to stop ComboBox 'clicks' and so forth. This is now reset in the Paint event eg:

  Private Sub Form1_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles MyBase.Paint
        Debug.WriteLine("Form1_Paint")
        If _blnFormLoading = True Then
            Debug.WriteLine("Form painted for the first time")
            voidLoadLayout()
            _blnFormLoading = False
        End If
  End Sub

I agree with your comment about not being able to create a subordinate form within the Form_Load event. I think that there are a few additional events that we could do with. The ones I'd like to see are Closing and/or OnParentClosing events on a UserControl. But then again I expect everyone has their pet requirements.

Thanks again for your help.

Crispin
Posted 14 years ago by Actipro Software Support - Cleveland, OH, USA
Avatar
Crispin,

This all is a false alarm! I was using your test app to test this (and I swore it had worked before). The problem with your test app is that in your Main sub, you didn't do:
Application.Run(_objFrmApplicationGeneratorWin)
If you don't use Application.Run, then windows will not display get their messages to process correctly. After I changed it to that line instead of what you had, everything worked no matter where you load the layout from.

So the point is, everything is fine with UIStudio. You just need to make sure in the Main sub you use Application.Run.


Actipro Software Support

Posted 14 years ago by Crispin Horsfield - Software developer, Caz Limited
Avatar
Whoa! Easy when you know how...

The Application.Run construct is not one I've had to employ before but it works a treat. Also removes the need for Disposing of the DockManager.

I've noticed constructs like it in C#, but since I'm a readonly C# programmer, I didn't really take it in.

Although I took a quick look at the MSDN reference to Application.Run, I still haven't got my head around what it actually does that's different from a form loading in the usual way - VB is probably doing something automatically - but explanations can wait for another day.

Thanks again.

Crispin
Posted 14 years ago by Actipro Software Support - Cleveland, OH, USA
Avatar
Application.Run() handles the Windows message loop for your Form. Without it, your form won't be responsive to user input. If you've done Win32 programming in C, it makes more sense. VB6 always wrapped that sort of stuff for you.

I apologize for not catching that sooner.


Actipro Software Support

Posted 14 years ago by Crispin Horsfield - Software developer, Caz Limited
Avatar
I've done a lot of C programming in the past, but not with Win32. Ta for the tip.

Crispin
Posted 14 years ago by Domenico
Avatar
I have the same problems (locked floating windows after load layout and random GDI+ errors), so I start playing and modifying the VB sample trying to reproduce the situation.
I use .Net framework 1.1, UIStudio 1.5.0037 and XP Pro SP2.
Using the unmodified sample I created a layout with four floating toolwindows: one is the "Welcome to UIStudio" toolwindow, the other three are created from the menu.

Here is what I discovered (start with the unmodified sample):

1) When I load the layout the floating toolwindows aren't locked but the WindowFocused event doesn't fire for them. You have to close and then reactivate the windows from the menu to have the event working.

2) In the DockForm.vb file add the following piece of code to load the layout at startup:

    Private Sub DockForm_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load
        dockManager.LoadToolWindowLayoutFromFile("C:\Program Files\Actipro Software\UIStudio\v1.5.0037\TestApplication-VB\TWLayout.xml")
    End Sub
The result is the same obtained in step 1.

3) In the EntryPoint.vb file change the Application.Run(New DockForm) statement with

                Dim df As DockForm = New DockForm
                df.ShowDialog()
Now the floating windows are locked. You have to close and then reactivate the windows from the menu to have them work again.
When the application closes you may have some random GDI+ errors.

4) Try to load the layout a little later. In the DockForm.vb file remove the event added in step 2 and add this piece of code:

    Private Sub DockForm_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles MyBase.Paint
        Static flag As Boolean = True

        If flag Then
            flag = False
            dockManager.LoadToolWindowLayoutFromFile("C:\Program Files\Actipro Software\UIStudio\v1.5.0037\TestApplication-VB\TWLayout.xml")
        End If
    End Sub
The result is the same obtained in step 1.

5) Ok, I have to use the Application.Run statement to have everything ok. But I can use this statement only in modules (EntryPoint.vb is a module), not in form codes. I tried to change the starting object to the LauncherForm object and to load the DockForm directly from it. I always obtained the same behaviour.

6) Some other bugs I have found:
- If you float a window with the doubleclick method, the WindowFocused event fires for another window, but never for the window you detach.
- If a window has the CanClose property set to False and then you float it, you can see the close button on it's caption. The button is inactive.
- When an autohide window hides, the WindowFocused event fires for it.

Thanks in advance
Andrea Monti and Domenico Betunio
Posted 14 years ago by Actipro Software Support - Cleveland, OH, USA
Avatar
Well any Windows Forms application needs the root Form to be opened with Application.Run or else application message processing will not be correct and therefore you will see odd issues like child Forms locking up. Think of UIStudio simply as adding child Forms with an owner of the parent form of the HostContainerControl. That's all they are. We don't do anything special with them so any behavior like that you can probably duplicate with making a regular Form and adding child Forms with the Owner being the main Form.

The WindowFocused event fires whenever the ToolWindow.OnEnter method is raised by Microsoft's code. So some windows like the markup label won't generally fire it since it doesn't really receive focus. Things like TextBoxes or ListBoxes will fire it though.

As long as the main Form is originally called by Application.Run, I added your DockForm_Load code to our sample's DockForm and it loaded a layout of floating windows without any locks or issues that I can see.

We have another build that we are going to release soon. With that build, I see that double-clicking is firing the WindowFocused event ok, but I thought that was something that was fixed in build 37.

The problem with the close button on a tool window floating form is that once the floating form is created, it's not really possible to show/hide it if you toggle the property on the DockManager due to the way Windows works. So as you noticed, clicking it does nothing if CanClose is false.

Your last item does seem to be a bug.


Actipro Software Support

Posted 14 years ago by Domenico
Avatar
I know that I have to use Application.Run, but this works only if it's used inside a module. In my project I have two forms, the first is a launcher and the second contains the docking code. I cannot close the launcher and return a parameter to a Sub Main (in a module) as you do in your sample. If in this context I use Application.Run I obtain a System.InvalidOperationException, so I must use the ShowDialog. I modify the sample to reflect this situation and I obtain locked floating toolwindows and GDI+ errors. So it's impossible (or not recommended) to load a form with docking code from another form?

In your sample if you create the toolwindows with the menu commands "Create 3 toolwindows..." you obtain three new toolwindows with a RichTextBox inside them. Save the layout, reload it and the RichTextBox is gone. No RichTextBox, no OnEnter and WindowFocused events, I'm right? But I don't understand why if you close the toolwindows and reopen them with the menu commands "Close All ToolWindows" and "Activate All Inactive ToolWindows" the WindowFocused event is back and working again.

Thank you very much
Posted 14 years ago by Crispin Horsfield - Software developer, Caz Limited
Avatar
Domenico

I created a module Main.vb which fires up the main form. Typically this would look like:


Public NotInheritable Class Main

    Private Shared WithEvents _objFormMain As New FormMain
    
    Public Shared Sub Main()
       'Read app.config file here if necessary eg database connection strings
       'These can be made available as shared properties to your app

       Application.Run(_objFormMain)
    End Sub


End Class

You can also invoke splash screens, do preprocessing or whatever before firing up the main form.

HTH

Crispin
Posted 14 years ago by Actipro Software Support - Cleveland, OH, USA
Avatar
Like I said, UIStudio at its core is simply a main Form with several tool window child Forms that have it as Owner. If you want, create a similar scenario just out of empty normal Forms and you should see the same results as what is happening.

Layouts store the positions of tool windows however you still have to create the child controls as necessary for them. In our sample app, we don't have code wired up to reload the RichTextBoxes after a layout load for tool windows that were previously dynamically created. This sort of code should be performed in the WindowInitialize event.

Remember that ToolWindow.Enter kicks off the WindowFocused event. Since there is no child control at that point for those tool windows, the WindowFocused will not fire since the ToolWindow.Enter event is not firing when the tool window interior is clicked. However if you activate a tool window, we programmatically focus the tool window. That's why you see the WindowFocused event fire since ToolWindow.Enter fires in that case.


Actipro Software Support

Posted 14 years ago by Domenico
Avatar
I created a sample project and I understand my mistake.
Thanks to everybody.
The latest build of this product (v2020.1 build 0400) was released 6 days ago, which was after the last post in this thread.

Add Comment

Please log in to a validated account to post comments.