|
An ordinary VB developer shares his own successes and failures |
|
| Home News Articles Resources Tips Downloads About me | ||
What's that "Windows Form Designer generated code" anyway?Taking a closer look at the code generated by the Windows Forms Designer and understanding some of the important implications.A couple of days ago, I've got an email from a fellow developer with a rather interesting question concerning the Visual Studio .NET Windows Forms designer. The guy asked about the meaning of thecomponents member generated by
the designer and why it is not used in most of the Forms he
designed.
The
#Region " Windows Form Designer generated code "
...
'Form overrides dispose to clean up the component list.
Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
If disposing Then
If Not (components Is Nothing) Then
components.Dispose()
End If
End If
MyBase.Dispose(disposing)
End Sub
'Required by the Windows Form Designer
Private components As System.ComponentModel.IContainer
...
#End Region
The code and the comments indicate that the components
member is used by the Form to hold the list of components in
order to be able to dispose the components as part of the
Form.Dispose call.
I've checked the designer-generated code in all my .NET
Windows Forms projects and I've realized that the
You might recall that a component is a class that implements
the
#Region " Windows Form Designer generated code "
...
<System.Diagnostics.DebuggerStepThrough()> _
Private Sub InitializeComponent()
Me.components = New System.ComponentModel.Container
...
Me.ImageList1 = _
New System.Windows.Forms.ImageList(Me.components)
...
#End Region
Within the New(ByVal c As IContainer) constructor, the
component adds itself to the container by calling the
IContainer.Add method. Within the Form.Dispose method
the components.Dispose method is called ensuring that all
the resources held by the Form's components are correctly
released.
At this point it started to make sense, although I still
wondered how are components without the System.Windows.Forms.ColumnHeader System.Windows.Forms.DataGridTableStyle System.Windows.Forms.ColorDialog System.Windows.Forms.FontDialog System.Windows.Forms.OpenFileDialog System.Windows.Forms.SaveFileDialog ...Regarding their "dispose" behavior, the classes can be roughly divided into two groups:
The classes in the first group are always contained within a
parent component (or control), so they are disposed along
with their container. For instance
The classes in the second group (represented by the
In order to verify the above-mentioned disposable behavior,
I've created a Windows Application project implementing two
components (you can download the solution
here).
The
Public Class SimpleComponent
Inherits System.ComponentModel.Component
Public Sub New()
MyBase.New()
End Sub
Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
Debug.WriteLine(String.Format("{0}({1})", Me.GetType().FullName, disposing))
MyBase.Dispose(disposing)
End Sub
End Class
Public Class ContainedComponent
Inherits System.ComponentModel.Component
Public Sub New()
MyBase.New()
End Sub
Public Sub New(ByVal Container As System.ComponentModel.IContainer)
Me.New()
Container.Add(Me)
End Sub
Protected Overloads Overrides Sub Dispose( _
ByVal disposing As Boolean)
Debug.WriteLine(String.Format("{0}({1})", Me.GetType().FullName, disposing))
MyBase.Dispose(disposing)
End Sub
End Class
After recompiling the solution, I was able to add the two
components to the Visual Studio .NET toolbox and I've
dropped both of the components onto a DialogForm form
class. Here is the instantiation code that the Windows Forms
designer generated for the two components:
#Region " Windows Form Designer generated code " ... Me.components = New System.ComponentModel.Container Me.SimpleComponent1 = New DisposableComponents.SimpleComponent Me.ContainedComponent2 = New DisposableComponents.ContainedComponent(Me.components) ... #End RegionBecause the ContainedComponent class exposes the
New(IContainer) constructor, the designer generated code
to call this constructor instead of the default one. In the
constructor, the ContainedComponent instance adds itself
to the Form.components container, so it is automatically
disposed when the Form's Dispose(Boolean) method is
called.
In contrast, the
In the application's main form, the following code is used
to display the
Private Sub ShowDialogButton_Click(...)
Dim Dialog As DialogForm
Try
Dialog = New DialogForm
Dialog.ShowDialog(Me)
Catch ex As Exception
Trace.WriteLine(ex.ToString())
MsgBox(ex.Message, MsgBoxStyle.Exclamation)
Finally
If Not Dialog Is Nothing Then
Dialog.Dispose()
End If
End Try
End Sub
When you run the application and click the
ShowDialogButton button, the DialogForm instance is
displayed. If you dismiss the dialog, the
DialogForm.Dispose method is called and you'll see in the
Output window that the ContainedComponent.Dispose method
has been called as well.
In contrast, the
After looking at how the Windows Forms designer handles
components, I think it might be useful to have a quick look
at how
Every Windows Forms control derives from the
Public ReadOnly Property Controls() As ControlCollectionThe implementation of the Control.Dispose(Boolean) is
overridden in such a way that it iterates through the
Controls collection calling the Dispose method
for each member of the collection. The Windows Forms
Designer "knows" this and the code it generates for
Control-derived classes always adds controls to the
Control.Controls collection that the Form class inherits
from the Control class:
#Region " Windows Form Designer generated code " ... <System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent() Me.TextBox1 = New System.Windows.Forms.TextBox ... Me.Controls.Add(Me.TextBox1) End Sub ... #End RegionThis way, when the Form is being disposed, the contained
controls are disposed automatically as well.
This leads me to an important recommendation:
If you've been designing a component that allocates UNMANAGED resources, please double check that you: 1. Implement the specificPublic New(IContainer)
constructor and add your component instance to the
container by calling the IContainer.Add(Me) method in the
constructor. (If you create your component using the Visual
Studio .NET 'Component Class' template, it generates the
specific constructor for you properly.)
2.
Implement the |
||
|
|
||
| ©2003-2008 Palo Mraz. All Rights Reserved. See my 'new browser window' policy | ||