MFCでコマンドライン引数を解析する方法と言えば、
CWinApp::ParseCommandLine
を使うやり方が一般的・・・と言いますか、SDIやMDIプロジェクトだったら、最初からこれが使われているため、ここを拡張して独自の起動引数なんかを解析するのが普通かと思います。

長年その方法でやっていたのですが、最近になって __argc__targv (頭にアンダースコア2個) を使えば、解析済みの引数が参照出来る事を知りました。
ParseCommandLineを使うとわざわざCCommandLineInfoの派生クラスを作る必要があったりして面倒くさいのですが、__argcと__targvならば簡単に参照できるので、場合によってはこちらを使った方が手軽そうです。

データ構造として、起動引数を1クラスに管理させたい、とか思い始めるとParseCommandLineの方が良いのかなとも思ったりして、悩みどころではあります。

ホイールイベントはフォーカスを持つウィンドウで優先的に処理されるようなのですが、場合によってはフォーカスよりもマウスカーソル位置を優先したい事があります。

今まで結構回りくどいやり方をしていたのですが、PreTranslateMessageにちょろっと書いたら上手く動いたようなので、載せておきます。
(あまり動作検証していませんが)

BOOL CMainFrame::PreTranslateMessage(MSG* pMsg)
{
	if (pMsg->message == WM_MOUSEWHEEL) {
		CWnd* pWnd = WindowFromPoint(pMsg->pt);
		if (pWnd && IsChild(pWnd)) {
			pMsg->hwnd = pWnd->GetSafeHwnd();
		}
	}

	return __super::PreTranslateMessage(pMsg);
}

Edge1

↑右がリソースエディタで貼ったツリーコントロールで、
左が↓のコードで貼ったツリーコントロール。

// m_TreeはCTreeCtrl型
m_Tree.CreateEx(WS_EX_CLIENTEDGE, WS_CHILD | WS_VISIBLE | TVS_SHOWSELALWAYS, CRect(10, 10, 200, 200), 
this, 0);
m_Tree.InsertItem(_T("ああああ"));

見てお判りの通りWS_EX_CLIENTEDGEが付きません。
CTreeCtrl::CreateExのソースを見てみると、変なことやってますので、そりゃ付かないですね。

BOOL CTreeCtrl::CreateEx(DWORD dwExStyle, DWORD dwStyle, const RECT& rect,
	CWnd* pParentWnd, UINT nID)
{
	BOOL bRet = Create(dwStyle, rect, pParentWnd, nID);
	if (bRet && dwExStyle != 0)
	{
		bRet = ModifyStyleEx(0, dwExStyle);
	}
	return bRet;
}

かと言って

m_Tree.CreateEx(0, WS_CHILD | WS_VISIBLE | TVS_SHOWSELALWAYS, CRect(10, 10, 200, 200), this, 0);
m_Tree.ModifyStyleEx(0, WS_EX_CLIENTEDGE, SWP_FRAMECHANGED);
m_Tree.InsertItem(_T("ああああ"));

こんなコードにしても、↓のようにビジュアルテーマ(?)が適用されません。
Edge5
(WS_BORDERだとちょっと濃い枠が付くため、これもちょっと違います。)

回避方法としては、

m_Tree.CWnd::CreateEx(WS_EX_CLIENTEDGE, WC_TREEVIEW, _T(""), WS_CHILD | WS_VISIBLE | TVS_SHOWSELALWAYS, CRect(10, 10, 200, 200), this, 0);
m_Tree.InsertItem(_T("ああああ"));

というように、CWnd::CreateExを直接呼び出してしまう事ぐらいでしょうか。

これで一応リソースエディタで貼った物と同じ枠が付きます。
Edge6

MFCのバグ一覧とかどっかにまとまってたりしないのかな・・・。

VS2008 SP1 MFC環境では、ポップアップメニューの表示には
theApp.GetContextMenuManager()->ShowPopupMenu(・・・)
などと呼ぶことになりますが、これを何気なくダイアログ上でやると、メニューは表示されるものの、適当な場所をクリックしてもメニューが閉じてくれません。

これで少しハマっていたのですが、ダイアログの親クラスをCDialogではなくCDialogExにするとうまくメニュー処理がされるようです。
(こういうものこそASSERTで引っかけてくれよorz)

Edge12

MFCでアプリを書いていると、スクロール処理が必要になってくる場合が多々あります。
CScrollViewで行ける場合は良いのですが、自前でやらないといけない場合には、OnHScrollとOnVScrollをインプリメントしてやらないといけません。

これが結構面倒だなーといつも思っていたので使い回せるようなイベントハンドラを作ってみました。

bool Scroll(CWnd* pWnd, int nBar, UINT nSBCode, UINT nPos, int nLine /*= 1*/)
{
	SCROLLINFO info = {sizeof(SCROLLINFO), SIF_ALL};
	if (!pWnd->GetScrollInfo(nBar, &info)) {
		return false;
	}
	int nNewPos = pWnd->GetScrollPos(nBar);
	int nOldPos = nNewPos;

	switch (nSBCode) {
	case SB_LEFT:
		nNewPos = info.nMin;
		break;
	case SB_ENDSCROLL:
		break;
	case SB_LINELEFT:
		nNewPos -= nLine;
		break;
	case SB_LINERIGHT:
		nNewPos += nLine;
		break;
	case SB_PAGELEFT:
		nNewPos -= info.nPage;
		break;
	case SB_PAGERIGHT:
		nNewPos += info.nPage;
		break;
	case SB_RIGHT:
		nNewPos = info.nMax;
		break;
	case SB_THUMBPOSITION:
		nNewPos = nPos;
		break;
	case SB_THUMBTRACK:
		nNewPos = nPos;
		break;
	}

	pWnd->SetScrollPos(nBar, nNewPos);
	return pWnd->GetScrollPos(nBar) != nOldPos;
}

スクロール位置に変更があったときにtrueを返しますので、これを見て画面更新などを行います。

こんな感じで使います。

void CHogeHogeWnd::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
	if (Scroll(this, SB_HORZ, nSBCode, nPos, 16)) {
		// ここで画面更新など
	}
}

void CHogeHogeWnd::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
	if (Scroll(this, SB_VERT, nSBCode, nPos, 16)) {
		// ここで画面更新など
	}
}

page移動量は若干の調整が必要かもしれませんね。

てか、CScrollWnd的なものを作ってテンプレートっぽくしておくのがベストなんだろうなぁ。

記事検索

アーカイブ