動的にウィンドウスタイルを変更したい

[戻る]
無空 2015/01/16(金) 00:56:18
MFC、VS2012

ボタンクリックでウィンドウ全体にWS_EX_LAYOUTRTLを適用させたいのですが、
部分的にしか適用されず困っています。

VS2012のリソースエディタで画面プロパティ[Layout RTL]を
有効にすると画面のタイトルバー・テキスト・ボタンなど全てのコントロールが
WS_EX_LAYOUTRTLが反映されたのですが、

void CChangeDlg::OnBtnClick()
{
   this->ModifyStyleEx(0, WS_EX_LAYOUTRTL);
}

としてボタンクリックで動的に変更させようとすると
タイトルバーしか反映されません。

コントロール全体に対して、動的にWS_EX_LAYOUTRTLの適用は可能でしょうか?
よろしくご教授お願いいたします。

仲澤@失業者 2015/01/16(金) 10:08:58
>タイトルバーしか反映されません。

this(つまりこのウインドウ)だけしかやってないようなので、
当然な気がします。

全ての子ウインドウ、または、適用したい子ウインドウに対して
スタイルの変更をやらないといけないと思ういます。

無空 2015/01/16(金) 15:32:28
リソースエディタではウィンドウのみ設定する事で
全体に反映されるのですが、動的に変更したい場合は個々に
設定しないといけないのでしょうか?

試しにスタティックテキストに WS_EX_LAYOUTRTLを
適用させてみたのですが、リソースエディタで設定した場合と
挙動が異なります。

リソースエディタで設定時は文字が右寄り+スタティックテキストも右寄り
になるのですが、
m_text->ModifyStyleEx(0, WS_EX_LAYOUTRTL);
とすると中身の文字は右寄りになるのですが
スタティックテキスト本体は右寄りになりません。

コントロールに対しては他に設定が必要でしょうか?

仲澤@失業者 2015/01/19(月) 10:01:27
1.リソースエディタ設定の結果は*.rcの中身をみれば
 便宜的にやってくれているのだな、ということがわかります。

2.起動中のコントロールにどのようなスタイルが適用されているのかは、
 Spy++を使えば見ることができます。

3.また、拡張も含めて、ウインドウスタイルの一部は
 すべてのウインドウに適用できるわけではありません。

4.拡張スタイルの一部は、CreateWindow時に適用できないものがあります。

以上のような点に注意して調べてみてはどうでしょう。

ITO 2015/01/19(月) 10:30:06
うーーん、
 スタティックテキストは特殊なのでウインドウスタイルを動的に変更
するのは難しかったと思います。
  (できなかったケースが多い)
 エディットボックスは、リッチエディットに変えたほうが動的に変更するのは
楽です。
 

 

無空 2015/01/19(月) 21:54:16
リソースエディタで設定した場合と動的に変更した場合で
スタイルの違いは無く、どちらもWS_EX_LAYOUTRTLが適用されていました。
ただ、見た目の挙動だけが異なっています。

スタイルの動的変更は技術的に無理なのでしょうか?
皆さんならこの場合どうするのか、何か案を頂けると助かります・・・

AR 2015/01/21(水) 12:22:59
ITOさんも仰ってますが、ウィンドウスタイルは生成時にしか反映されないものがあるの
で、変更できないスタイルを切り替える場合は、コントロールの破棄+生成で切り替えて
ます。

あと、スタティックコントロールだったら、描画を全部自前でやっちゃうこともありますね。
流し込む画像ファイルを切り替えて・・・という感じで見た目を変える場合が多いです。

ITO 2015/01/21(水) 13:41:47
ピクチャボックスを使う手もありますね。
CDC +pDCも取得できるので自由に描けます。

ロマ 2015/01/23(金) 19:10:46
SDKで
ダイアログにIDC_STATIC_TEXT, IDC_BUTTON_LAYOUTをつくり、
case WM_COMMAND:
  if(LOWORD(wParam) == IDC_BUTTON_LAYOUT)
  {
    HWND hwnd = GetDlgItem(hDlg, IDC_STATIC_TEXT);
    LONG ex = GetWindowLong(hwnd, GWL_EXSTYLE);
    SetWindowLong(hwnd, GWL_EXSTYLE, ex ^ WS_EX_LAYOUTRTL);
    InvalidateRect(hwnd, 0, TRUE);
  }
これで、IDC_STATIC_TEXTの文字列は、IDC_BUTTON_LAYOUTを押す毎に左右に動きました
(win7)。

ロマ 2015/01/24(土) 11:25:19
質問を誤解していました。私の上記書き込みを削除します。

> リソースエディタで設定時は文字が右寄り+スタティックテキストも右寄り
> になるのですが、
> m_text->ModifyStyleEx(0, WS_EX_LAYOUTRTL);
> とすると中身の文字は右寄りになるのですが
> スタティックテキスト本体は右寄りになりません。

やっと意味がわかりました。
対策は、各コントロールを移動するしかないと思います。

WS_EX_LAYOUTRTLがある場合、
ダイアログ作成時に各コントロールはダイアログの右上を原点として位置決めされます。
各コントロールの位置は、作成後には変わりませんです。

無空 2015/01/26(月) 00:50:31
返信遅くなって申し訳ありません。
回答ありがとうございます。

頂いた回答を見る限り、スタイルの動的変更は難しいのですね・・・
ARさんの仰った、ウィンドウ破棄+生成で対応しようかと思います。

Win32ならばCreateWindowExにパラメータを指定すれば良いみたいですが、
MFCでは生成時にスタイルを指定するのはどこが良いのでしょうか?
DoModalで表示するウィンドウではPreCreateWindowのようなものは
無いようですし・・・・

WM_CREATEをオーバーライドしたOnCreateで指定できそうなんですが、
以下の方法では反映してくれませんでした。
ここのタイミングではウィンドウが既に生成されていて遅いんでしょうか?

int OnCreate(LPCREATESTRUCT lpCS)
{
    lpCS->dwExStyle += WS_EX_LAYOUTRTL;
    if(CDialog::OnCreate(lpCS) == -1)
        return -1;

    return 0;
}

AR 2015/01/26(月) 11:10:03
>頂いた回答を見る限り、スタイルの動的変更は難しいのですね・・・

 厳密には、スタティックテキストのように大量に使われるコンポーネントは、軽量なコ
ンポーネントにする必要がある→大胆に機能を削る必要があったという事だと思います。
 またITOさんのおっしゃってる通りなんですが、凝ったことがしたければ高機能なコン
ポーネントを使え、その代わり大量に使うと重くなる、と言うことだと思います。

>Win32ならばCreateWindowExにパラメータを指定すれば良いみたいですが、
>MFCでは生成時にスタイルを指定するのはどこが良いのでしょうか?

 MFCならCStatic::Create()で、拡張属性も指定できるCreateEx()はMSDNに存在してません。
 ただMFCでもWin32APIでもやることは大差ないので、CreateWindowEx()を使用するのが
一番簡単だと思います。
 一応、CWnd::CreateEx()のヘルプをみると、スタティックテキストの生成サンプルも
載ってるので参考にしてください。
https://msdn.microsoft.com/ja-jp/library/atzdh4ys.aspx


 あと、OnCreate()はCreateWindowEx()などを使った結果として、WM_CREATEのイベント
が発生し、そのイベントを受け取った時に呼ばれるメソッドですので混同しないようにし
てください。
 例えば、OnCreate()は生成した後に呼ばれるものですし、
>lpCS->dwExStyle += WS_EX_LAYOUTRTL;
 足し算じゃなくて論理和使ってくださいとか、
 PreCreateWindow()はイベントメソッドじゃなくてオーバーライドメソッドでしょうとか、
 そもそも、ダイアログに対してWS_EX_LAYOUTRTL属性を付与してどーするんです
か?・・・この辺りが突っ込みどころでしょう。

仲澤@失業者 2015/01/26(月) 19:34:26
え〜と、いまさらで申し訳ないのですが、
Right to Left を使うってことは、各国語対応したいってことでよいですよね。

最近のMFCでは複数言語に対応する場合、
言語毎のサテライトDLL(リソースのみのDLL)を作れって事らしいです。

んで、各言語毎のサテライトDLLにはDLGのテンプレートリソースごと入るわけで、この場
合、

1.そもそも、動的にスタイル変更する必要が「無い」

ってことになります。

スタティックコントロールの位置なども、この場合、
例えば、「アラビア語専用リソースDLL」等に実装されるわけで、
アラビア語用にガチにレイアウトしてOKというわけです。

う〜む、どうもすみません(vv;)。

無空 2015/01/27(火) 00:14:28
>ARさん
ダイアログに対してWS_EX_LAYOUTRTL属性を付与する意味は、
タイトルバーも右寄りにしたいのです。

リソースエディタならばダイアログのみに属性付与で反映できるのに、
コードを書く場合は全てのコントロールに対して書かねばならないというのは
ソースの見栄えも悪くなるし、何より面倒だったので・・・

リソースエディタの一括付与が出来るのだから、
コード上でも一括付与が簡単に出来るんじゃないのか?と思った次第です。

>足し算じゃなくて論理和使ってください
忘れてました。指摘ありがとうございます。


>仲澤@失業者さん
はい。仰る通り、各国語対応が主目的となります。
(質問は大分省略させてもらいました)

普通に起動時の言語のみで動くアプリならば問題ないのですが、
今回の要件は、ボタンで言語を切り替える機能を求められておりまして・・・
MFCの意向には背いているかもしれませんが、動的に変更する必要があるのです。

無空 2015/01/27(火) 00:24:00
連投すみません。

ARさんの仰っていたCreateEx()で何とかしようと思っていたのですが、
以下の方法で生成時の属性適用が出来ました。

int CChangeDlg::OnCreate(LPCREATESTRUCT lpCS)
{
    if(CDialog::OnCreate(lpCS) == -1)
        return -1;

    this->ModifyStyleEx(0, WS_EX_LAYOUTRTL);

    return 0;
}

OnInitDialog()にModifyStyleEx()で属性適用させようとしても駄目でしたが
OnCreate()で適用可能なのがイマイチ良くわかりません。

ARさんは
>OnCreate()は生成した後に呼ばれるもの
と仰っていますが、少しタイミングが違うのでしょうか?

OnCreate() → ダイアログ生成、属性適用可能? → OnInitDialog()

内部ではこのような順に呼ばれているのでしょうか?

無空 2015/01/27(火) 00:25:44
何度もすいません。補足です。

上記の方法ならば、一回行うだけで
ダイアログのタイトル・全てのコントロールに対して
WS_EX_LAYOUTRTLが適用されました。

リソースエディタで指定した場合と同じ状況になります。

AR 2015/01/27(火) 10:19:13
アドバイスの前提が崩れちゃいました。
すみません。

MFCの多言語対応であれば、以下の解説URLが定番っぽいように思います。
起動後に切り替える仕組みなど、以前私も参考にしましたし。

http://hp.vector.co.jp/authors/VA000092/win32/multi-lang-mfc.html
http://www.vcppclub.com/samples/gen/p_loctest/loctest.html

文字高さや、文字幅も言語によって異なるので、仲澤@失業者さんの仰ってるような「ア
ラビア語専用リソースDLL」みたいな各言語専用のDLLを作った方が良い場合が多いです。
もっともリソースを丸ごと入れ替えるので、一部だけ入れ替えるというのは最初の設計段
階から考えてないとしんどいですがね。


>OnCreate() → ダイアログ生成、属性適用可能? → OnInitDialog()

TRACE入れるなり、ブレイクポイントを入れるなり、ご自身で確認してみてください。
多分、やってみないと理解が進まないと思います。
(各オブジェクトのハンドルを使って生成済みか否か判断します)

無空 2015/01/30(金) 00:48:39
リソースDLLにはしたかったのですが・・・
時間が足りない事と、元となる設計を大きく崩したくないと言うこともあり
アドバイスして頂いて申し訳無いのですが、今の形で行こうと思います。

今後、新規で作成する場合は参考にさせて頂きます。


>各オブジェクトのハンドルを使って生成済みか否か判断します
試してみました。
OnCreate()の段階でハンドルが取れる=生成済みなのですね。

そうすると、ModifyStyleEx(0, WS_EX_LAYOUTRTL)が
OnCreate()では有効で、OnInitDialog()では有効ではない
ということの違いが良くわかりません。

どちらでもハンドルが取れる状態なので、有効になって良いと思うのですが・・・

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