2012年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のような、何らかの関係があるのかもしれない。


ウィンドウを表示する

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

VBやDelphiやBCBなら、わざわざプログラムを書かなくても
プロジェクトを作成して実行ボタンを押すだけで表示されるウィンドウだが、
敢えて自分でプログラムを書くなら、最低限これだけ書かなければならない。

今回はSampleWindow.cppというファイルを作成した。


#include <windows.h>

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

char szClassName[] = "SampleWindow";

int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPreInst, LPSTR lpCmdLine, int nCmdShow) {
HWND hWnd;
MSG msg;
WNDCLASS wndcls;
if (!hPreInst) {
wndcls.style = CS_HREDRAW | CS_VREDRAW;
wndcls.lpfnWndProc = WndProc;
wndcls.cbClsExtra = 0;
wndcls.cbWndExtra = 0;
wndcls.hInstance = hInstance;
wndcls.hIcon = NULL;
wndcls.hCursor = LoadCursor(NULL, IDC_ARROW);
wndcls.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wndcls.lpszMenuName = NULL;
wndcls.lpszClassName = szClassName;
if (!RegisterClass(&wndcls)) {
return FALSE;
}
}
hWnd = CreateWindow(szClassName, lpCmdLine, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, 320, 240, NULL, NULL, hInstance, NULL);
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
switch (msg) {
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, msg, wParam, lParam);
}
return 0L;
}

このプログラム自体はWeb上にいくらでも(解説つきで)転がっているので、
個々の処理が何をしているものなのか、説明は割愛させていただく。

ではコンパイルしてみよう。
Eclipseのメニューから「プロジェクト」→「すべてビルド」を選択すると、
コンパイル・ビルド・リンクが自動的に行われ、
問題なければ実行ファイルがプロジェクトのDebugフォルダに出力される。

で、コンパイルしてみたら、エラーが表示された。
Eclipseのエラー表示ビューに、こう表示されている。


undefined reference to `__imp_GetStockObject' SampleWindow.cpp /Sample/src 行 19 C/C++ 問題

原因が分からないときはとりあえずググる。
GetStockObjectというAPIが、gdi32に入っているので、これをリンクしないといけないらしい。
MinGWだけでコンパイルするなら、コンパイルオプションに-lgdi32を加えればよいだろう。

Eclipseでリンクの設定を行う場合、プロジェクトエクスプローラーでプロジェクト(ここではSample)を選択し、
メニューの「プロジェクト」→「プロパティー」を選択する。
現れたウィンドウの左側にあるツリービューから「MinGW C++ Linker」→「ライブラリー」を選択し
追加ボタンを押して現れるウィンドウにgdi32を入力すればOK。

link_gdi32.png

では再びコンパイルしてみよう。今度はエラーは表示されない。
Debugフォルダに、Sample.exeが出力されている。

いざ実行!
...してみたが、ウィンドウらしきものは表示されない。代わりにコマンドプロンプトが表示されている。

Eclipseからでもこのファイルを実行できる。
メニューの「実行」→「実行構成」で実行構成ウィンドウを表示し、
実行構成を作成すれば、自動的にこのファイルを実行する構成ができるだろう。
もちろん、Eclipseから実行しても、ウィンドウらしきものは表示されない。

ググってみたところ、MinGWでウィンドウを表示するプログラムを作成する場合、
コンパイルオプションに-mwindowsをつけなければならないようだ。
それでは先程の要領で、-mwindowsをコンパイルオプションに追加してみよう。
ただし、今度は-lオプションではないため、
「MinGW C++ Linker」→「その他」を選択し、
リンカー・フラグ(分かりにくいが、ウィンドウの右上にある)欄に-mwindowsを追加する。

再度コンパイルし、出力されたSample.exeを実行してみよう。

味もそっけもないウィンドウ

ようやくウィンドウが表示された。今度はコマンドプロンプトも表示されていない。