お世話になっております。
多言語対応アプリをVC2005 MFC MDIにて製作しております。
プロパティシートを作っているのですが、プロパティシートにボタンを作りたいのです
が、日本語版WindowsXPで場所を合わせても英語版WindowsXPで場所が大きくずれてしまい
ます。
何が原因なのでしょうか?
プロパティシートのInitDialog()の中でCreateしております。
CRect rectSize;
GetDlgItem( IDOK )->ShowWindow( SW_HIDE );
GetDlgItem( IDCANCEL )->ShowWindow( SW_HIDE );
GetWindowRect(rectSize);
m_font= new CFont();
m_font->CreatePointFont(100,MS UI Gothic);
m_Bsend = new CButton();
m_Bsend->Create(GET_STR(EV_TXT_MAIN_SET_BTN01), WS_VISIBLE | WS_TABSTOP,
CRect( rectSize.Width()-176,
rectSize.Height()-20,
rectSize.Width()-88,
rectSize.Height()+5),
this, IDC_BSEND );
また、日本語版と英語版で取得できるrectサイズが違うのも少し気になっております。
数値はとりあえず合わせながら作ってみたので根拠はありません。
176とかピクセル数はやめるべき。
ダイアログが使用しているフォントや画面のDPIで変化するから。
ベストな方法は判らない。
多少フォントやDPIが違ってもいいように余裕を持つとか
IDOKなどをヒントにするとかかな。
DLG内に「動的」にボタンを作るのは簡単なのですが、
位置やサイズを決定するのには「こつ」がいります。
1.自分の場合は、DLGに常に存在する他のコントロールの
位置を取得して、その位置からのオフセットで「動的」
ボタンの位置を決定し、同種のコントロールのサイズ
を基準にしてサイズを決定してます。
2.これができない場合は、ダミーのコントロールを
DLGリソースに置き、このコントロールの位置と
サイズを取得したらDestroy()してしまい、この位置
に「動的」コントロールを再配置する方法をとっています。
以上の方法のどちらでも概ね問題の無い場所に配置できます。
位置の取得はスクリーン座標、位置の設定は親ウインドウ
のクライアント座標になるため、変換が必要(ScreenToClient()等)
な点に注意が必要です。
リソースと併用になっていると思うので元のコントロールの配置は
多分、ダイアログ単位で記述されていると思います。
この為、画面のDPIやらフォント設定で1ダイアログ単位の値が変わってしまうので
プログラム内で固定値で制御するのはNGです。
既に皆さんが書かれているようにリソースで配置されているコントロールから
大きさや位置を取得してそれを元に相対的に配置するのが無難でしょう。
まあ、それでも必ずうまく行くと言う保証は有りませんけれど。
状況によっては、ボタンがウインドウに収まりけれないケースも出るでしょうから。
ご意見ありがとうございます。
お二人に共通するように、IDOKを基準に設定するようにしました。
また、スクリーン座標・クライアント座標を勉強し直しました。
元々あるソースの移植なのですが、IDOK,IDCANCELは使用しない為に消していました。
そこで、下記の様にしてみました。
CRect btnRect;
// IDOKの場所を取得
GetDlgItem( IDOK )->GetWindowRect(btnRect);
this->ScreenToClient(btnRect);
// 既存のボタンを消す
GetDlgItem( IDOK )->ShowWindow( SW_HIDE );
GetDlgItem( IDCANCEL )->ShowWindow( SW_HIDE );
// フォントの設定
m_font= new CFont();
m_font->CreatePointFont(100,FONT_NAME);
// ボタン描画
m_Bsend = new CButton();
m_Bsend->Create(GET_STR(EV_TXT_MAIN_SET_BTN01), WS_VISIBLE | WS_TABSTOP,
btnRect, this, IDC_BSEND );
m_Bsend->SetFont(m_font);
しかし、全く違う位置にボタンが出てしまいます。
ためしにIDOKを消さずにそのまま出してみると所定の位置にOKボタンが表示されていました。
計算方法を間違えているのでしょうか?
PATIO さん
ありがとうございました。
ご意見頂いているのに気付きませんでした。
日本語版XPと英語版XPでダイアログで使用するフォントもあわせているのですが、なぜダ
イアログのサイズなどが変わってしまうのか不思議に思っていました。
英語版の方が幅が狭く縦が長い。
DPI(ドットパーインチの事ですよね?)が関係するのでしょうか?
これは日本語版と英語版で異なってしまうのでしょうか。
子ウインドウでないのに相対座標にするからだ
ロケールも関係あるかもしれません。
ダイアログ単位の計算にしても具体的な式までは確認していないのでわかりませんが、
基本的に表示する文字列が収まるようにコントロールのサイズや位置を調整するために
文字のサイズを基準にしたダイアログ単位と言う概念を使っているはずなので
同じフォントであっても英語版と日本語版ではダイアログ単位の計算に使っている
対象が異なるのかもしれません。
文字のサイズと一口に言っても実は細かく細分化されていたと思います。
日本語の場合はありませんが、アルファベットの場合はgの下に突き出る部分とか
hの上に突き出す部分とかにそれぞれ名前があったと思います。
詳しい所は忘れてしまいましたけれど。
あ さん
すみませんが、もう少し詳しくお願いします。
ちなみにこちらはDoModal()によって呼ばれたプロパティシートですので、一応子ウィン
ドウとなります。
PATIOさん
フォントにそのような違いがあったのですか。しかも名前まで付いているんですね。
ちなみにWindows98で動かして見た所、少し位置がずれましたので、OSのバージョンに
よっても異なるようですね。
かなり奥が深そうです。
gakさん
ダイアログ単位というのは知りませんでした。
色々と調べてみました。
既存のIDOKボタンのRectを取得した後、それをMapDialogRect()にて変換して描画してみ
ましたが、どうも違うようです。
やり方が悪いのか、この場合は使い方が違うのか。。。
コントロールの配置を動的に変更する場合、私はダイアログサイズを取得して、
そのサイズをベースに位置を再計算します。たとえばボタンを中央に置く時は、
ダイアログ幅からボタンサイズを引いてその半分をx座標とします。
ただ、この方法はプロパティシートではダイアログサイズが元のリソースで定義
されたものと異なる可能性が高いので、うまくいきませんね。
> そこで、下記の様にしてみました。
>
> ...
>
> しかし、全く違う位置にボタンが出てしまいます。
取得した値、変換された値、どの位置に表示されたか、を注意深く調べていけば、
自然に答えは出てくると思いますけど。
実際にダイアログボックスのコントロールの移動をやったアプリがどれだったか
覚えていないので、答えは保留(積極的に探す気になっていない)。
ダイアログ単位はリソーステキストに記述するときの単位。プログラムで取得した
位置、座標はピクセル単位だったと思う。
たぶんこんなことじゃないのかな。
this->ScreenToClient(btnRect);
今回thisのクライアントの相対座標にしたわけだ。
これは今までとは違う点。
m_Bsend->Create(略, WS_VISIBLE | WS_TABSTOP, btnRect, this, 略
m_Bsendボタンを子ウィンドウにしていないと
this親ウィンドウを動かしてもm_Bsendボタンがその場に留まるという
現象になったはずなんだけど。
あとから子ウィンドウに変更してるのか?
親ウィンドウを動かしたらm_Bsendも移動させているのか?
m_Bsendボタンが子ウィンドウじゃないから
CreateWindowの座標はスクリーン座標が指定されたものとして扱う。
でもbtnRectはthisのクライアント座標にしている。
maruさん
こちらでもありがとうございます。
>ただ、この方法はプロパティシートではダイアログサイズが元のリソースで定義
>されたものと異なる可能性が高いので、うまくいきませんね。
そうなんです、プロパティシート自体はクラスのみでリソースは存在しません。
プロパティページのサイズ自体で勝手に変わるものだと思っています。
現状のIDOKボタンの場所を取得して、同じ場所にボタンをクリエイトしてもなぜ場所が大
きくずれるのか、全く分かりません。
座標も、あらゆる可能性を考えたのですが、どうも整合性がとれずどの座標がとれている
のか。。。
>ダイアログ単位はリソーステキストに記述するときの単位。プログラムで取得した
>位置、座標はピクセル単位だったと思う。
といいますと、ソースコードから直接ボタンを描画する際にはダイアログ単位は必要なさ
そうですね。
wclrp ( 'o') さん
はい、頂いたアドバイスを参考に相対座標にしてみました。
m_Bsendボタンは子ウィンドウにクリエイトしています。
Create関数の中のthisがプロパティシートになります。
よって子ウィンドウを動かしてもボタンは付いていきます。
>> ダイアログ単位はリソーステキストに記述するときの単位。プログラムで取得した
>> 位置、座標はピクセル単位だったと思う。
> といいますと、ソースコードから直接ボタンを描画する際にはダイアログ単位は必要なさ
> そうですね。
そうなります。
ダイアログ単位はリソーススクリプト上の話のはず。
画面上に表示されたコントロールに対して問合せた場合はピクセル単位になってます。
であるから、表示されてピクセル座標で表現された内容を便りにして配置するわけです。
ダイアログ単位のままでは環境によって位置や大きさが変わってしまうから。
> はい、頂いたアドバイスを参考に相対座標にしてみました。
> m_Bsendボタンは子ウィンドウにクリエイトしています。
> Create関数の中のthisがプロパティシートになります。
> よって子ウィンドウを動かしてもボタンは付いていきます。
wclrp ( 'o')が言いたいのは、WS_CHILDが無いのがおかしいといっているのは無いかなぁ。
CButtonのCreateの説明の所にも、WS_CHILDは常時指定すると書かれているし。
失礼しました。
wclrp ( 'o')さんを呼び捨てにしてました。
ご容赦を。