Conditionally prevent selection changes in TreeView controlIt may be desirable to prevent a user from changing the currently selected node in a TreeView control. Unfortunately, the NodeClick and Click events don't provide a cancel mechanism. The way to work around this is through subclassing the TreeView control. Brad Martinez compiled an excellent sample project on how to subclass the TreeView control. You can download the TVEventCancel project here. Unfortunately while the TVEventCancel project covers how to outright prevent a selection change, it does not cover conditionally canceling that change. An example where this may be helpful is if you wish to prompt the user if they wish to save changes before allowing a node change. While this may not seem like a big deal, the TreeView control is actually a superclassed version of the treeview common control in Comctl32.dll which causes it to behave somewhat strangely at times. This is one of those times. Conditionally canceling the selection becomes somewhat more complex because the TreeView actually sends two TVN_SELCHANGING messages when the mouse is used to select a new node. One action is type "Unknown" (0) while the other is type "Mouse" (1). When using the keyboard to change selections, only a single "Keyboard" (2) message is sent. In order to prevent the selection change, all of these messages must be canceled by having your custom WindowProc function return 1. However, a conditional statement with a user prompt (e.g. MsgBox function) will be raised twice by any mouse selection. To work around this you can set a flag to prevent changes on the first Unknown action. An example replacement WindowProc follows:
'
' Based on TVEventCancel code by Brad Martinez ' http://btmtz.mvps.org/treeview/ ' ' The NMHDR structure contains information about a notification ' message. The pointer to this structure is specified as the lParam ' member of the WM_NOTIFY message. Private Type NMHDR hwndFrom As Long ' Window handle of control sending message idFrom As Long ' Identifier of control sending message code As Long ' Specifies the notification code End Type Private Type NMTREEVIEW ' was NM_TREEVIEW hdr As NMHDR ' The 'action' member specifies a notification-specific action ' flag. Is NMTREEVIEW_action for TVN_SELCHANGING, TVN_SELCHANGED, ' TVN_SETDISPINFO. Is TVM_EXPAND_wParam for TVN_ITEMEXPANDING, ' TVN_ITEMEXPANDED action As Long itemOld As TVITEM itemNew As TVITEM ptDrag As POINTAPI End Type Private blnAllowNodeChange as Boolean ' a flag that allows the current ' node selection to change, ' necessary because of duplicate ' actions (0 and 1) when ' changing nodes conditionally. Private blnChanged as Boolean ' a flag set elsewhere in the ' application to indicate data ' has changed and has not yet been ' saved for the purposes of this ' example Private lngOldWinProc as Long ' address of original WinProc Public Function WindowProc(ByVal hwnd As Long, ByVal uMsg As Long, _ ByVal wParam As Long, ByVal lParam As Long) As Long Dim bytAction As Long Dim lngReturn As Long Dim nmHeader As NMHDR Dim nmTree As NMTREEVIEW Const USE_OLD_PROC As Byte = 0 Const USE_TV_SUB_PROC As Byte = 1 Const USE_TV_SUB_PROC_2 As Byte = 2 Select Case uMsg Case WM_NOTIFY ' handle treeview notify messages If TreeView1.hwnd Then ' Fill the NMHDR struct from the lParam pointer. ' (for any WM_NOTIFY msg, lParam always points to a struct ' which is either the NMHDR struct, or whose 1st member is ' the NMHDR struct) MoveMemory nmHeader, ByVal lParam, Len(nmHeader) ' The actual notification message is in the code member of ' the NMHDR struct. Select Case nmHeader.code ' The current selection is about to change. Return TRUE ' to prevent the selection from changing. Case TVN_SELCHANGING If blnAllowNodeChange = False Then ' action was previously cancelled ' there's a bogus second action that has to be captured ' and handled bytAction = USE_TV_SUB_PROC_2 Else ' lParam points to the NMTREEVIEW struct, fill it. MoveMemory nmTree, ByVal lParam, Len(nmTree) ' ' THIS IS A SAMPLE CONDITIONAL STATEMENT ' If blnChanged Then ' data has changed Select Case MsgBox("The data for the current " _ & "selection have changed." & vbCr _ & "Do you want to save before proceeding?", _ vbQuestion + vbYesNoCancel, "Save") Case vbYes ' save settings ' execute some save procedure here ' clear changed flag blnChanged = False ' proceed with normal treeview msg handling bytAction = USE_OLD_PROC Case vbCancel If nmTree.action = _ NMTREEVIEW_action.TVC_BYKEYBOARD Then ' only one msg sent by keyboard selection bytAction = USE_TV_SUB_PROC_2 Else ' use the custom procedure to prevent ' selection change bytAction = USE_TV_SUB_PROC End If Case Else ' no button pressed ' don't save, clear flag blnChanged = False ' proceed with standard treeview handling bytAction = USE_OLD_PROC End Select Else ' data has not changed, proceed with standard ' treeview message handling bytAction = USE_OLD_PROC End If End If Case Else ' a non-subclassed treeview message bytAction = USE_OLD_PROC End Select End If Case Else ' a non-subclassed message bytAction = USE_OLD_PROC End Select ' determine what we're supposed to be doing Select Case bytAction Case USE_TV_SUB_PROC ' this is the first event to fire when a node is clicked ' If nmTree.itemNew.hItem has a value, then the item is ' being selected. If nmTree.itemNew.hItem Then ' set flag to prevent secondary action from changing nodes blnAllowNodeChange = False ' Return 1 to cancel the selection WindowProc = 1 End If Case USE_TV_SUB_PROC_2 ' this is a second action which fires when a node is clicked ' with the mouse and the user is prompted with a conditional ' prompt... no traceable source of this message... ' allow node changes again blnAllowNodeChange = True ' Return 1 to cancel the selection WindowProc = 1 Case Else ' proceed normally WindowProc = CallWindowProc(lngOldWinProc, hwnd, uMsg, _ wParam, lParam) End Select End Function Author: ASAK Created: Nov 25 2005 Categories: Visual Basic TechByte #92 Warning: By visiting this site and/or by using any information contained herein, you agree to the Techbytes.ca terms of use. Add a comment about this TechByteIf you wish to add a comment regarding this TechByte, please use the form below. Please note that by submitting comments using this form you are allowing all of the information submitted to be visible on this website. Any comments submitted using this form will only be shown on the website if they are approved by the administrators of this site. IF APPROVED, COMMENTS MAY TAKE SEVERAL DAYS TO BE POSTED. Other TechBytes: |
|

