|< Free Open Study >|| |
You have seen the use of the TreeView form in your add-in in Chapters 2 and 3. In this section, you are simply going to correct a problem that you may or may not have discovered in using the add-in created in Chapter 2 and enhanced in Chapter 3. You may have noticed that if you click the same node in the TreeView twice in a row, it does not respond on the second click. You will review this add-in menu again because there is an apparent design flaw in .NET in such controls as the TreeView, ComboBox, and others in which the user makes a selection of an object.
When you select an item in a TreeView, you use the AfterSelect event to determine which node was clicked. In the MSDN help, this is the event that is prescribed for determining the node that was clicked. The problem arises when you click the same node twice in a row. If the node that you click is already selected, the AfterSelect event will not fire. You might think you can use the Click event, because it fires every time a node is clicked, even if you click one that's already selected. In the Click event you could then check the SelectedNode.Text to determine the selected node. No, I'm sorry—it's only when you click one that's already selected that you can get the correct value from SelectedNode.Text in the Click event! The reason is that the Click event fires before the AfterSelect event, and at the time the Click event fires, the new selection has not yet taken place.
So, you have a paradox. The AfterSelect event won't fire when the same node is clicked twice in a row. However, using the Click event only provides the correct node if you click the same node twice in a row. Hopefully, Microsoft will correct this problem before too much frustration occurs. For now, I'll show you how to work around it. You're going to open MyAddinTest1.sln in order to add code to make it work properly.
Once you have opened the add-in solution, add the code shown in Listing 7-14. In the Windows Form Designer, place a Timer control on the form and leave the default name, Timer1, unchanged. Set the Interval property to 50. This will cause the Timer to interrupt 50 milliseconds after it has been enabled.
Listing 7-14: Making the TreeView Work
Private Sub tvMenu_AfterSelect(ByVal sender As Object, ByVal e As _ System.Windows.Forms.TreeViewEventArgs) Handles tvMenu.AfterSelect Dim i As Integer mbMenuClicked = False CallTVSelection(e.Node.Text) End Sub Shared Sub CallTVSelection(ByVal tvSelText As String) Select Case UCase(tvSelText) Case "SMART DESKTOP" 'ignore root clicks Case "BLOCK COMMENT" Call Connect.BlockComment() Case "UNCOMMENT" Call Connect.BlockUnComment() Case "BLOCK CHANGE" Call Connect.BlockChange() Case "BLOCK DELETE" Call Connect.BlockDelete() Case "PROC ERROR HANDLER" Call Connect.GenLocalErrorTrap() Case "CLONE PROCEDURE" Call Connect.CloneProcedure() Case Else MsgBox("Please click on a Child Node.", _ MsgBoxStyle.Information, "Unknown Request") End Select End Sub Private Sub tvMenu_Click(ByVal sender As Object, _ ByVal e As System.EventArgs) _ Handles tvMenu.Click ' At the time the click event fires, the selection of the just clicked ' node has not yet taken place, so if you try to ' determine the node clicked in the click event, you ' will always find the node which was selected prior ' to the click. To get around this mismatch of event ' firing, we start a timer which will fire in 50ms ' If the AfterSelect event fires, because a new selection ' was made by the user, it will tell the Timer Event to ' ignore the interrupt. mbMenuClicked = True Timer1.Enabled = True End Sub Private Sub Timer1_Tick(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles Timer1.Tick ' If the boolean, mbMenuClicked is True, that means the ' AfterSelect event did not fire, and the Click Event ' has asked us to display the current selection in ' the treeview. Dim s As String If mbMenuClicked Then mbMenuClicked = False If Not tvMenu.SelectedNode Is Nothing Then CallTVSelection(tvMenu.SelectedNode.Text) End If End If Timer1.Enabled = False End Sub
Move the Select Case construct from the AfterSelect event to the new method named CallTVSelection, so that it can be used from two places without duplicating the code. This new method will call the desired processing method based on the SelectedNode.Text that will be passed to it.
Place a module-level Boolean in the declaration section of the form. You can view it in the code for this section, but it will appear as
Private mbNodeClicked As Boolean
Remember that the AfterSelect event won't fire if the node that is clicked by the user is already selected. However, the Click event fires on every click of the TreeView. To get around this mismatch of event firing, enable a Timer to interrupt in 50 milliseconds after the Click event fires. You do this in the Click event so that the Timer event will be informed as to whether the AfterSelect event fired or not. In the Click event the Boolean is set to True, denoting that the Click event has fired. The Timer is also enabled at that time.
If the AfterSelect event now fires, it will set the Boolean to False, denoting that the AfterSelect event has fired and handled the click of the TreeView. This will cause the Timer event to ignore the interrupt because the AfterSelect event has already handled the calling of the new selection. If the AfterSelect event does not fire, this means that the user has clicked the same node twice in a row. When the Timer event fires, the Boolean will still be True (set in the Click event) and the Timer event will make a call to CallTVSelection, at the behest of the Click event.
This may all sound very confusing, but it is actually very simple. The Boolean is a simple semaphore telling the Timer event whether or not the AfterSelect event has fired. If it hasn't, the Timer event has to make the call to CallTVSelection. Regardless of who calls CallTVSelection, it is passed the proper value of SelectedNode.Text.
|< Free Open Study >|| |