CMFCToolBarを初期状態で非表示にしたい

[戻る]
NOR 2011/12/09(金) 14:03:27
Visual C++ 2008 MFC、XPです。

CMFCToolBarを使ったツールバーを複数用意しているのですが、
その中のいくつかを、VisualStudioなどのように、
初期状態で非表示にできないか模索しています。

作成時にWS_VISIBLEを取り除いてみたり、
CFrameWndEx::ShowPane()を入れたりしてみたのですが、
フローティング状態だったときの復元が正しく行われずに困っています。

以下が問題の発生する方法です。

1. デフォルト設定のままSDIのプロジェクトを作成し、いったん実行してすぐ終了
2. 初期状態に戻すために、該当のレジストリを削除
   (「HKEY_CURRENT_USER\Software\アプリケーション ウィザードで〜」の中)
3. if (!m_wndToolBar.CreateEx(this, TBSTYLE_FLAT, WS_CHILD〜
   の部分からWS_VISIBLEを取り除いて、リビルドして実行
4. ツールバーは非表示になっている
5. いったん終了して再度実行しても、ツールバーはちゃんと非表示になっている
6. 「表示」→「ツールバーと〜」→「標準」を選んで再表示
7. いったん終了して再度実行しても、ツールバーはちゃんと表示になっている
8. ツールバーをフローティングにする
9. いったん終了して再度実行すると、外枠だけ表示され、中が描画されていない
   (「表示」→「ツールバーと〜」を見ると、表示されていないことになっている)

正しく初期状態で非表示にする方法をご存じのかたはいらっしゃいませんでしょうか。

もしくは、MFCのバグや仕様(初期状態で非表示にすることを想定していない)
ということになるのでしょうか?

仲澤@失業者 2011/12/09(金) 17:39:06
自分とこのアプリは
「ドック」状態だと、バーの表示/非表示状態は終了時の状態に
もどりますね。

「フローティング」にしていると、
「フローティング」+「消去」状態は無視されて、
「フローティング」+「表示」になりますね。
つまり、フローティング状態で消去してから、アプリを再起動すると
フローティング表示されちゃうってことですね。

中身がちゃんと表示される以外はNORさんと同じ状態かもしれません。
う〜む。

NOR 2011/12/14(水) 09:15:29
> 中身がちゃんと表示される以外はNORさんと同じ状態かもしれません。

試していただき、ありがとうございます。
これってやはりまたMFCのバグになりますでしょうか。
それとも、初期状態で非表示にする正しい方法が別にあるのでしょうか。

なお、通常通りWS_VISIBLEを付けておくと、
フローティングのまま終わらせてもちゃんと復元されますが、

> 「フローティング」+「消去」状態は無視されて、
> 「フローティング」+「表示」になりますね。

この現象は同じように発生するようです。

> 中身がちゃんと表示される以外はNORさんと同じ状態かもしれません。

これはWS_VISIBLEを付けないで作成しても、
フローティング復元時に中身が表示されているということでしょうか?

仲澤@失業者 2011/12/14(水) 14:00:02
>これはWS_VISIBLEを付けないで作成しても、
いいえ、付けてます。ウイザードで生成したままですね。
フローティングでの消去状態を再現するには、
自分でコードする必要があるのかもしれません。

NOR 2012/01/05(木) 09:19:50
>いいえ、付けてます。ウイザードで生成したままですね。

付けたままだと、当然初期状態で表示されてしまいますよね。

普段使わないようなツールバーもいくつか用意してあり、
デフォルトですべて出してしまうと、
最初に起動したときの画面がものすごくしつこくなってしまうため、
非表示の状態から開始できないかと調べていました。

ところが、新たに仲澤@失業者さんに見つけていただいた
「フローティング+消去」の状態が正しく復元されない
というバグも出てきてしまいました。

ひょっとしたらこのバグが解決できれば、
当初の「WS_VISIBLEを取り除く」だけで、同時に解決できるのかもしれません。

このバグについてなにか情報をお持ちのかたがいらっしゃれば、
ご意見いただけると嬉しいです。
よろしくお願いいたします。

えーと 2012/01/05(木) 17:08:32
VC 2010では解決されていますね。

自分の場合はMFC自体のラッパーでオリジナルのLoadState()で
実現していたので気にしていなかったです。
なので、目的の状態で再現していました。

解決しようと思うと、MFCソースを追っていくしかないですが、1ヶ月近く
経っていますね。
追いきれなかったのでしょうか?

原因は
SDIの場合だと
afxpaneframewnd.cppの2834行あたりから見て貰うと判ります。
本状態の場合、レジストリから読んだ後、フロートの状態がONなので
メンバm_bRecentFloatingStateがTRUEになります。

しかし、このソース部分で強制的にSetDelayShow(TRUE)が呼ばれてしまうから
WS_VISIBLEを外して作成した場合は、外枠だけが表示されることになります。
WS_VISIBLE付きで作成されると、あたかも強制的に表示するように見える訳です。

>当初の「WS_VISIBLEを取り除く」だけで、同時に解決できるのかもしれません。
VS2010にアップすれば何もしなくてもOKです。
※フロート状態の非表示でも、ドッキング非表示状態でも。

どうしてもVC2008で行いたい場合は、原因が判ったので、表示される前に
SetDelayShow(FALSE)を呼び出したら良いだけです。

WinAppEx:LoadState()でレジストリを元に再現されるわけですが、
ドッキング状態を再現する時に各コントロールのSetDockState()が呼ばれます。
なので、ツールバーのSetDockState()をオーバーライドしてあげればよいです。
void CHogeToolBar::SetDockState(CDockingManager* pDockManager)
{
    __super::SetDockState( pDockManager );
    if( pDockManager->m_bRestoringDockState && m_bRecentFloatingState && !
GetRecentVisibleState() ){
        GetParentMiniFrame()->SetDelayShow( FALSE );
//      ShowPane( FALSE, FALSE, FALSE );
    }
}
※WS_VISIBLE付きで作成したのであればShowPane()部分は必要です。

えーと 2012/01/05(木) 17:27:18
WS_VISIBLEなしで作成で、フロート状態で再現する場合は
ShowPane()で表示してやる必要があるので、
どのみちShowPane()が必要ですね。
なので、
if( pDockManager->m_bRestoringDockState && m_bRecentFloatingState && !
 ){
    BOOL bShow = GetRecentVisibleState();
    GetParentMiniFrame()->SetDelayShow( bShow );
    ShowPane( bShow, bShow , FALSE );
}
かな?

えーと 2012/01/05(木) 17:28:12
if( pDockManager->m_bRestoringDockState && m_bRecentFloatingState && !
 ){
でななく
if( pDockManager->m_bRestoringDockState && m_bRecentFloatingState ){
です。
消し忘れてました。

NOR 2012/01/05(木) 18:45:08
[[解決]]
> 解決しようと思うと、MFCソースを追っていくしかないですが、1ヶ月近く
> 経っていますね。
> 追いきれなかったのでしょうか?

他のトラブルの対応も入ってしまい、さらに年末年始を挟んだため、
追加報告が遅れてしまいました。申し訳ありません。

えーとさんに提示していただいたSetDockState()のオーバライドで、
WS_VISIBLEありで作成した場合でも無しで作成した場合でも、
「フローティング+表示」や「フローティング+消去」の状態を
正しく復元できるようになりました。

こちらもCMFCToolBarの派生クラスを共通基本クラスとして作成していたため、
そこでSetDockState()をオーバライドするだけですべて直りました。

いい加減にVS2010に移行したいところではあるのですが、
内部的になかなかいい区切りが見つからず、
もうしばらく2008になりそうなので、今回の方法を組み込ませていただきます。
ありがとうございました。

NOR 2012/01/10(火) 14:38:57
1年半前にトップメニューのアクセスキーのアンダーバーが
表示されないバグについて相談させていただいたのですが、
http://rarara.cafe.coocan.jp/cgi-bin/lng/vc/vclng.cgi?print+201007/10070007.txt

今回の件でいろいろネットを漁っているうちに、
半年前に投稿されていた回避策をたまたま見つけました。
描画時だけ文字をすり替えていました。
http://connect.microsoft.com/VisualStudio/feedback/details/478417/missing-hotkey-for-top-level-buttons-in-cmfcmenubar

この問題もVS2010では解決していますが、2008を使われているかたはご参考まで。

えーと 2012/01/11(水) 10:57:53
情報提供ありがとうございます。

本問題とは別なので、別スレッドを立てたほうが良かったかもしれませんね?

とりあえず、私も2008コンパイル時のみ本処理となるように追加しました。
※本問題に関しても

見た目の問題(キー自体は動く)なので、VS2008時代は対応しませんでしたが
もしそこまでこだわるならif条件に!bHorzを追加しておくと良いかもしれません。

違いは、リンク先通りだとメニューバーを縦配置した時に文字とアンダーライン
の間に余白がありません。
条件を追加すると、1ドットの余白ができます。
横配置でもアンダーライン間は余白があるので、合わせたほうが良いかも?

なぜ、縦配置の時に独自描画で下線を描いていたのかこれで理解出来ました。

えーと 2012/01/11(水) 11:04:40
> ※本問題に関しても
は、無視して下さい。
自分のは対応しなくても動いてます...(TT)

NOR 2012/01/11(水) 12:11:34
> 本問題とは別なので、別スレッドを立てたほうが良かったかもしれませんね?

やはりそっちのほうがよかったのですね。
連投で、解決扱いのまま件名一覧部分に変化が無かったため、
誰も読み直してくれないかもと思っていました。
気づいていただきありがとうございます。

> 見た目の問題(キー自体は動く)なので、VS2008時代は対応しませんでしたが
> もしそこまでこだわるならif条件に!bHorzを追加しておくと良いかもしれません。
>
> 違いは、リンク先通りだとメニューバーを縦配置した時に文字とアンダーライン
> の間に余白がありません。

なるほど、たしかに縦配置のときは、
「T」が「H」みたいになってしまっていました。

if (!m_bMenuMode) {

の部分を

if (!m_bMenuMode && bHorz) {

と変更しておきました。ありがとうございます。

#!bHorzではなくbHorzですよね?

えーと 2012/01/11(水) 15:14:13
> #!bHorzではなくbHorzですよね?

すみません、そのとおりです。
ソースではちゃんとbHorzにしたのに...

NOR 2012/01/11(水) 18:28:10
> すみません、そのとおりです。

ありがとうございます。間違っていなくて安心しました。

VS2010のCMFCToolBarButtonのソースと比べてみましたが、
DrawTextにstrWithoutAmpを渡してしまっているだけのことなんですね。
ライブラリの深いところにある基本クラスの実装ミスの弊害は怖いものです。

古いスレッドには追加発言できません。