WTLのタブ型ビューでタブを下側に配置する

tabview01.gif
最新のWTLではタブ型ビューがサポートされ、(MFCや.NET Frameworkなどにはまだまだ追いつけないものの)少しだけUIの幅が広がった。

新しいタブ型ビューではタブのドラッグにも対応するなど、使える機能を備えたものだ。しかし残念ながらタブを下側に配置する機能は用意されていなかった。ここではWTLのCTabView派生クラスを作ることでタブの下側表示に対応させた。

利用するときは、それまでCTabView型だった変数宣言をCDnpTabViewに変更して、タブを下に配置したいときにCDnpTabView::SwitchTabType(CDnpTabView::TAB_DOWN);を呼ぶ。逆にCDnpTabView::SwitchTabType(CDnpTabView::TAB_UP);を呼べば通常通りの上側表示に戻る。

サンプルプロジェクトでは「バージョン表示」用のメニューやボタンを操作したときにタブ位置が上側と下側に交互に変わるようにした。

(CTabViewImpl<>派生で作ればstatic_castを利用してもっとスマートに実装できたことに後から気付き後悔中。気が向いたときに作り直します)

依存環境:ATL/WTL
#pragma once

#include "atlctrlx.h"

///
///\brief
///	WTLのタブビューの下側タブ対応版
///
///\attention
///WTL 8.0での動作を前提として作られています。今後のWTLバージョンアップによっては
///使えなくなる可能性があります。
///
class	CDnpTabView	: public CTabView
{
public:


	///
	///\brief
	///	タブ配置位置指定用列挙子
	///
	enum	TAB_POSITION
	{
		TAB_UP,			//!< タブを上側に配置する
		TAB_DOWN		//!< タブを下側に配置する
	};

private:

	TAB_POSITION	_enumUpTabOrDown;		//!< 現在設定されているタブ配置位置

public:
	CDnpTabView()
	{
		_enumUpTabOrDown = TAB_UP;
	}

	BEGIN_MSG_MAP(CDnpTabView)
		MESSAGE_HANDLER(WM_CREATE, OnCreate)
		MESSAGE_HANDLER(WM_SIZE, OnSize)
		NOTIFY_HANDLER(m_nTabID, TCN_SELCHANGE, OnTabChanged)
		CHAIN_MSG_MAP(CTabView)
	ALT_MSG_MAP(1)	//タブコントロール
		MESSAGE_HANDLER(WM_WINDOWPOSCHANGING, OnWindowPosChanging)
		CHAIN_MSG_MAP_ALT(CTabView,1)
	END_MSG_MAP()




	///
	///\brief
	///	タブ配置位置の動的変更
	///
	///\param	enumType
	///	新しいタブの配置位置\n
	///	CDnpTabView::TAB_UP と CDnpTabView::TAB_DOWN のみサポート
	///
	void	SwitchTabType(TAB_POSITION enumType)
	{
		if(_enumUpTabOrDown == enumType)
			return;

		switch(_enumUpTabOrDown)
		{
		case	TAB_UP:
			m_tab.ModifyStyle(TCS_BOTTOM,0);
			break;

		case	TAB_DOWN:
			m_tab.ModifyStyle(0,TCS_BOTTOM);
			break;

		default:
			ATLASSERT(0);		//対応していない配置位置指定された
			return;
		}

		_enumUpTabOrDown = enumType;

		UpdateLayout();		//このクラスのUpdateLayout()を実行
	}



	///
	///\brief
	///	生成時処理
	///
	///\attention
	///	WTL 8.0の動作を前提としています。
	///
	LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
	{
		if(_enumUpTabOrDown == TAB_UP)
		{
			//デフォルトの動作に任せる
			bHandled = FALSE;
			return	0;
		}

		//以下のコードはWTL 8.0の CTabViewImpl::OnCreate() と同じにしている
		{
			CreateTabControl();
		}

		if(_enumUpTabOrDown == TAB_DOWN)
			m_tab.ModifyStyle(0,TCS_BOTTOM);		//タブを下タイプに変更。ComCtl32.dll Ver6では無視される

		return	0;
	}




	///
	///\brief
	///	タブ配置位置の強制変更
	///
	///	CTabViewImpl::UpdateLayout() が仮想関数でなく、直接いじることができないため、
	///	CTabViewImpl::UpdateLayout() 内で指定されたタブ配置位置を強制変更する
	///
	LRESULT OnWindowPosChanging(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
	{
		bHandled = FALSE;

		//タブが上側なら処理の必要なし
		if(_enumUpTabOrDown == TAB_UP)
			return	0;

		WINDOWPOS*	pPos = (WINDOWPOS*)lParam;
		if(pPos == NULL)
			return	0;

		if(!(pPos->flags & SWP_NOMOVE))
		{
			if(_enumUpTabOrDown == TAB_DOWN && pPos->y == 0)
			{
				//タブの位置を下側に変更
				RECT	rect;

				GetClientRect(&rect);
				pPos->y = rect.bottom - m_cyTabHeight;
			}
		}

		return	0;
	}



	///
	///\brief
	///	タブ選択時処理
	///
	///\attention
	///	WTL 8.0の動作を前提としています。
	///
	LRESULT OnTabChanged(int /*idCtrl*/, LPNMHDR /*pnmh*/, BOOL& bHandled)
	{
		if(_enumUpTabOrDown == TAB_UP)
		{
			//デフォルトの処理に任せる
			bHandled = FALSE;
			return	0;
		}

		//以下のコードはWTL 8.0の CTabViewImpl::OnTabChanged() と同じにしている
		{
			SetActivePage(m_tab.GetCurSel());
			OnPageActivated(m_nActivePage);
			UpdateLayout();				//このクラスのUpdateLayout()が実行される
		}

		return 0;
	}



	///
	///\brief
	///	親ウインドウサイズ変更時処理
	///
	///\attention
	///	WTL 8.0の動作を前提としています。
	///
	LRESULT OnSize(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
	{
		if(_enumUpTabOrDown == TAB_UP)
		{
			//デフォルトの処理に任せる
			bHandled = FALSE;
			return	0;
		}

		//以下のコードはWTL 8.0の CTabViewImpl::OnSize() と同じにしている
		{
			UpdateLayout();		//このクラスのUpdateLayout()が実行される
		}
		return 0;
	}



	///
	///\brief
	///	ページ追加処理
	///
	///\attention
	///	WTL 8.0の動作を前提としています。
	///
	bool AddPage(HWND hWndView, LPCTSTR lpstrTitle, int nImage = -1, LPVOID pData = NULL)
	{
		//以下のコードはWTL 8.0の CTabViewImpl::OnSize() と同じにしている
		{
			//このクラスのInsertPageが実行される
			return InsertPage(GetPageCount(), hWndView, lpstrTitle, nImage, pData);
		}
	}


	///
	///\brief
	///	ページ挿入処理
	///
	bool InsertPage(int nPage, HWND hWndView, LPCTSTR lpstrTitle, int nImage = -1, LPVOID pData = NULL)
	{
		bool	ret;

		ret = __super::InsertPage(nPage,hWndView,lpstrTitle,nImage,pData);

		UpdateLayout();		//このクラスのUpdateLayout()を実行する

		return	ret;
	}

 
	///
	///\brief
	///	レイアウト設定処理
	///
	void UpdateLayout()
	{
		RECT rect;
		GetClientRect(&rect);

		if(_enumUpTabOrDown == TAB_UP)
		{
			//タブの位置をウインドウ上側に変更
			if(m_tab.IsWindow() && ((m_tab.GetStyle() & WS_VISIBLE) != 0))
				m_tab.SetWindowPos(NULL, 0, 0, rect.right - rect.left, m_cyTabHeight, SWP_NOZORDER);

			//ビューウインドウの位置を下側に変更
			if(m_nActivePage != -1)
				::SetWindowPos(GetPageHWND(m_nActivePage), NULL, 0, m_cyTabHeight, rect.right - rect.left, rect.bottom - rect.top - m_cyTabHeight, SWP_NOZORDER);
		}
		else if(_enumUpTabOrDown == TAB_DOWN)
		{
			//タブの位置をウインドウ下側に変更
			if(m_tab.IsWindow() && ((m_tab.GetStyle() & WS_VISIBLE) != 0))
				m_tab.SetWindowPos(NULL, 0, rect.bottom - m_cyTabHeight, rect.right - rect.left, m_cyTabHeight, SWP_NOZORDER);

			//ビューウインドウの位置を上側に変更
			if(m_nActivePage != -1)
				::SetWindowPos(GetPageHWND(m_nActivePage), NULL, 0, 0, rect.right - rect.left, rect.bottom - rect.top - m_cyTabHeight, SWP_NOZORDER);
		}
		else
		{
			ATLASSERT(0);	//対応していないタブ配置位置
		}
	}
};

プロジェクトファイルをダウンロード


カテゴリー「WTL」 のエントリー