
To Override Or To Handle?Windows Forms controls expose many methods and events that can be used to extend the functionality of an object but what is the best method, overriding a virtual method or adding a handler to an event? Obviously, if you use a stock component such as a button or label and you want to add functionality in your application that responds to an event such as a Paint or Click then the choice is clear, you simply add a handler to the event provided and create the code that performs the work in some method of your class. If, on the other hand, you create a class of your own derived from some other control, you have a potential choice, to override one of the protected methods or to add a handler to the class events. Lets look at the possibilities. The following listing shows a simple derived UserControl which does something whenever the size of the control changes. In the first instance, the SizeChanged event is handled and in the second instance the OnSizeChanged virtual method is overidden. These user controls were built with Visual Studio and the handlers added to the control using the handler list in the editor. Show the handled control in C#
using
System;
using System.Collections; using System.ComponentModel; using System.Drawing; using System.Data; using System.Windows.Forms;
namespace OverrideOrHandleExample { /// <summary> /// Summary description for UserControlH. /// </summary> public class UserControlH : System.Windows.Forms.UserControl { private System.Windows.Forms.Panel panel1; private System.Windows.Forms.Panel panel2; /// <summary> /// Required designer variable. /// </summary> private System.ComponentModel.Container components = null;
public UserControlH() { // This call is required by the Windows.Forms Form Designer. InitializeComponent();
// TODO: Add any initialization after the InitializeComponent call
}
/// <summary> /// Clean up any resources being used. /// </summary> protected override void Dispose( bool disposing ) { if( disposing ) { if(components != null) { components.Dispose(); } } base.Dispose( disposing ); }
#region Component Designer generated code /// <summary> /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// </summary> private void InitializeComponent() { this.panel1 = new System.Windows.Forms.Panel(); this.panel2 = new System.Windows.Forms.Panel(); this.SuspendLayout(); // // panel1 // this.panel1.BackColor = System.Drawing.Color.FromArgb(((System.Byte)(192)), ((System.Byte)(0)), ((System.Byte)(0))); this.panel1.Location = new System.Drawing.Point(16, 16); this.panel1.Name = "panel1"; this.panel1.Size = new System.Drawing.Size(96, 64); this.panel1.TabIndex = 0; // // panel2 // this.panel2.BackColor = System.Drawing.Color.FromArgb(((System.Byte)(255)), ((System.Byte)(128)), ((System.Byte)(0))); this.panel2.Location = new System.Drawing.Point(16, 88); this.panel2.Name = "panel2"; this.panel2.Size = new System.Drawing.Size(96, 72); this.panel2.TabIndex = 1; // // UserControlH // this.Controls.Add(this.panel2); this.Controls.Add(this.panel1); this.Name = "UserControlH"; this.Size = new System.Drawing.Size(136, 176); this.SizeChanged += new System.EventHandler(this.UserControlH_SizeChanged); this.ResumeLayout(false);
} #endregion
private void UserControlH_SizeChanged(object sender, System.EventArgs e) { this.panel1.Location=new Point(0,0); this.panel1.Size=new Size(this.Width,this.Height/2); this.panel2.Location=new Point(0,this.Height/2); this.panel2.Size=new Size(this.Width,this.Height/2); } } } Show the handled control in VB
Public
Class UserControlH
Inherits System.Windows.Forms.UserControl
#Region " Windows Form Designer generated code "
Public Sub New() MyBase.New()
'This call is required by the Windows Form Designer. InitializeComponent()
'Add any initialization after the InitializeComponent() call
End Sub
'UserControl 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
'NOTE: The following procedure is required by the Windows Form Designer 'It can be modified using the Windows Form Designer. 'Do not modify it using the code editor. Friend WithEvents Panel1 As System.Windows.Forms.Panel Friend WithEvents Panel2 As System.Windows.Forms.Panel <System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent() Me.Panel1 = New System.Windows.Forms.Panel Me.Panel2 = New System.Windows.Forms.Panel Me.SuspendLayout() ' 'Panel1 ' Me.Panel1.BackColor = System.Drawing.Color.Aqua Me.Panel1.Location = New System.Drawing.Point(40, 8) Me.Panel1.Name = "Panel1" Me.Panel1.Size = New System.Drawing.Size(96, 48) Me.Panel1.TabIndex = 0 ' 'Panel2 ' Me.Panel2.BackColor = System.Drawing.Color.Lime Me.Panel2.Location = New System.Drawing.Point(40, 80) Me.Panel2.Name = "Panel2" Me.Panel2.Size = New System.Drawing.Size(96, 48) Me.Panel2.TabIndex = 0 ' 'UserControlH ' Me.Controls.Add(Me.Panel1) Me.Controls.Add(Me.Panel2) Me.Name = "UserControlH" Me.Size = New System.Drawing.Size(176, 192) Me.ResumeLayout(False)
End Sub
#End Region
Private Sub UserControlH_SizeChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.SizeChanged Me.Panel1.Location = New Point(0, 0) Me.Panel1.Size = New Size(Me.Width, Me.Height / 2) Me.Panel2.Location = New Point(0, Me.Height / 2) Me.Panel2.Size = New Size(Me.Width, Me.Height / 2) End Sub End Class Show the overridden control in C# using System; using System.Collections; using System.ComponentModel; using System.Drawing; using System.Data; using System.Windows.Forms;
namespace OverrideOrHandleExample { /// <summary> /// Summary description for UserControlO. /// </summary> public class UserControlO : System.Windows.Forms.UserControl { private System.Windows.Forms.Panel panel1; private System.Windows.Forms.Panel panel2; /// <summary> /// Required designer variable. /// </summary> private System.ComponentModel.Container components = null;
public UserControlO() { // This call is required by the Windows.Forms Form Designer. InitializeComponent();
// TODO: Add any initialization after the InitializeComponent call
}
/// <summary> /// Clean up any resources being used. /// </summary> protected override void Dispose( bool disposing ) { if( disposing ) { if(components != null) { components.Dispose(); } } base.Dispose( disposing ); }
#region Component Designer generated code /// <summary> /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// </summary> private void InitializeComponent() { this.panel1 = new System.Windows.Forms.Panel(); this.panel2 = new System.Windows.Forms.Panel(); this.SuspendLayout(); // // panel1 // this.panel1.BackColor = System.Drawing.Color.FromArgb(((System.Byte)(0)), ((System.Byte)(192)), ((System.Byte)(192))); this.panel1.Location = new System.Drawing.Point(8, 8); this.panel1.Name = "panel1"; this.panel1.Size = new System.Drawing.Size(96, 72); this.panel1.TabIndex = 0; // // panel2 // this.panel2.BackColor = System.Drawing.Color.FromArgb(((System.Byte)(128)), ((System.Byte)(128)), ((System.Byte)(255))); this.panel2.Location = new System.Drawing.Point(8, 96); this.panel2.Name = "panel2"; this.panel2.Size = new System.Drawing.Size(96, 72); this.panel2.TabIndex = 0; // // UserControlO // this.Controls.Add(this.panel1); this.Controls.Add(this.panel2); this.Name = "UserControlO"; this.Size = new System.Drawing.Size(112, 176); this.ResumeLayout(false);
} #endregion
protected override void OnSizeChanged(EventArgs e) { this.panel1.Location=new Point(0,0); this.panel1.Size=new Size(this.Width,this.Height/2); this.panel2.Location=new Point(0,this.Height/2); this.panel2.Size=new Size(this.Width,this.Height/2); base.OnSizeChanged (e); }
} } Show the overridden control in VB
Public
Class UserControlO
Inherits System.Windows.Forms.UserControl
#Region " Windows Form Designer generated code "
Public Sub New() MyBase.New()
'This call is required by the Windows Form Designer. InitializeComponent()
'Add any initialization after the InitializeComponent() call
End Sub
'UserControl 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
'NOTE: The following procedure is required by the Windows Form Designer 'It can be modified using the Windows Form Designer. 'Do not modify it using the code editor. Friend WithEvents Panel1 As System.Windows.Forms.Panel Friend WithEvents Panel2 As System.Windows.Forms.Panel <System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent() Me.Panel1 = New System.Windows.Forms.Panel Me.Panel2 = New System.Windows.Forms.Panel Me.SuspendLayout() ' 'Panel1 ' Me.Panel1.BackColor = System.Drawing.Color.FromArgb(CType(64, Byte), CType(64, Byte), CType(64, Byte)) Me.Panel1.Location = New System.Drawing.Point(8, 8) Me.Panel1.Name = "Panel1" Me.Panel1.Size = New System.Drawing.Size(128, 64) Me.Panel1.TabIndex = 0 ' 'Panel2 ' Me.Panel2.BackColor = System.Drawing.Color.FromArgb(CType(192, Byte), CType(255, Byte), CType(192, Byte)) Me.Panel2.Location = New System.Drawing.Point(8, 88) Me.Panel2.Name = "Panel2" Me.Panel2.Size = New System.Drawing.Size(128, 56) Me.Panel2.TabIndex = 1 ' 'UserControlO ' Me.Controls.Add(Me.Panel2) Me.Controls.Add(Me.Panel1) Me.Name = "UserControlO" Me.ResumeLayout(False)
End Sub
#End Region
Protected Overrides Sub OnSizeChanged(ByVal e As System.EventArgs) Me.Panel1.Location = New Point(0, 0) Me.Panel1.Size = New Size(Me.Width, Me.Height / 2) Me.Panel2.Location = New Point(0, Me.Height / 2) Me.Panel2.Size = New Size(Me.Width, Me.Height / 2) MyBase.OnSizeChanged(e); End Sub End Class Both incarnations of this user control simply resize their contained panels to cover 50% of the control height whenever they are resized. They both do exactly what they should and so in this case, either method is as good. However, a problem arises when the handled versions of the controls need to have their own behaviour modified. The code generated by Visual Studio has the InitializeComponent method and the SizeChanged event handler marked with private access. You cannot override InitializeComponent to remove the addition of the event handler, you can't specify the event-handler method to remove it from the list of events and you can't override the event handler itself so, if you ever wanted to modify the control, for example to change the way the panel layout was arranged, the original behaviour would still remain in place. Adding another event handler is of course possible but remember that events can have more than one handler tied to them and they are all called in order. For this simple demonstration, the overhead of laying out the panels twice wouldn't matter but if the process were complex or time-consuming because it relied on some input-output function, the overhead could mean a real performance hit for your control. Generally, if you are deriving from a control, always override. A control should never handle it's own events because to do so can seriously effect the rules of object-orientation. |