今回は、AutoIt で作成する、インタラクティブな階層型 ToDoリストの作り方を解説します。
このツールは、単に項目をリスト表示するだけではありません。「家の掃除」のような親タスクをチェックすると、その下にある「リビングの掃除」「窓を拭く」といった子タスクが全てチェックされる、といった親子間のチェック状態が双方向に連動する高度な機能を備えています。
目次
完成したスクリプトの全コード
#AutoIt3Wrapper_AU3Check_Parameters=-w 5
#Region ;**** Directives created by AutoIt3Wrapper_GUI ****
#AutoIt3Wrapper_Outfile_x64=HierarchicalToDoList.exe
#AutoIt3Wrapper_Res_Description=階層型 ToDoリスト
#AutoIt3Wrapper_Res_Fileversion=1.0.0.0
#AutoIt3Wrapper_Res_ProductName=階層型 ToDoリスト
#AutoIt3Wrapper_Res_ProductVersion=1.0.0.0
#AutoIt3Wrapper_Res_CompanyName=©2025 wenbang https://windows-waza.com/
#AutoIt3Wrapper_Res_LegalCopyright=©2025 wenbang https://windows-waza.com/
#AutoIt3Wrapper_Res_LegalTradeMarks=HierarchicalToDoList.exe
#AutoIt3Wrapper_Res_Language=1041
#EndRegion ;**** Directives created by AutoIt3Wrapper_GUI ****
#include <GUIConstantsEx.au3>
#include <GuiTreeView.au3>
#include <TreeViewConstants.au3>
#include <WindowsConstants.au3>
#include <Array.au3>
; TVN_ITEMCHANGEDの定数を手動で定義
Global Const $TVN_ITEMCHANGED = -419
Global $g_hTreeView, $g_bInUpdate = False
Local $hGUI = GUICreate("階層型 ToDoリスト", 400, 400)
Local $idTreeView = GUICtrlCreateTreeView(10, 10, 380, 380, $TVS_CHECKBOXES)
$g_hTreeView = GUICtrlGetHandle($idTreeView)
_PopulateTasks()
GUIRegisterMsg($WM_NOTIFY, "WM_NOTIFY")
GUISetState(@SW_SHOW)
While 1
If GUIGetMsg() = $GUI_EVENT_CLOSE Then ExitLoop
WEnd
Exit
Func _PopulateTasks()
Local $hProject1 = GUICtrlCreateTreeViewItem("家の掃除", $idTreeView)
Local $hTask1_1 = GUICtrlCreateTreeViewItem("リビング", $hProject1)
GUICtrlCreateTreeViewItem("掃除機をかける", $hTask1_1)
GUICtrlCreateTreeViewItem("窓を拭く", $hTask1_1)
Local $hProject2 = GUICtrlCreateTreeViewItem("週末の買い物", $idTreeView)
GUICtrlCreateTreeViewItem("トマト", $hProject2)
GUICtrlCreateTreeViewItem("牛乳", $hProject2)
_GUICtrlTreeView_Expand($idTreeView, $hProject1)
_GUICtrlTreeView_Expand($idTreeView, $hProject2)
EndFunc
Func WM_NOTIFY($hWnd, $iMsg, $wParam, $lParam)
#forceref $hWnd, $iMsg, $wParam
If $g_bInUpdate Then Return $GUI_RUNDEFMSG
Local $tNMHDR = DllStructCreate($tagNMHDR, $lParam)
If DllStructGetData($tNMHDR, "hWndFrom") <> $g_hTreeView Then Return $GUI_RUNDEFMSG
Local $iCode = DllStructGetData($tNMHDR, "Code")
If $iCode = $TVN_ITEMCHANGED Then
$g_bInUpdate = True
Local $tItemChange = DllStructCreate($tagNMTVITEMCHANGE, $lParam)
Local $hItem = DllStructGetData($tItemChange, "hItem")
Local $bIsChecked = _GUICtrlTreeView_GetChecked($idTreeView, $hItem)
_CheckAllChildren($hItem, $bIsChecked)
_SynchronizeParentStates()
$g_bInUpdate = False
EndIf
Return $GUI_RUNDEFMSG
EndFunc
Func _CheckAllChildren($hParent, $bChecked)
Local $hChild = _GUICtrlTreeView_GetFirstChild($idTreeView, $hParent)
While $hChild
_GUICtrlTreeView_SetChecked($idTreeView, $hChild, $bChecked)
_CheckAllChildren($hChild, $bChecked)
$hChild = _GUICtrlTreeView_GetNextSibling($idTreeView, $hChild)
WEnd
EndFunc
Func _SynchronizeParentStates()
Local $aParents = _GUICtrlTreeView_GetParents($idTreeView)
If Not IsArray($aParents) Then Return
For $i = UBound($aParents) - 1 To 0 Step -1
Local $hParent = $aParents[$i]
Local $bAllChildrenChecked = True
Local $hChild = _GUICtrlTreeView_GetFirstChild($idTreeView, $hParent)
While $hChild
If Not _GUICtrlTreeView_GetChecked($idTreeView, $hChild) Then
$bAllChildrenChecked = False
ExitLoop
EndIf
$hChild = _GUICtrlTreeView_GetNextSibling($idTreeView, $hChild)
WEnd
_GUICtrlTreeView_SetChecked($idTreeView, $hParent, $bAllChildrenChecked)
Next
EndFunc
Func _GUICtrlTreeView_GetParents($hWnd)
Local $aReturn[0], $hItem = _GUICtrlTreeView_GetFirstItem($hWnd)
While $hItem
If _GUICtrlTreeView_GetChildren($hWnd, $hItem) Then _ArrayAdd($aReturn, $hItem)
$hItem = _GUICtrlTreeView_GetNextVisible($hWnd, $hItem)
WEnd
If @error Or UBound($aReturn) = 0 Then Return 0
Return $aReturn
EndFunc
コードの詳しい解説
TreeView の作成とイベント処理の準備
- コントロールIDとハンドル:
GUICtrlCreateTreeViewでコントロールを作成すると「コントロールID」が返ってきます。しかし、If DllStructGetData($tNMHDR, "hWndFrom") <> $g_hTreeViewは、Windows が使う「ハンドル」を必要とするため、GUICtrlGetHandleを使ってハンドルを取得し、グローバル変数$g_hTreeViewに格納しています。 WM_NOTIFY: TreeViewのチェックボックスがクリックされたことをリアルタイムで知るには、GUIGetMsgだけでは不十分です。GUIRegisterMsgとWM_NOTIFY関数を組み合わせることで、Windows から送られてくる詳細な通知メッセージを直接傍受し、チェック状態の変化を即座に捉えることができます。
親から子へのチェック反映
_CheckAllChildren関数が、親アイテムの状態変更を、その下にある全ての子や孫に反映させる役割を担います。これは「再帰」というテクニックを使い、ドミノ倒しのように、全ての下位アイテムのチェック状態を親と完全に同じにします。
子から親へのチェック反映
_SynchronizeParentStates関数が、子アイテムの状態変更を、親アイテムに反映させる、このスクリプトで最も重要な処理を行います。
- まず
_GUICtrlTreeView_GetParentsというヘルパー関数を使い、ツリーの中から親となるアイテム(子を持つアイテム)を全てリストアップします。 - リストアップした親アイテムを、階層の深いものから順番にループ処理します。
- ループの中で、各親アイテムが持つ全ての子アイテムのチェック状態を調べます。
- もし全ての子がチェックされていれば、その親アイテムにもチェックを付けます。一つでもチェックされていない子がいれば、親アイテムのチェックを外します。
チェックが変更されるたびに、この全体の同期処理が走ることで、常に親子間のチェック状態が正しく保たれます。
まとめ
このスクリプトは、AutoIt の GUIプログラミングの中でも、特に高度なテクニックをいくつか含んでいます。
WM_NOTIFY を使ったリアルタイムなイベント処理や、親子関係をたどって状態を同期させるロジックは、単なる ToDoリストを超えて、より複雑でインタラクティブなアプリケーションを開発するための素晴らしい基礎となります。

コメント