質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.31%

Q&A

解決済

1回答

1121閲覧

MFC MDI で子のタイトルバーの色を変更する方法

TNT_Hiroshi

総合スコア1

0グッド

0クリップ

投稿2024/07/04 04:15

編集2024/07/04 23:15

Visual studio C++, MFCの MDIでアプリを作成しています。

アプリを黒を基調とした画面にしたく、クライアントウィンドウ内に表示する
子のタイトルバーの色を変更したいのですが、方法をご教示ください。

色々とWeb上で検索しているのですが、C#、XAML のやり方は見つけたのですが、
MFCでのやり方が見つけられません。

https://learn.microsoft.com/ja-jp/windows/apps/develop/title-bar

追記
hiroki-o さんコメントありがとうございます。
・自分の環境は Windows 10 ですが、Windows 7 以上では、同様の表示ができるようにしたいです。
(無理なら Win10 以上とか限定してでもやりたいです)

・アプリを単独で黒くしたいです。
Windous の設定 → 個人設定 → 色 から、ダークモードを選ぶと、
親ウィンドウのタイトルバーだけが ダークモード になり、子のタイトルバーの色は
変更されていませんでした。
Windous の設定で選んだ色で、子のタイトルバーも同じ色になるなら
それでも良いです。

・親ウィンドウの全体的な色は 新しいプロジェクト を作成する時、
Visual スタイルと色 の設定で「Office 2007 (Black theme」 を選択すると
設定できました。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

hiroki-o

2024/07/04 11:09

以下の情報を、「質問を編集して」追記すると、回答が得られる可能性が高まります。 ・対象とするWindows(11,10,8,7,...) ・アプリをダークモード対応にしたいのか、アプリ単独で黒くしたいのか ・親ウィンドウのタイトルバーの色変更には成功しているのか ・Visual Studioのバージョン(MFCなら、あまり関係ないかもですが)
TNT_Hiroshi

2024/07/05 03:03

kikukiku さんありがとうございます。 サイト確認させて頂きました。 おっしゃる通り大変そうですね。 Windowsのダークモードの設定がそのまま、アプリ、子ウィンドウまで適用されるような 方法があれば、それでも良いのですが・・・。 もう少し、何か方法を募らせて頂きたいです。
dodox86

2024/07/05 03:14

Windows Vista以降ではデスクトップウィンドウマネージャーと言うものが画面の描画を管理しており、WM_NCPAINTをプログラマーの意図通りにハンドリングするのはかなり難しいです。あれに対応すればこれができないみたいにハマりますので要注意ですね。 タイトルバーに関してはWindowsのバージョンを選びますが、DwmSetWindowAttributeと言うAPIがウィンドウ毎にハンドリングできるので、MFCでのMDIの子ウィンドウに適用できる可能性があります。 [Change the color of the title bar (caption) of a win32 application - Stackoverflow] https://stackoverflow.com/questions/39261826/change-the-color-of-the-title-bar-caption-of-a-win32-application ※興味深いご質問だったのでWM_NCPAINT含め当方でも少し試していましたが、Windows 11での検証環境が無いもので、、、
guest

回答1

0

ベストアンサー

「DwmSetWindowAttribute APIが使えそう」と言う回答ですが、当方で未検証であるのと、Windowsのバージョンを限定するということで情報としてご覧ください:

「質問へのコメント」で一部コメントしましたが、WM_NCPAINTメッセージによるウィンドウのキャプションバー含む非クライアント領域の自前の描画は、Windows Vista以降に導入されたデスクトップウィンドウマネージャー(Desktop Window Manager)の管理が介在することで、プログラマーが自在に描画することは不可能とは言わないまでも、困難になっています。

実際、当方でもWindows 10 Pro(22H2)環境下でVisual Studo 2022/MFCのMDIデスクトップアプリにてMDIの子ウィンドウでWM_NCPAINTをハンドリングし、タイトルバーを任意の色で描画するように試してみました。完成には時間がかかりそうで途中で断念したので感想程度ですが、Windows XP/Visual C++6.0でのそれと比べ、正しく実装するにはかなり困難と判断しました。

そこで、Stackoverflowの以下の質問・回答記事を参考にさせていただき、DwmSetWindowAttribute が使えるのでは? と言う提案をひとつの回答として投稿しておきます。

Change the color of the title bar (caption) of a win32 application - Stackoverflow

DwmSetWindowAttributeはウインドウハンドルを引数で渡すことで個別にそのウィンドウの属性を変えられるので、属性値にDWMWA_CAPTION_COLORを指定し、MDIの子ウィンドウのウィンドウハンドルを指定すれば、その子ウィンドウのキャプションバーの色を変えられそうです。

ただし、リファレンスに以下の記載があるように、

DWMWA_CAPTION_COLOR
DwmSetWindowAttribute で を使用します。 キャプションの色を指定します。 pvAttribute パラメーターは COLORREF 型の値を指します。
色のDWMWA_COLOR_DEFAULT (値0xFFFFFFFF) を指定すると、キャプション色に対するシステムの既定の動作を使用するようにウィンドウがリセットされます。
この値は、Windows 11 ビルド 22000 以降でサポートされています。

「Windows 11 ビルド 22000 以降でサポート」とのことなので、それ以前のWindows上でアプリを稼働させると、機能しません。当方の環境(Windows 10 Pro 22H2)ではHRESULTの返り値が0x80070057で、"E_INVALIDARG One or more arguments are invalid." を示しました。動作可能な適切な環境であるならば、呼び出し自体は恐らく成功するものと思われます。

MFC/MDIの子ウィンドウのキャプションバーの属性を変えるタイミングですが、恐らくはWM_CREATEのメッセージハンドラーであるCChildFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) を使うようになるでしょうか。例えば以下のようなコードです。

C++

1#pragma comment(lib, "Dwmapi.lib") 2 3int CChildFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) 4{ 5 if (CMDIChildWnd::OnCreate(lpCreateStruct) == -1) 6 return -1; 7 8 // フレームのクライアント領域全体を占めるビューを作成します。 9 BOOL succeeded = m_wndView.Create(nullptr, nullptr, AFX_WS_DEFAULT_VIEW, 10 CRect(0, 0, 0, 0), this, AFX_IDW_PANE_FIRST, nullptr); 11 if (!succeeded) 12 { 13 TRACE0("ビュー ウィンドウを作成できませんでした。\n"); 14 return -1; 15 } 16 17 // TODO: ここに特定な作成コードを追加してください。 18 COLORREF color; 19 color = RGB(0xff, 0, 0); 20 21 HRESULT hresult = DwmSetWindowAttribute( 22 this->m_hWnd, DWMWINDOWATTRIBUTE::DWMWA_CAPTION_COLOR, 23 &color, sizeof(color)); 24 bool succeededToCall = SUCCEEDED(hresult); 25 TRACE2("hresult=0x%X, result=%d\n", hresult, succeededToCall); 26 27 return 0; 28}

上記のコード例ではセットする色にRGB(0xff, 0, 0)で赤を指定していますが、例えばDwmGetWindowAttributeを使って、アプリのメインウィンドウのキャプションバーの色を取得し、それを適用することで同じ色になると思います。例えば以下のようなコードです。

C++

1 HRESULT hresult = DwmGetWindowAttribute( 2 AfxGetMainWnd()->m_hWnd, DWMWINDOWATTRIBUTE::DWMWA_CAPTION_COLOR, 3 &color, sizeof(color)); 4 bool succeededToCall = SUCCEEDED(hresult); 5 TRACE2("hresult=0x%X, result=%d\n", hresult, succeededToCall);

その他:

  • Visual Studioで自動生成されるCChildFrameクラスのコード中にはCChildView m_wndView等、別のウィンドウも含まれている為、それらの兼ね合いもあるかもしれません。
  • 質問者さんの現状で、アプリの親ウィンドウの色はWindowsの設定が適用されているが、MDIの子ウィンドウには適用されていないということでしたが、これは恐らくMDIの子ウィンドウにウィンドウスタイルとしてWS_CHILD(=WS_CHILDWINDOW)がセットされているせいかと思われます。Spy++ツールなどで解析してみてください。

投稿2024/07/05 05:26

dodox86

総合スコア9369

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

dodox86

2024/07/05 05:41

MFCはC#/.NET Frameworkも無い頃からWindowsのネイティブAPIを使って自前でゴリゴリと書いたコードでウィンドウを操作し、新たなUIも提供してきたガッツ(?)あるC++のライブラリです。MDI自体はMFCの初期からあるもので古いUI形態ですが、タブ形式も追加されいまだにサポートしてくれていますね。動きがおかしいと思ったらソースを辿ることもできるので、そこから解決のヒントを得られることもあります。(複雑さ加減に諦めることも多くあります)
kikukiku

2024/07/05 06:48

dodox86さんのコメントの通りかと思います。 質問者さん自身にガッツがあれば、ゴリゴリ試しても良いと思いますが、 中途半端であれば、DwmSetWindowAttributeだけ試して 動作すれば採用。しなければ諦めるが正解のように思います。 自分だったらMDIそのものを選択しないかも(最近そのようなアプリをみかけないですね)。
TNT_Hiroshi

2024/07/05 08:53

dodox86さん ありがとうございます。 本日、時間がなくなってしまって、検証までできませんでしたが、 頂いた内容で試行錯誤してみます。 調査に時間がかかりそうなこともあり、クローズとし、結果がでましたら追記させて頂きます。 貴重な情報ありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.31%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問