Sign in to follow this  
Followers 0
CMJ

[solved] Delete All TreeView Items activates the On Event of each Item

8 posts in this topic

#1 ·  Posted (edited)

I have a treeview. Each Parent item in the tree has an GuiCtrlOnEvent set. If an item in the tree is selected and I delete all items in the treeview the OnEvent for the each parent item below the selected item will fire.

See the code below for an example of this. To prove it. Run the script and select Delete All. Items are Deleted and all is well. Then select one item in the tree. Now press Delete all. You will see a message box display for each parent item in the tree below your selection (Assuming you did not select an item under the last parent item).

I can think of two ways to resolve this. Either I need to stop the Event from firing in the first place which I would prefer or I need to be able to unselect the item. Neither of these statements work:

_GUICtrlTreeView_SetSelected(<tree>, <hWnd of slected item>, False)

GUICtrlSetState(<hWnd of slected item>, $GUI_NOFOCUS)

Thanks in advance for any help.

cj

EDIT: With the help of rover I have updated this script so that it should work...There are some messages and console logs you will want to remove if you are going to re-use this.

#include <GUIConstantsEx.au3>
#include <GuiTreeView.au3>
#include <GuiImageList.au3>
#include <WindowsConstants.au3>

$Debug_TV = False ; Check ClassName being passed to functions, set to True and use a handle to another control to see it work
Opt("GUIOnEventMode", 1)
Global $hTreeView, $cTreeview
Global $message = "You clicked a parent"
_Main()

Func _Main()

    Local $hItem
    Local $iStyle = BitOR($TVS_HASBUTTONS, $TVS_HASLINES, $TVS_LINESATROOT, $TVS_DISABLEDRAGDROP, $TVS_SHOWSELALWAYS)

    GUICreate("TreeView Delete All", 400, 300)

    $cTreeview = GUICtrlCreateTreeView(2, 2, 396, 225, $iStyle, $WS_EX_CLIENTEDGE)
    $hTreeView = GUICtrlGetHandle(-1)
    GUISetState()

    GUICtrlCreateButton("Delete All", 2, 250, 75, 25)
    GUICtrlSetOnEvent(-1, "DeleteAll")
    GUICtrlCreateButton("Rebuild Tree", 79, 250, 75, 25)
    GUICtrlSetOnEvent(-1, "BuildTree")

    BuildTree()

    ; Loop until user exits
    GUISetOnEvent($GUI_EVENT_CLOSE, 'Close')
    While 1
        Sleep(10)
    WEnd

EndFunc   ;==>_Main

Func DeleteAll()
    $message = "You did not click a parent." & @CRLF & "Why is this showing?"
    Local $hParentItem = _GUICtrlTreeView_GetFirstItem($hTreeView), $hChildItem, $cItem, $iCnt, $Selected = 0
    Do
        $cItem = _GUICtrlTreeView_GetItemParam($hTreeView, $hParentItem)
        ConsoleWrite('! Item Param Data (0 for items added with UDF) = ' & $cItem & @crlf)
        $hParentItem = _GUICtrlTreeView_GetNextSibling($hTreeView, $hParentItem)
        GUICtrlSetOnEvent($cItem, "") ;unregister event
    Until $hParentItem = 0
GUICtrlSendMsg($cTreeView, $TVM_DELETEITEM, $TVI_ROOT, 0)
EndFunc   ;==>DeleteAll

Func What()
Local $test = GUICtrlGetHandle(@GUI_CtrlId)
    MsgBox(0, "Warning", $message & @CRLF & @GUI_CtrlId &@CRLF& $test)

EndFunc   ;==>What

Func BuildTree()
    For $x = 1 To 5
        $hItem = GUICtrlCreateTreeViewItem("Parent - " & $x, $cTreeView)
        GUICtrlSetOnEvent(-1, "What")
        ConsoleWrite('+ Item added ControlID = ' & $hItem & @crlf )
        For $y = 1 To 5
            GUICtrlCreateTreeViewItem("Child - " & $y, $hItem)
        Next
    Next
EndFunc   ;==>BuildTree

Func Close()
    GUIDelete($hTreeView)
    Exit
EndFunc   ;==>Close
Edited by CMJ

Share this post


Link to post
Share on other sites



I tried dirty workaound but it doesn't work :-(

#include <GUIConstantsEx.au3>
#include <GuiTreeView.au3>
#include <GuiImageList.au3>
#include <WindowsConstants.au3>

$Debug_TV = False ; Check ClassName being passed to functions, set to True and use a handle to another control to see it work
Opt("GUIOnEventMode", 1)
Global $hTreeView
Global $message = "You clicked a parent"
Global $b_deleting = False
_Main()

Func _Main()

    Local $hItem
    Local $iStyle = BitOR($TVS_HASBUTTONS, $TVS_HASLINES, $TVS_LINESATROOT, $TVS_DISABLEDRAGDROP, $TVS_SHOWSELALWAYS)

    GUICreate("TreeView Delete All", 400, 300)

    $hTreeView = GUICtrlCreateTreeView(2, 2, 396, 225, $iStyle, $WS_EX_CLIENTEDGE)
    GUISetState()

    GUICtrlCreateButton("Delete All", 2, 250, 75, 25)
    GUICtrlSetOnEvent(-1, "DeleteAll")
    GUICtrlCreateButton("Rebuild Tree", 79, 250, 75, 25)
    GUICtrlSetOnEvent(-1, "BuildTree")

    BuildTree()

    ; Loop until user exits
    GUISetOnEvent($GUI_EVENT_CLOSE, 'Close')
    While 1
        Sleep(10)
    WEnd

EndFunc   ;==>_Main

Func DeleteAll()
    $b_deleting = True
    $message = "You did not click a parent." & @CRLF & "Why is this showing?"
    GUICtrlSendMsg($hTreeView, $TVM_DELETEITEM, $TVI_ROOT, 0)
;~     _GUICtrlTreeView_DeleteAll($hTreeView)
    $b_deleting = False
EndFunc   ;==>DeleteAll

Func What()
    If $b_deleting = True Then Return
    MsgBox(0, "Warning", $message)
EndFunc   ;==>What

Func BuildTree()
    $message = "You clicked a parent"
    For $x = 1 To 5
        $hItem = GUICtrlCreateTreeViewItem("Parent - " & $x, $hTreeView)
        GUICtrlSetOnEvent(-1, "What")
        For $y = 1 To 5
            GUICtrlCreateTreeViewItem("Child - " & $y, $hItem)

        Next
    Next
EndFunc   ;==>BuildTree


Func Close()
    GUIDelete($hTreeView)
    Exit
EndFunc   ;==>Close

Share this post


Link to post
Share on other sites

I had tried that too. Crazy that it does not work. The bad thing is that I figured this out because the What() function in this script represents a Delete item from my database function in my live application so there are times when data just starts disappearing...

Thanks for trying to help.

Share this post


Link to post
Share on other sites

#4 ·  Posted (edited)

You also have a GUICtrlCreateTreeViewItem memory leak as well as the event message queue misfiring.

The misfire event is common with message loop mode as well.

The solution is to call GuiGetMsg() in a Do Until GuiGetMsg() = 0 loop to clear out any events.

or switch between event/loop/event mode.

There are the message queue APIs that might have a clear queue function.

An event function is disabled with GUICtrlSetOnEvent($cItem, "")

but in a modified version of your script below, the misfire is still occurring when the event is unregistered, and the parent item deleted in the same loop the child items are deleted in.

Only by running a second loop to destroy the parent items did the event misfiring stop.

Memory leak:

If you look at the consolewrite in BuildTree() the control IDs returned should be the same

for each build/delete cycle, instead there is an incremental increase of new control IDs * added because the previous ones are not deleted.

You will also see a growing memory leak in taskmgr or process explorer.

* With the original TVM_DELETEITEM message

This is the same issue as a listview with GUICtrlCreateListViewItem and _GUICtrlListView_DeleteAllItems

AutoIt uses the item param data to store a control ID so the GUICtrlCreateTreeViewItem items have to be deleted with GUICtrlDelete().

These control IDs are never deleted, so they increase with each delete/build cycle.

For some reason the treeview delete functions were not coded the same as the listview delete functions (delete items differently by handle or control ID).

To get around using a more complex delete all items function instead of TVM_DELETEITEM, you could

use the _GUICtrlTreeView_Add*** and _GUICtrlTreeView_InsertItem functions

with a WM_NOTIFY message handler for the parent clicks.

Edit: rewording on-the-fly error, missing includes

#include <GUIConstantsEx.au3>
#include <GuiTreeView.au3>
#include <GuiImageList.au3>
#include <WindowsConstants.au3>

$Debug_TV = False ; Check ClassName being passed to functions, set to True and use a handle to another control to see it work
Opt("GUIOnEventMode", 1)
Global $hTreeView, $cTreeview
Global $message = "You clicked a parent"
_Main()

Func _Main()

    Local $hItem
    Local $iStyle = BitOR($TVS_HASBUTTONS, $TVS_HASLINES, $TVS_LINESATROOT, $TVS_DISABLEDRAGDROP, $TVS_SHOWSELALWAYS)

    GUICreate("TreeView Delete All", 400, 300)

    $cTreeview = GUICtrlCreateTreeView(2, 2, 396, 225, $iStyle, $WS_EX_CLIENTEDGE)
    $hTreeView = GUICtrlGetHandle(-1)
    GUISetState()

    GUICtrlCreateButton("Delete All", 2, 250, 75, 25)
    GUICtrlSetOnEvent(-1, "DeleteAll")
    GUICtrlCreateButton("Rebuild Tree", 79, 250, 75, 25)
    GUICtrlSetOnEvent(-1, "BuildTree")

    BuildTree()

    ; Loop until user exits
    GUISetOnEvent($GUI_EVENT_CLOSE, 'Close')
    While 1
        Sleep(10)
    WEnd

EndFunc   ;==>_Main

Func DeleteAll()
    $message = "You did not click a parent." & @CRLF & "Why is this showing?"
    ;GUICtrlSendMsg($cTreeView, $TVM_DELETEITEM, $TVI_ROOT, 0)

    Local $hParentItem = _GUICtrlTreeView_GetFirstItem($hTreeView), $cItem = 0, $hChildItem, $iCnt
    Do
        $hChildItem = _GUICtrlTreeView_GetFirstChild($hTreeView, $hParentItem)
        $iCnt = _GUICtrlTreeView_GetChildCount($hTreeView, $hParentItem)
        For $i = 1 To $iCnt
            $cItem = _GUICtrlTreeView_GetItemParam($hTreeView, $hChildItem)
            $hChildItem = _GUICtrlTreeView_GetNextChild($hTreeView, $hChildItem)
            GUICtrlDelete($cItem) ;delete child
        Next
        $cItem = _GUICtrlTreeView_GetItemParam($hTreeView, $hParentItem)
        ConsoleWrite('! Item Param Data (0 for items added with UDF) = ' & $cItem & @crlf)
        $hParentItem = _GUICtrlTreeView_GetNextSibling($hTreeView, $hParentItem)
        GUICtrlSetOnEvent($cItem, "") ;unregister event
        ;GUICtrlDelete($cItem) ;delete parent
    Until $hParentItem = 0

    $hParentItem = _GUICtrlTreeView_GetFirstItem($hTreeView)
    Do
        $cItem = _GUICtrlTreeView_GetItemParam($hTreeView, $hParentItem)
        $hParentItem = _GUICtrlTreeView_GetNextSibling($hTreeView, $hParentItem)
        GUICtrlDelete($cItem) ;delete parent
    Until $hParentItem = 0

$message = "You clicked a parent"

EndFunc   ;==>DeleteAll

Func What()
    MsgBox(0, "Warning", $message)
EndFunc   ;==>What

Func BuildTree()
    For $x = 1 To 5
        $hItem = GUICtrlCreateTreeViewItem("Parent - " & $x, $cTreeView)
        GUICtrlSetOnEvent(-1, "What")
        ConsoleWrite('+ Item added ControlID = ' & $hItem & @crlf )
        For $y = 1 To 5
            GUICtrlCreateTreeViewItem("Child - " & $y, $hItem)
        Next
    Next
EndFunc   ;==>BuildTree

Func Close()
    GUIDelete($hTreeView)
    Exit
EndFunc   ;==>Close
Edited by rover

I see fascists...

Share this post


Link to post
Share on other sites

#5 ·  Posted (edited)

Thanks rover.

Great info. Only one problem. Your code still triggers the What function...but only one time, so that is definitely progress! After more investigation it appears that this happens because if a child item in the tree is selected and you delete it, the parent item gets the selection and the event for that parent item is run. You can see a modified version of your code below that seems to prove this.

so I guess that means that the best answer is to find a way to deselect an item. This seems simple at first, but none of these functions work:

_GUICtrlTreeView_SetState($hTreeView,$hChildItem,$TVIS_SELECTED,False)

_GUICtrlTreeView_SetSelected($hTreeView,$hChildItem, False)

GUICtrlSetState($$hChildItem, $GUI_NOFOCUS)

Any ideas as to how to either stop the selection from falling back to the parent or to simply de-select the item?

Thanks again for all the help.

Chris

P.S. I had been aware of the memory leak and just could not find an answer for that. Thanks for that too! And I have tried using the _GUICtrlTreeView_Add*** and _GUICtrlTreeView_InsertItem functions but cannot in this case because of some other limitations I have found with those functions. I try to stick with native autoit functions over the treeview UDF in all cases and most of the time it proves to be a good philosophy.

#include
#include
#include
#include

$Debug_TV = False ; Check ClassName being passed to functions, set to True and use a handle to another control to see it work
Opt("GUIOnEventMode", 1)
Global $hTreeView, $cTreeview
Global $message = "You clicked a parent"
_Main()

Func _Main()

Local $hItem
Local $iStyle = BitOR($TVS_HASBUTTONS, $TVS_HASLINES, $TVS_LINESATROOT, $TVS_DISABLEDRAGDROP, $TVS_SHOWSELALWAYS)

GUICreate("TreeView Delete All", 400, 300)

$cTreeview = GUICtrlCreateTreeView(2, 2, 396, 225, $iStyle, $WS_EX_CLIENTEDGE)
$hTreeView = GUICtrlGetHandle(-1)
GUISetState()

GUICtrlCreateButton("Delete All", 2, 250, 75, 25)
GUICtrlSetOnEvent(-1, "DeleteAll")
GUICtrlCreateButton("Rebuild Tree", 79, 250, 75, 25)
GUICtrlSetOnEvent(-1, "BuildTree")

BuildTree()

; Loop until user exits
GUISetOnEvent($GUI_EVENT_CLOSE, 'Close')
While 1
Sleep(10)
WEnd

EndFunc ;==>_Main

Func DeleteAll()
$message = "You did not click a parent." & @CRLF & "Why is this showing?"
;GUICtrlSendMsg($cTreeView, $TVM_DELETEITEM, $TVI_ROOT, 0)

Local $hParentItem = _GUICtrlTreeView_GetFirstItem($hTreeView), $hChildItem, $cItem, $iCnt, $Selected = 0
Do
$hChildItem = _GUICtrlTreeView_GetFirstChild($hTreeView, $hParentItem)
$iCnt = _GUICtrlTreeView_GetChildCount($hTreeView, $hParentItem)
For $i = 1 To $iCnt
$cItem = _GUICtrlTreeView_GetItemParam($hTreeView, $hChildItem)
$hChildItem = _GUICtrlTreeView_GetNextChild($hTreeView, $hChildItem)
If _GUICtrlTreeView_GetSelected($hTreeView, $hChildItem) Then
$Selected = 1
EndIf

GUICtrlDelete($cItem) ;delete child
Next
$cItem = _GUICtrlTreeView_GetItemParam($hTreeView, $hParentItem)
ConsoleWrite('! Item Param Data (0 for items added with UDF) = ' & $cItem & @crlf)
$hParentItem = _GUICtrlTreeView_GetNextSibling($hTreeView, $hParentItem)
GUICtrlSetOnEvent($cItem, "") ;unregister event
MsgBox(0,0,$cItem)
;GUICtrlDelete($cItem) ;delete parent
Until $hParentItem = 0 or $Selected = 1

If $Selected = 1 Then Return

$hParentItem = _GUICtrlTreeView_GetFirstItem($hTreeView)
Do
$cItem = _GUICtrlTreeView_GetItemParam($hTreeView, $hParentItem)
$hParentItem = _GUICtrlTreeView_GetNextSibling($hTreeView, $hParentItem)
ConsoleWrite('--Delete Parent Item = ' & $cItem & @crlf)
GUICtrlDelete($cItem) ;delete parent
Until $hParentItem = 0

;$message = "You clicked a parent"

EndFunc ;==>DeleteAll

Func What()
MsgBox(0, "Warning", $message & @CRLF & @GUI_CtrlId)
EndFunc ;==>What

Func BuildTree()
For $x = 1 To 5
$hItem = GUICtrlCreateTreeViewItem("Parent - " & $x, $cTreeView)
GUICtrlSetOnEvent(-1, "What")
ConsoleWrite('+ Item added ControlID = ' & $hItem & @crlf )
For $y = 1 To 5
GUICtrlCreateTreeViewItem("Child - " & $y, $hItem)
Next
Next
EndFunc ;==>BuildTree

Func Close()
GUIDelete($hTreeView)
Exit
EndFunc ;==>Close
Edited by CMJ

Share this post


Link to post
Share on other sites

Here is an interesting bit of information.

If you add a ContinueLoop step after $Selected = 1 at Line 50 in my previous code you do not get the problem. I am not sure why that would solve it but it does seem to be a work around for now...Still testing.

I would still love to know how to de-select the item but this may at least let me move on.

Share this post


Link to post
Share on other sites

I did end up finding a solution here and it is a little simpler than the original code. Just use this function in place of the DeleteAll() function in either Rover's or my last post.

Thanks again for all the help.

Func DeleteAll()
Local $hParentItem = _GUICtrlTreeView_GetFirstItem($hTreeView), $hChildItem, $cItem, $iCnt, $Selected = 0
Do
$cItem = _GUICtrlTreeView_GetItemParam($hTreeView, $hParentItem)
$hParentItem = _GUICtrlTreeView_GetNextSibling($hTreeView, $hParentItem)
GUICtrlSetOnEvent($cItem, "") ;unregister event
Until $hParentItem = 0
GUICtrlSendMsg($cTreeView, $TVM_DELETEITEM, $TVI_ROOT, 0)
EndFunc ;==>DeleteAll

Share this post


Link to post
Share on other sites

#8 ·  Posted (edited)

I did end up finding a solution here and it is a little simpler than the original code. Just use this function in place of the DeleteAll() function in either Rover's or my last post.

Thanks again for all the help.

Func DeleteAll()
Local $hParentItem = _GUICtrlTreeView_GetFirstItem($hTreeView), $hChildItem, $cItem, $iCnt, $Selected = 0
Do
$cItem = _GUICtrlTreeView_GetItemParam($hTreeView, $hParentItem)
$hParentItem = _GUICtrlTreeView_GetNextSibling($hTreeView, $hParentItem)
GUICtrlSetOnEvent($cItem, "") ;unregister event
Until $hParentItem = 0
GUICtrlSendMsg($cTreeView, $TVM_DELETEITEM, $TVI_ROOT, 0)
EndFunc ;==>DeleteAll

You still have the memory leak from undeleted child item control ids if you use TVM_DELETEITEM.

Might I suggest purging the message queue after all delete item events.

Your code still triggers the What function...but only one time

In my DeleteAll function there were no misfires (only tested on my XP box - 7x64 box being rebuilt)

Setting events for items of a Treeview as if they are controls obviously has issues with false events when items are unregistered/deleted.

Treeview/Listview Item notifications should be processed in a WM_NOTIFY message handler...

Consider the added complication of having to code delete functions to loop through and unregister (item set to run an event mode function) and delete every root and child item.

and then clear the message queue, as opposed to using the existing udf single line delete all or delete all children functions with WM_NOTIFY for event handling.

And I have tried using the _GUICtrlTreeView_Add*** and _GUICtrlTreeView_InsertItem functions but cannot in this case because of some other limitations I have found with those functions.

What problems are you having using the udf functions?

I forgot that _GUICtrlTreeView_GetItemParam returns False instead of 0 when there is no item data (the combobox and listview param functions return 0 for no data).

Try this message queue purge example, no memory leaks and no event misfires.

Edit: clarification.

#include <GUIConstantsEx.au3>
#include <GuiTreeView.au3>
#include <GuiImageList.au3>
#include <WindowsConstants.au3>

$Debug_TV = False ; Check ClassName being passed to functions, set to True and use a handle to another control to see it work
Opt("GUIOnEventMode", 1)
Global $hTreeView, $cTreeview
Global $message = "You clicked a parent"
_Main()

Func _Main()

Local $hItem
Local $iStyle = BitOR($TVS_HASBUTTONS, $TVS_HASLINES, $TVS_LINESATROOT, $TVS_DISABLEDRAGDROP, $TVS_SHOWSELALWAYS)

GUICreate("TreeView Delete All", 400, 300)

$cTreeview = GUICtrlCreateTreeView(2, 2, 396, 225, $iStyle, $WS_EX_CLIENTEDGE)
$hTreeView = GUICtrlGetHandle(-1)
GUISetState()

GUICtrlCreateButton("Delete All", 2, 250, 75, 25)
GUICtrlSetOnEvent(-1, "DeleteAll")
GUICtrlCreateButton("Rebuild Tree", 79, 250, 75, 25)
GUICtrlSetOnEvent(-1, "BuildTree")

BuildTree()

; Loop until user exits
GUISetOnEvent($GUI_EVENT_CLOSE, 'Close')
While 1
Sleep(10)
WEnd

EndFunc ;==>_Main

Func DeleteAll()
$message = "You did not click a parent." & @CRLF & "Why is this showing?"
Local $hParentItem = _GUICtrlTreeView_GetFirstItem($hTreeView), $cItem = 0, $hChildItem
Do
$hChildItem = _GUICtrlTreeView_GetFirstChild($hTreeView, $hParentItem)
Do
$cItem = _GUICtrlTreeView_GetItemParam($hTreeView, $hChildItem)
$hChildItem = _GUICtrlTreeView_GetNextChild($hTreeView, $hChildItem)
;GUICtrlSetOnEvent($cItem, "") ;unregister event
GUICtrlDelete($cItem) ;delete child
Until $hChildItem = 0
$cItem = _GUICtrlTreeView_GetItemParam($hTreeView, $hParentItem)
ConsoleWrite('! Item Param Data (False for items added with UDF) = ' & $cItem & @crlf)
$hParentItem = _GUICtrlTreeView_GetNextSibling($hTreeView, $hParentItem)
GUICtrlSetOnEvent($cItem, "") ;unregister event
GUICtrlDelete($cItem) ;delete parent
Until $hParentItem = 0

;purge message queue
Opt("GUIOnEventMode", 0) ;switch to PeekMessage API (loop mode)
Do
Until GUIGetMsg() = 0 ;clear message queue
Opt("GUIOnEventMode", 1) ;switch back to GetMessage API (event mode)
$message = "You clicked a parent"
EndFunc ;==>DeleteAll

Func What()
MsgBox(0, "Warning", $message)
EndFunc ;==>What

Func BuildTree()
For $x = 1 To 5
$hItem = GUICtrlCreateTreeViewItem("Parent - " & $x, $cTreeView)
GUICtrlSetOnEvent(-1, "What")
ConsoleWrite('+ Item added ControlID = ' & $hItem & @crlf )
For $y = 1 To 5
GUICtrlCreateTreeViewItem("Child - " & $y, $hItem)
Next
_GUICtrlTreeView_SetChildren($cTreeView, $hItem) ;using control id for native items
Next
EndFunc ;==>BuildTree

Func Close()
GUIDelete($hTreeView)
Exit
EndFunc ;==>Close
Edited by rover

I see fascists...

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now
Sign in to follow this  
Followers 0