I am experiencing some strange behavior between a UserControl's validating event and a treeview control. Initially, I thought it was related to an issue in the Knowledgebase article 810852 (http://support.microsoft.com/kb/810852), but then I realized that the hotfix mentioned was in .Net v1.1, which I am using.
I took the sample from that article and recreated the situation I see in my application. (Code included below.)
If you run the enclosed application and initially navigate to Tab2 and in the text box type an "A" (no quotes), then click on the plus sign next to the node labeled root, you get a message box as expected. Click OK in that dialog box and in the results textbox you see that a "before select", and "after select" event fired in the tree control and THEN the validating event in the textbox on Tab2 fired (it displays the word "Cancel"). Click the plus sign again and click ok in the dialog box. This time ONLY the validating event fires, none of the tree view events fire. Now wait, it gets stranger!!
Now close the app and run it again. This time, first click on the plus sign next to the root node. Now go to tab 2 and in the text box type an "A". (no quotes). Now click on the same plus sign next to the root node again. This time only the validating event fires. Click on the plus sign next to either child node. Again, only the validating event fires.
Now my question, if a validating event sets e.cancel to true why would ANY subsequent event handlers fire? Actually looking at the results it seems that in the first test the "before select" and "after select" events fired BEFORE the validating event on tab2, and the only the "before expand" event was inhibited by the e.cancel.
I would think that all validating events would get precedence and other events would not fire until the validating events were complete. And that does indeed seem to be the case on subsequent clicks in the first test case and on ALL clicks in the second test case.
This has just about driven me nuts, (yeah I know it's not a drive it's a short walk). I am using a tree as a navigation element and after the user changes data on the tab pages I attempt to update the database. On constraint errors, I don't want the user to be able to navigate away from the tab. But if the tree events are allowed to fire that is EXACTLY what will happen. The user will be allowed to navigate away from the existing tab and pending changes will be lost.
Can anyone shed some light on this?
Gary Shell
________________
code sample:
Public Class Form1
Inherits System.Windows.Forms.Form
#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
'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
'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 UserControl11 As WindowsApplication1.UserControl1
Friend WithEvents TreeView1 As System.Windows.Forms.TreeView
Public WithEvents Results As System.Windows.Forms.TextBox
<System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()
Me.UserControl11 = New WindowsApplication1.UserControl1
Me.TreeView1 = New System.Windows.Forms.TreeView
Me.Results = New System.Windows.Forms.TextBox
Me.SuspendLayout()
'
'UserControl11
'
Me.UserControl11.Location = New System.Drawing.Point(216, 16)
Me.UserControl11.Name = "UserControl11"
Me.UserControl11.Size = New System.Drawing.Size(320, 240)
Me.UserControl11.TabIndex = 0
'
'TreeView1
'
Me.TreeView1.ImageIndex = -1
Me.TreeView1.Location = New System.Drawing.Point(32, 16)
Me.TreeView1.Name = "TreeView1"
Me.TreeView1.Nodes.AddRange(New System.Windows.Forms.TreeNode() {New System.Windows.Forms.TreeNode("root", New System.Windows.Forms.TreeNode() {New System.Windows.Forms.TreeNode("child 1", New System.Windows.Forms.TreeNode() {New System.Windows.Forms.TreeNode("grandchild1")}), New System.Windows.Forms.TreeNode("child2", New System.Windows.Forms.TreeNode() {New System.Windows.Forms.TreeNode("grandchild 2")})})})
Me.TreeView1.SelectedImageIndex = -1
Me.TreeView1.Size = New System.Drawing.Size(136, 112)
Me.TreeView1.TabIndex = 3
'
'Results
'
Me.Results.Location = New System.Drawing.Point(32, 144)
Me.Results.Multiline = True
Me.Results.Name = "Results"
Me.Results.ScrollBars = System.Windows.Forms.ScrollBars.Vertical
Me.Results.Size = New System.Drawing.Size(136, 104)
Me.Results.TabIndex = 4
Me.Results.Text = ""
'
'Form1
'
Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13)
Me.ClientSize = New System.Drawing.Size(568, 266)
Me.Controls.Add(Me.Results)
Me.Controls.Add(Me.TreeView1)
Me.Controls.Add(Me.UserControl11)
Me.Name = "Form1"
Me.Text = "Form1"
Me.ResumeLayout(False)
End Sub
#End Region
Private Sub TreeView1_BeforeSelect(ByVal sender As Object, ByVal e As System.Windows.Forms.TreeViewCancelEventArgs) Handles TreeView1.BeforeSelect
results.Text = results.Text & "before select" & vbCrLf
End Sub
Private Sub TreeView1_AfterSelect(ByVal sender As Object, ByVal e As System.Windows.Forms.TreeViewEventArgs) Handles TreeView1.AfterSelect
results.Text = results.Text & "after select" & vbCrLf
End Sub
Private Sub TreeView1_BeforeExpand(ByVal sender As Object, ByVal e As System.Windows.Forms.TreeViewCancelEventArgs) Handles TreeView1.BeforeExpand
results.Text = results.Text & "before expand" & vbCrLf
End Sub
End Class
-----------------------------------
Public Class UserControl1
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
'UserControl1 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 TabControl1 As System.Windows.Forms.TabControl
Friend WithEvents TabPage1 As System.Windows.Forms.TabPage
Friend WithEvents TabPage2 As System.Windows.Forms.TabPage
Friend WithEvents UserControl21 As WindowsApplication1.UserControl2
<System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()
Me.TabControl1 = New System.Windows.Forms.TabControl
Me.TabPage1 = New System.Windows.Forms.TabPage
Me.TabPage2 = New System.Windows.Forms.TabPage
Me.UserControl21 = New WindowsApplication1.UserControl2
Me.TabControl1.SuspendLayout()
Me.TabPage2.SuspendLayout()
Me.SuspendLayout()
'
'TabControl1
'
Me.TabControl1.Controls.Add(Me.TabPage1)
Me.TabControl1.Controls.Add(Me.TabPage2)
Me.TabControl1.Location = New System.Drawing.Point(8, 8)
Me.TabControl1.Name = "TabControl1"
Me.TabControl1.SelectedIndex = 0
Me.TabControl1.Size = New System.Drawing.Size(304, 216)
Me.TabControl1.TabIndex = 0
'
'TabPage1
'
Me.TabPage1.Location = New System.Drawing.Point(4, 22)
Me.TabPage1.Name = "TabPage1"
Me.TabPage1.Size = New System.Drawing.Size(296, 190)
Me.TabPage1.TabIndex = 0
Me.TabPage1.Text = "TabPage1"
'
'TabPage2
'
Me.TabPage2.Controls.Add(Me.UserControl21)
Me.TabPage2.Location = New System.Drawing.Point(4, 22)
Me.TabPage2.Name = "TabPage2"
Me.TabPage2.Size = New System.Drawing.Size(296, 190)
Me.TabPage2.TabIndex = 1
Me.TabPage2.Text = "TabPage2"
'
'UserControl21
'
Me.UserControl21.Location = New System.Drawing.Point(24, 24)
Me.UserControl21.Name = "UserControl21"
Me.UserControl21.TabIndex = 0
'
'UserControl1
'
Me.Controls.Add(Me.TabControl1)
Me.Name = "UserControl1"
Me.Size = New System.Drawing.Size(320, 240)
Me.TabControl1.ResumeLayout(False)
Me.TabPage2.ResumeLayout(False)
Me.ResumeLayout(False)
End Sub
#End Region
End Class
------------------------------------
Public Class UserControl2
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 TextBox1 As System.Windows.Forms.TextBox
<System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()
Me.TextBox1 = New System.Windows.Forms.TextBox
Me.SuspendLayout()
'
'TextBox1
'
Me.TextBox1.Location = New System.Drawing.Point(8, 40)
Me.TextBox1.Name = "TextBox1"
Me.TextBox1.Size = New System.Drawing.Size(128, 20)
Me.TextBox1.TabIndex = 0
Me.TextBox1.Text = "TextBox1"
'
'UserControl2
'
Me.Controls.Add(Me.TextBox1)
Me.Name = "UserControl2"
Me.ResumeLayout(False)
End Sub
#End Region
Private Sub UserControl2_Validating(ByVal sender As Object, ByVal e As System.ComponentModel.CancelEventArgs) Handles MyBase.Validating
If LCase(TextBox1.Text) = "a" Then
MessageBox.Show("Invalid entry. Reenter data again.")
e.Cancel = True
Dim myform As Form1
myform = Me.Parent.Parent.Parent.Parent
'the parent is the tab page, its parent is the tab control,
'its parent is the usercontrol and its parent is the form. Whew!
myform.Results.Text = myform.Results.Text & "Cancel" & vbCrLf
Else
e.Cancel = False
End If
End Sub
End Class