Problem with the dispose method in the tab strip

Docking/MDI for Windows Forms Forum

Posted 15 years ago by Adamczak - Developer, AAA Software Enterprises
Avatar
I've been using your TabStripPanel and DockableWindow for a long time. I was trying to convert to use UIStudio and I've run into a blocking issue.

I have created a simple form that has a dock manager on it. I created a single document window, which contains a userControl that has a tab strip on it. I added two tabs, each one contains a single user control for that tab. I put trace messages in the constructors for all of these, in the dispose methods and the onload methods. I create one of these views, and things run ok, here is debug output:

DocumentView constructor: enter
Tab1 constructor: enter
Tab1 constructor: exit
Tab2 constructor: enter
Tab2 constructor: exit
DocumentView.tabStrip1_SelectionChanging: enter
DocumentView.tabStrip1_SelectionChanging: exit
Tab1.OnLoad: enter
Tab1.OnLoad: exit
DocumentView.tabStrip1_SelectionChanged: enter
DocumentView.tabStrip1_SelectionChanged: exit
DocumentView constructor: exit
DocumentView.OnLoad: enter
DocumentView.OnLoad: exit

Notice, the selection changing events fire twice, why?

The blocking issue for me is when I close the document window, all sorts of unexpected things happen. Here's the debug output:

DocumentView.Dispose: enter
Tab1.OnLoad: enter
Tab1.OnLoad: exit
Tab2.Dispose: enter
Tab2.Dispose: exit
DocumentView.tabStrip1_SelectionChanging: enter
DocumentView.tabStrip1_SelectionChanging: exit
DocumentView.tabStrip1_SelectionChanged: enter
DocumentView.tabStrip1_SelectionChanged: exit
Tab1.Dispose: enter
Tab1.Dispose: exit
DocumentView.Dispose: exit

Notice the dispose for the document view enters, then I get a SECOND Tab1.OnLoad event (how is this possible? very bad) Then I get a Tab2.Dispose (that's good), then I get 2 selection changed events (that's bad too), and then I get a Tab1.Dispose event (that's good).

I was expecting to see the Tab2.Dispose and Tab1.Dispose events only. This is causing all sorts of issues for me. Most of my code does something in the OnLoad event for the tab controls that should happen only once. Also, most of my code does something when the tab is changed, but if that event fires when disposing of controls, bad things happen.

I have a sample project that I can zip up and send you to reproduce this behavior.

Comments (8)

Posted 15 years ago by Actipro Software Support - Cleveland, OH, USA
Avatar
Yes, please send over the test project. We're planning on a new maintenance release soon so we can build any appropriate fixes in it for you.


Actipro Software Support

Posted 15 years ago by Adamczak - Developer, AAA Software Enterprises
Avatar
How do I attach a file in the new forums?
Posted 15 years ago by Actipro Software Support - Cleveland, OH, USA
Avatar
The forums don't support attachments so just e-mail it to us.


Actipro Software Support

Posted 15 years ago by Adamczak - Developer, AAA Software Enterprises
Avatar
I've sent it to support@actiprosoftware.com
Posted 15 years ago by Actipro Software Support - Cleveland, OH, USA
Avatar
Ok, couple things here...

First at the topic of your message, you said the selection event fired twice but this is wrong. One is a SelectionChanging event and the other is a SelectionChanged event. So it's just firing once. You also did and "enter" and "exit" in each of those events.

Now to the dispose portion...

As the TabStrip control is disposed, it removes its child controls and therefore when pages are removed, the selection events will fire. I'd recommend detaching from the selection events before you call the base class' Dispose. Then you don't get the selection event messages.

As for the OnLoad issue, here's the stack trace from the Tab1.OnLoad. What's happening is that the TabStrip is being disposed and it goes to dispose Tab2. However after Tab2 is removed, the layout code fires and for some reason internal .NET framework code is recreating Tab1. I've been looking at this for a while and am at a loss as to why it needs to create Tab1 again since Tab1 has not yet been disposed. Any ideas?
   at TabStripTest.Tab1.OnLoad(EventArgs e) in c:\code\test\adamczak\uisdispose\tabstriptest\tab1.cs:line 52
   at System.Windows.Forms.UserControl.OnCreateControl()
   at System.Windows.Forms.Control.CreateControl(Boolean fIgnoreVisible)
   at System.Windows.Forms.Control.CreateControl(Boolean fIgnoreVisible)
   at System.Windows.Forms.Control.CreateControl(Boolean fIgnoreVisible)
   at System.Windows.Forms.Control.CreateControl()
   at System.Windows.Forms.Control.WmShowWindow(Message& m)
   at System.Windows.Forms.Control.WndProc(Message& m)
   at System.Windows.Forms.ScrollableControl.WndProc(Message& m)
   at System.Windows.Forms.ControlNativeWindow.OnMessage(Message& m)
   at System.Windows.Forms.ControlNativeWindow.WndProc(Message& m)
   at System.Windows.Forms.NativeWindow.DebuggableCallback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
   at System.Windows.Forms.UnsafeNativeMethods.IntCreateWindowEx(Int32 dwExStyle, String lpszClassName, String lpszWindowName, Int32 style, Int32 x, Int32 y, Int32 width, Int32 height, IntPtr hWndParent, IntPtr hMenu, IntPtr hInst, Object pvParam)
   at System.Windows.Forms.UnsafeNativeMethods.CreateWindowEx(Int32 dwExStyle, String lpszClassName, String lpszWindowName, Int32 style, Int32 x, Int32 y, Int32 width, Int32 height, IntPtr hWndParent, IntPtr hMenu, IntPtr hInst, Object pvParam)
   at System.Windows.Forms.NativeWindow.CreateHandle(CreateParams cp)
   at System.Windows.Forms.Control.CreateHandle()
   at System.Windows.Forms.Control.get_Handle()
   at System.Windows.Forms.Control.CreateGraphicsInternal()
   at System.Windows.Forms.Control.CreateGraphics()
   at ActiproSoftware.WinUICore.UIControl.UpdateLayout() in C:\Code\ActiproSoftware\WinUICore\Source\WinUICore\UIControl.cs:line 1565
   at ActiproSoftware.WinUICore.UIControl.Invalidate(InvalidationLevels levels, InvalidationTypes types) in C:\Code\ActiproSoftware\WinUICore\Source\WinUICore\UIControl.cs:line 941
   at ActiproSoftware.WinUICore.UIControl.OnLayout(LayoutEventArgs e) in C:\Code\ActiproSoftware\WinUICore\Source\WinUICore\UIControl.cs:line 1152
   at System.Windows.Forms.Control.PerformLayout(Control affectedControl, String affectedProperty)
   at System.Windows.Forms.ControlCollection.Remove(Control value)
   at ActiproSoftware.UIStudio.TabStrip.TabStripControlCollection.RemoveInternal(Control value) in C:\Code\ActiproSoftware\UIStudio\Source\Dock\UIStudio\TabStrip\TabStripControlCollection.cs:line 53
   at ActiproSoftware.UIStudio.TabStrip.TabStripControlCollection.Remove(Control value) in C:\Code\ActiproSoftware\UIStudio\Source\Dock\UIStudio\TabStrip\TabStripControlCollection.cs:line 107
   at System.Windows.Forms.Control.Dispose(Boolean disposing)
   at System.Windows.Forms.ContainerControl.Dispose(Boolean disposing)
   at ActiproSoftware.WinUICore.UIContainerControl.Dispose(Boolean disposing) in C:\Code\ActiproSoftware\WinUICore\Source\WinUICore\UIContainerControl.cs:line 849
   at System.ComponentModel.Component.Dispose()
   at ActiproSoftware.ComponentModel.LogicalTreeNodeCollection.DisposeAllChildObjects() in C:\Code\ActiproSoftware\Shared\Source\ComponentModel\LogicalTreeNodeCollection.cs:line 292
   at ActiproSoftware.WinUICore.UIElementCollection.Dispose(Boolean disposing) in C:\Code\ActiproSoftware\WinUICore\Source\WinUICore\UIElementCollection.cs:line 64
   at ActiproSoftware.ComponentModel.DisposableObject.RaiseDisposedEvent(Boolean disposing) in C:\Code\ActiproSoftware\Shared\Source\ComponentModel\DisposableObject.cs:line 82
   at ActiproSoftware.ComponentModel.DisposableObject.Dispose() in C:\Code\ActiproSoftware\Shared\Source\ComponentModel\DisposableObject.cs:line 95
   at ActiproSoftware.WinUICore.UIControl.Dispose(Boolean disposing) in C:\Code\ActiproSoftware\WinUICore\Source\WinUICore\UIControl.cs:line 838
   at ActiproSoftware.UIStudio.TabStrip.TabStrip.Dispose(Boolean disposing) in C:\Code\ActiproSoftware\UIStudio\Source\Dock\UIStudio\TabStrip\TabStrip.cs:line 906
   at System.ComponentModel.Component.Dispose()
   at System.Windows.Forms.Control.Dispose(Boolean disposing)
   at System.Windows.Forms.ContainerControl.Dispose(Boolean disposing)
   at TabStripTest.DocumentView.Dispose(Boolean disposing) in c:\code\test\adamczak\uisdispose\tabstriptest\documentview.cs:line 55


Actipro Software Support

Posted 15 years ago by Adamczak - Developer, AAA Software Enterprises
Avatar
I can't really help you debug without seeing your source, but my guess is that the .Net dispose code disposes all of the child controls of the tab strip first (which removes the handles). Then your tab strip dispose is called, and that does the remove. Since each remove causes the tab strip to redraw, .Net needs to redraw all the other children (other than the one that was removed), and so it recreates the handles for the, thus causing the onLoad event to occur again. If the tab strip code used a different way to remove the tabs during dispose (like locking redraws or something, and then removing all the pages at once) you would probably not experience this behavior (and the selection changed events would not fire either, which is my other annoyance, since for every control that uses the tab strip, I have to unhook in dispose [I have about 100 of these controls]).

In my opinion (and this is how it worked in the previous version of you tab strip), the disposing of a tab strip should not fire any selection changing events (but I can live with that). The OnLoad event firing again is impossible for me to work around, and is definitely a show stopper for me.
Posted 15 years ago by Actipro Software Support - Cleveland, OH, USA
Avatar
Good news... code changes have been made to TabStrip and NavigationBar to not fire selection events when disposing.

Also, a chnage has been made to WinUICore so that the layout code doesn't fire when disposing.

This fixes both problems. The next maintenance release will contain both.


Actipro Software Support

Posted 15 years ago by Adamczak - Developer, AAA Software Enterprises
Avatar
Thank you for your quick resolution on this. This will be very helpful.
The latest build of this product (v2020.1 build 0400) was released 3 days ago, which was after the last post in this thread.

Add Comment

Please log in to a validated account to post comments.