プログラミングの最近のブログ記事

ウィンドウサイズを固定する

| コメント(0) | トラックバック(0)   このエントリーをはてなブックマークに追加 このエントリーを含むはてなブックマーク

続いて、ウィンドウサイズを固定することを考える。

前回の「設定」と今回の「固定」はどう違うかというと、
ここで「固定」とは、一度決めたサイズはユーザーが変更できないようにするということである。

前回作成したウィンドウだが、
ウィンドウエッジにマウスカーソルを持っていけば、マウスカーソルの形が変わって
ウィンドウサイズを自由に変更できるようになっている。
これでは、せっかく設定したウィンドウサイズも意味がない。

手っ取り早くウィンドウサイズを固定するには、
CreateWindow関数の第3引数から、WS_THICKFRAMEの値を引けばよい。
現在のプログラムは、WS_OVERLAPPEDWINDOWなので、
ここからWS_THICKFRAMEの値を引くには、

WS_OVERLAPPEDWINDOW & ~WS_THICKFRAME

とする。(ビット演算なので、単なる引き算ではダメ)

ついでに、ウィンドウの最大化も封印(使用不可)としよう。
この場合は、同じようにWS_MAXIMIZEBOXを引けばよい。

よって、プログラムのCreateWindow関数の呼び出しは、次のようになる。

hWnd = CreateWindow(szClassName, lpCmdLine,
                    WS_OVERLAPPEDWINDOW & ~WS_THICKFRAME & ~WS_MAXIMIZEBOX,
                    CW_USEDEFAULT, CW_USEDEFAULT, 320, 240, NULL, NULL, hInstance, NULL);

ちなみに、winuser.hでWS_OVERLAPPEDWINDOWの定義を見てみると、

#define WS_OVERLAPPEDWINDOW (WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU
                           | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX)

となっている。
WS_OVERLAPPEDWINDOWがそもそも、他の値を組み合わせて作られたもののようだ。

ウィンドウサイズを設定する

| コメント(0) | トラックバック(0)   このエントリーをはてなブックマークに追加 このエントリーを含むはてなブックマーク

前回作成したウィンドウだが、
ゲームの場合は、背景画像が固定サイズなので、ウィンドウサイズを設定する必要がある。
(小さなチップ画像を組み合わせて背景を作成するなら、その必要はない)

今回は、背景画像のサイズを800×600とする。
スマートフォン向けアプリを作るならもっと小さいサイズを検討せねばなるまいが、
PC用のWindowsアプリを作成するなら、この程度のサイズが要るだろう。

まず、ウィンドウのサイズを800×600にするといっても、
以前作成したプログラムのCreateWindow関数の引数320, 240を変えればよいというわけではない。
800×600というのは、ウィンドウのクライアント領域(内容を表示する領域)のサイズなので、
実際のウィンドウサイズは、これにタイトルバーやウィンドウエッジが含まれたサイズとなる。

clientwindowsize.png

ウィンドウサイズを設定するには、SetWindowPos関数を呼ぶ。
第1引数にウィンドウハンドル、
第2引数にZオーダー(ウィンドウが重なる場合に表示される順)を指定するためのウィンドウハンドル
第3引数にディスプレイの表示位置(左端からのピクセル数)、
第4引数にディスプレイの表示位置(上端からのピクセル数)、
第5引数にウィンドウの幅、
第6引数にウィンドウの高さ、
第7引数にウィンドウ位置のオプションを指定する。

クライアント領域を設定するところがどこにもないじゃないか、
と、途方に暮れる前に、まず現在のウィンドウから、
タイトルバーやウィンドウエッジの部分のサイズを取得することにしよう。
設定するクライアント領域のサイズに、これらのサイズを加えれば、設定したいウィンドウサイズになる。

タイトルバーやウィンドウエッジの部分のサイズを取得する最も楽な方法は、
現在のウィンドウサイズから、現在のクライアント領域のサイズを引けばよい。

現在のウィンドウサイズはGetWindowRect関数、
現在のクライアント領域のサイズはGetClientRect関数で取得できる。
いずれの関数も、第1引数はウィンドウハンドルで、第2引数はRECT構造体だ。

CreateWindow関数の後に、次のようなコードを追加しよう。

RECT rx; //ウィンドウ領域
RECT cx; //クライアント領域
GetWindowRect(hWnd, &rx);
GetClientRect(hWnd, &cx);

これでそれぞれのRECT構造体に左端(left)、上端(top)、右端(right)、下端(bottom)が入る。
それを踏まえて、新しいウィンドウサイズは、次のように指定することになる。

const int new_width = 800 + (rx.right - rx.left) - (cx.right - cx.left);
const int new_height = 600 + (rx.bottom - rx.top) - (cx.bottom - cx.top);

幅new_widthについては、
rx.right - rx.leftはウィンドウの幅を表し、
cx.right - cx.leftはクライアント領域の幅を表す。
上から下を引けば、ウィンドウエッジのサイズとなるから、これに設定したいクライアント領域の幅を足せばよい。

それでは、SetWindowPos関数を呼んでみよう。

SetWindowPos(hWnd, NULL, 0, 0, new_width, new_height, SWP_SHOWWINDOW);

コンパイルして実行したら、クライアントサイズが800×600のウィンドウが表示されたはず。

しかし気になるのは、必ずディスプレイの左上にウィンドウが表示されることだ。
できればディスプレイの中央に表示するようにしたい。

表示位置は、先のSetWindowPos関数の第3,4引数で指定すればよいのだが、
その値はどのように取得すればよいだろうか。

これにはまずディスプレイのサイズを取得する必要がある。
次のように、GetSystemMetrics関数で取得する。
SetWindowPos関数の前に追加しよう。

const int disp_width = GetSystemMetrics(SM_CXSCREEN);
const int disp_height = GetSystemMetrics(SM_CYSCREEN);

横方向は、(ディスプレイの幅-ウィンドウの幅)/2、
縦方向は、(ディスプレイの高さ-ウィンドウの高さ)/2
とすると、ウィンドウが画面の中央に表示される。

では改めてSetWindowPos関数を呼んでみよう。

SetWindowPos(hWnd, NULL, (disp_width - new_width) / 2, (disp_height - new_height) / 2,
             new_width, new_height, SWP_SHOWWINDOW);

今度はディスプレイの中央に表示された。

最後に。
最近のPCのディスプレイは従来のような4:3のサイズ比ではなく、16:9のサイズ比が多いようだ。
もしも実行環境が16:9のサイズ比であっても、ウィンドウそのものは4:3で構わないのだが、
もしフルスクリーン化することを考えるなら、ウィンドウを16:9のサイズで作成し
4:3のサイズからはみ出る両端部分を
黒帯(や見栄えのよい画像)で埋めることも考えなければならないだろう。

Excel VBAにおけるグラフプロット幅の変更とZoomの関係

| コメント(0) | トラックバック(0)   このエントリーをはてなブックマークに追加 このエントリーを含むはてなブックマーク

躓いたのでメモ。

<対象Excelバージョン>
Excel2002

<現象>
1.VBAで、グラフを作成する。
2.VBAで、作成したグラフのプロット部分の幅を変える
(ActiveChart.PlotArea.Width=XXX)

これを行うとプロット幅を変えることができるのだが、
そのグラフを配置したシートの表示倍率(ズーム)によって
見た目のサイズが変わってしまうことが発覚。

例えば、ズームを100%にして実行する場合と、50%にして実行する場合とでは
グラフプロット部分の幅が大きく変わってしまう。

PlotAreaのサイズがZoomに依存するとはExcelの不具合以外の何物でもないような気がするが...

<回避方法>
仕方ないので、グラフのプロット幅を変えるときだけ、Zoomを固定する。
こんな感じで。

'現在のズームを変数に保存する
Dim tmpZoom As Double
tmpZoom = ActiveWindow.Zoom
 
'ズームを一時的に100%に固定してからプロット幅を変更
ActiveWindow.Zoom = 100
ActiveChart.PlotArea.Width=XXX
 
'ズームを元に戻す
ActiveWindow.Zoom = tmpZoom

もう1つ。
Zoomプロパティは、アクティブウィンドウにしか適用できないため、
他のシートを選択している状態で実行するとエラーになる。
Worksheets("シート名").Activate
でアクティブにしてからZoomプロパティを設定すればよいのだが、
そのシートがアクティブになっていても、
グラフや、プロット領域などのパーツ(グラフ以外のオートシェイプも対象かも。未確認)を
選択しているとエラーになってしまう。

よって、

Worksheets("シート名").Activate
ActiveSheet.Range("A1").Select

などとして選択を解除してから実行する必要がある。

それにしても、ZoomがPlotAreaに影響するのは謎だ。
PlotArea.Widthの値はグラフ本体の幅を超えることはできないから、
大きな値を設定しても、ある値以上は大きくならない。
よって、PlotArea.Widthを1000に設定し、MsgBoxでPlotArea.Widthを表示したところ、
Zoomを100%にした場合と50%にした場合で大きく違っていた。
前者の方が小さいので、Zoom×PlotArea.Width=Nのような、何らかの関係があるのかもしれない。