【AutoIt】親子でチェックが連動する階層型ToDoリスト

今回は、AutoIt で作成する、インタラクティブな階層型 ToDoリストの作り方を解説します。

このツールは、単に項目をリスト表示するだけではありません。「家の掃除」のような親タスクをチェックすると、その下にある「リビングの掃除」「窓を拭く」といった子タスクが全てチェックされる、といった親子間のチェック状態が双方向に連動する高度な機能を備えています。

階層型 ToDoリスト
階層型 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だけでは不十分です。GUIRegisterMsgWM_NOTIFY関数を組み合わせることで、Windows から送られてくる詳細な通知メッセージを直接傍受し、チェック状態の変化を即座に捉えることができます。

親から子へのチェック反映

_CheckAllChildren関数が、親アイテムの状態変更を、その下にある全ての子や孫に反映させる役割を担います。これは「再帰」というテクニックを使い、ドミノ倒しのように、全ての下位アイテムのチェック状態を親と完全に同じにします。

子から親へのチェック反映

_SynchronizeParentStates関数が、子アイテムの状態変更を、親アイテムに反映させる、このスクリプトで最も重要な処理を行います。

  1. まず_GUICtrlTreeView_GetParentsというヘルパー関数を使い、ツリーの中から親となるアイテム(子を持つアイテム)を全てリストアップします。
  2. リストアップした親アイテムを、階層の深いものから順番にループ処理します。
  3. ループの中で、各親アイテムが持つ全ての子アイテムのチェック状態を調べます。
  4. もし全ての子がチェックされていれば、その親アイテムにもチェックを付けます。一つでもチェックされていない子がいれば、親アイテムのチェックを外します。

チェックが変更されるたびに、この全体の同期処理が走ることで、常に親子間のチェック状態が正しく保たれます。

まとめ

このスクリプトは、AutoIt の GUIプログラミングの中でも、特に高度なテクニックをいくつか含んでいます。

WM_NOTIFY を使ったリアルタイムなイベント処理や、親子関係をたどって状態を同期させるロジックは、単なる ToDoリストを超えて、より複雑でインタラクティブなアプリケーションを開発するための素晴らしい基礎となります。

  • URLをコピーしました!
  • URLをコピーしました!

コメント

コメントする

CAPTCHA


目次