VC CTreeCtrlの選択されたアイテムを起点に検索する

 

はじめに

CTreeCtrlのItemを選択されたところから検索したいなと思いました。
イメージとしてはレジストリエディタの検索みたいな感じです。
次へとやると選択されたところから下を探して、親をたどって下まで行ったらまた戻ってくるみたいな。

とまー、ツリーと言えば再帰で書けば楽勝でしょ…
と安直に思ったんです。
そしたら、すごいはまってしまったんですね。

検索するときに使うCTreeCtrlのメソッド

HTREEITEMをCTreeCtrlに渡して値の取得や親子兄弟を取得します。
基本、下の関数だけあれば検索できると思います。

HTREEITEM GetRootItem()
-> ツリーのルートアイテムを取得する
HTREEITEM GetSelectedItem()
-> 現在選択されているアイテムを取得する
CString GetItemText(HTREEITEM)
-> HTREEITEMのラベルを取得する
DWORD_PTR GetItemData(HTREEITEM)
-> HTREEITEMのデータを取得する

HTREEITEM GetChildItem(HTREEITEM)
-> HTREEITEMの子を取得する
HTREEITEM GetNextSiblingItem(HTREEITEM)
-> HTREEITEMの兄弟を取得する
HTREEITEM GetParentItem(HTREEITEM)
-> HTREEITEMの親を取得する

しかし、この親子兄弟関係、1個ずつしか取れないのです。
けっこう癖がありますね…

処理説明

自分と兄弟から下のノードとリーフに対して処理を行う。
終わったら、親の取得を行っていく。
処理対象は親の兄弟から。
以降、じいちゃん、ひいじいちゃんと確認していく。

呼び側

現在選択位置から検索して、S_FALSEだったら再度
ルートから検索するようにする。

HRESULT XXXXX::FindItem(HTREEITEM hItem, CString KeyWord, bool bParentSearch)
{
	HRESULT result = S_FALSE;
	if (hItem == NULL) { return S_FALSE; }

	HTREEITEM hSibling = hItem;
	while (hSibling != NULL)
	{
		//
		// 検索処理などstart
		// ここでは、KeyWordのラベルなら選択状態にする
		// DWORD_PTR dword = m_pTreeCtrl->GetItemData(hSibling);
		CString itemLabel = m_pTreeCtrl->GetItemText(hSibling);
		if (itemLabel == KeyWord && hSibling != m_pTreeCtrl->GetSelectedItem())
		{
			m_pTreeCtrl->SelectItem(hSibling);
			m_pTreeCtrl->DoProperActions(true);
			return S_OK; // <-終わったらS_OKで戻す
		}
		// 検索処理などend
		// 

		// 子を処理する
		HTREEITEM hChild = m_pTreeCtrl->GetChildItem(hSibling);
		if (hChild == m_pTreeCtrl->GetSelectedItem()) { return E_FAIL; }

		result = FindItem(hChild, KeyWord, false);
		if (result != S_FALSE) { return result; }

		// 兄弟を処理する
		hSibling = m_pTreeCtrl->GetNextSiblingItem(hSibling);
		if (hSibling == m_pTreeCtrl->GetSelectedItem()) { return E_FAIL; }
	}

	if (bParentSearch)
	{
		// 自分の親を取得する
		HTREEITEM hParent = m_pTreeCtrl->GetParentItem(hItem);
		while (hParent != NULL)
		{
			HTREEITEM hParentSibling = hParent;
			while (hParentSibling != NULL)
			{
				// 自分の親は処理せず親の弟妹を処理する
				// 兄姉は処理しない
				hParentSibling = m_pTreeCtrl->GetNextSiblingItem(hParentSibling);
				if (hParent == m_pTreeCtrl->GetSelectedItem()) { return E_FAIL; }
				// 末っ子の確認末っ子なら、親探しフラグをたてる
				HTREEITEM terminat = m_pTreeCtrl->GetNextSiblingItem(hParentSibling);
				result = FindItem(hParentSibling, KeyWord, bParentSearch && terminat == NULL);
				if (result != S_FALSE) { return result; }
			}
			// 自分の親のじいちゃんを取得する
			hParent = m_pTreeCtrl->GetParentItem(hParent);
		}
	}

	return result;
}
	HRESULT result = FindItem(m_pTreeCtrl->GetSelectedItem(), KeyWord, true);
	if (result == S_FALSE) 
	{
		result = FindItem(m_pTreeCtrl->GetRootItem(), KeyWord, false);
	}

終わりに

ルートからだけなら、1個目のwhileだけで事足りそうですね。
これに、先代を遡るとややこしくなってきました。
自分がはまったポイントです…
もう少し簡単に書けそうな気がしたのですが、自分はこれが限界の様です…

 

Follow me!

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です