初期表示のデザイン
- ◆プログラム作成ガイド
- ◆プログラムの開始
- ◆初期表示のデザイン
- ◆データの入力方法
- ◆クラスの作り方
- ◆データをメモリーに
- ◆ファイル読み書き
- ◆データの保存方法
- ◆ツリービューマウス右
- ◆画面表示インプリメント
- ◆複数のページ印字
- ◆DLL化する
- ◆エクセル似の入力
ここに紹介するプログラムは、WIN
XP 2003/Windows 7 (32bits) - OS 環境でマイクロソフトのVisual Studio 2010 で作ったものです。
Windows XP/ Windows 7(32bits) での動作は確認済みですが…
プログラムが起動されても、初期表示デザインは重要です。
この表現方法も初心者は相当悩むのではないでしょうか。
スタートダイアログ後の画面
この部分もなかなか良いアイデアが浮かばず、遅々として進みませんでした。
当初は、タブコントロールを使い各タブに入出力機能を貼り付けることも考えたのですが、使い勝手があまり思わしくなく、途中でやり直しとしました。
皆さんもこの辺でつっかかっている事が多いのではと思います。
プロのプログラマーも、ウインドウの機能を知り尽くしているグラフィックデザイナーがこの辺を決めているようです。
左のツリービューは良いとしても右の画面は通常のビューとし計算結果などを表示することにしました。
但し複数のページを使う可能性があるので、スクロールビューを使うことにしました。
VisualC++では、エクスプローラ風の機能をコーディングするには、VisualC++ Componentがあります。
「「プロジェクト」→「プロジェクトへ追加」→「コンポーネントおよびコントロール」→「VisualC++ Component」を選べば基本コードが作れます。
しかし、右の画面はリストビューとなってしまうためマニュアルでコードを変更する必要があります。
標準で作成したシングルのビューを持ったコードを見ながら、不要なコードを削除したり変更をすればそれほど難しくはないでしょう。
ヘッダーファイルの一部分を書きます。 インプリメントファイルの頭のコードです。
//////////////////////////////////////////////
// CAutoACJView
IMPLEMENT_DYNCREATE(CAutoACJView, CScrollView)
BEGIN_MESSAGE_MAP(CAutoACJView, CScrollView)
ON_COMMAND(ID_MENU_8PTFONT, On8ptfont)
ON_COMMAND(ID_MENU_10PTFONT, On10ptfont)
// 標準印刷コマンド
ON_COMMAND(ID_FILE_PRINT, CScrollView::OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_DIRECT, CScrollView::OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_PREVIEW, CScrollView::OnFilePrintPreview)
ON_WM_MOUSEWHEEL()
ON_UPDATE_COMMAND_UI(ID_MENU_10PTFONT, &CAutoACJView::OnUpdateMenu10ptfont)
ON_UPDATE_COMMAND_UI(ID_MENU_8PTFONT, &CAutoACJView::OnUpdateMenu8ptfont)
END_MESSAGE_MAP()
////////////////////////////////////////////////
// CAutoACJView コンストラクション/デストラクション
CAutoACJView::CAutoACJView()
{
m_ArrayPrint.RemoveAll();
m_nIndex = 0; // Docクラスから転送されたm_ArrayAircon[m_nIndex]
m_rectMonitor = 0; // 画面の大きさ
m_nMaxPage = 0; // 印刷ページ数
m_nHeight = 0; // 1行の高さ
m_nWidth = 0; // 1文字の平均幅
m_nMaxRow = 0; // 1ページに印字可能な行数
m_nLines_Detail =0; // 詳細データの印字行数
m_nFont = 0; // フォントの番号
m_bPrintDetail = FALSE; // 詳細データ印刷フラグ
}}
左のツリービューに表す項目
負荷計算は複数の部屋が対象となりますが、各部屋には壁・窓・その他の条件別々に表す必要あります。 壁・窓・その他はどの部屋にも共通しているため、ツリーで全体を表すのがよさそうです。
親のノード(ツリーの各項目)をプロジェクト名としました。すぐ下のサブノードは、対象の部屋名を現すことにしました。 更にその下のサブサブノードは、「壁・窓・その他」としました。
プログラム開始時点では、親ノードが表示されているだけです。
対対象の部屋を入力するのは、ツールバーのボタンで開始することにしました。
この部屋名入力ボタンを押すと、専用のダイアログが現れ必要な項目を入力し、OKボタンを押すと自動的に、入力した部屋に対応するノードがツリービューに階数のノードと共に追加されます。
壁・窓・その他の項目は、部屋のノードが追加されると自動的にその下に配置されるようにしました。
部屋の情報は、階数とは関係なくランダムで入力されることがあると思い、階数で自動的にソート出来る必要もありそうです。
壁・窓・その他のデータ入力は、色々考えたのですが、対象のサブサブノードをマウスでクリックすることで、専用のダイアログが表示されるようにしました。
負荷計算開始は?
色々とアイデアを考えましたが、部屋名のノードをマウス左クリックが違和感なく使えそうです。
視覚的に違和感がなく操作が出来そうです。これ以外にも、新しい機能を現在徐々に追加しています。
-
親ノードをマウスで右クックは、全ての部屋の負荷計算を自動的に連続で行う。
-
壁・窓・その他のノードをマウスで右クリックは、データのコピーおよび貼り付け。
まだまだ色々な追加機能をデザインすることが出来ると思います。
ツリービューに表示するアイコンなど
ここのあたりは画面のデザインセンスが大きくものを言うのでしょう。
親ノードは、プログラムのシンボルである太陽マークを選択・未選択状態の二種類作りました。
階数ごとに分けて表示した方が見やすいと思い、ファイルフォルダでおなじみのアイコンを、このノードに使うことにしました。
部屋を表すノードは、階数ノード下のサブサブノードとし、専用のアイコンを作りました。
負荷計算がされていない時と、完了した時(色をグリーンにする)を区別できるようなアイコンとしました。
壁・窓・その他は、専用のアイコンをつくり選択状態と・未選択状態の2種類を作りました。

さあ コーディングです
ツリービューコントロールは操作が複雑で、メンバ関数の引数にはふんだんに特殊な構造体が多用され理解が難しいコントロールす。
VisualC++5を購入した時、添え付けられていた入門書「VisualC++5.0プログラム入門―アスキー出版局」のサンプル(住所録)が相当参考になりました。
まずはツリービューのスタイルを決めます。
CCLeftViewクラス(MFCで自動的に作成されたツリービューコントロールクラス)で、PreCreateWindowをオーバーライドしこの中に、指定します。
cs.style|=TVS_HASLINES|TVS_LINESATROOT|TVS_HASBUTTONS|TVS_SHOWSELALWAYS |TVS_EDITLABELS;
詳しくは、MFCのヘルプを参照して下さい。
MFCのリソースエディタでツリービューに表示するアイコンを作成します。 ファイルホルダのアイコンなどは、MFCの標準のアイコンを拝借しました。
OnInitialUpdate( )関数で、ツリービューの初期設定を行います。
-
初期設定は、ツリービューに表示されるアイコンをメモリーに呼び込む。
-
階数別にソートする為のアレーを初期化する。
-
ツリービューを初期化する
-
親ノードの表示
などです。
入力されたデータに基づいてツリービューを更新するのは、OnUpdate関数です。 MFCのドキュメントビューアーキテクチュア仕様に基づいてドキュメントクラスで UpdateAllViews関数が呼ばれると、それに対応する動作が行われます。
OnUpdate、UpdateAllViews 関数の引数の使い方はヘルプを一読しても難解です。 Viewクラスは、LeftViewとScrollViewの2種類を使っています。
Documentクラスは1種類ですが、内部関数に多くの UpdateAllViews 関数が使われています。 例えば、部屋名を追加した場合 UpdateAllViews 関数が呼ばれるのでが、この時は、LeftView のOnUpdateが呼ばれるようにして、ビューを更新します。
この辺のコーディングは、 MFCのサンプルである “住所録” が非常に参考になりました。 部屋データの入力で階数が入力されるとき、 新しい階数のときは、自動的にサブノードが作られその下に部屋のノードが作成されます。
階数を表すサブノードは、自動的にソートされて表示する機能は相当悩されました。 MFCの標準機能を調べてみたのですが、該当が見つからず自分で考えたコーディングをしました。
ドキュメントクラスに TreeArray というメモリーを作り、各部屋と階数の情報を蓄え、この値をソートしてツリービューにフィードバックする方法です。 もっと良い方法があるかもしれませんので皆様考えてみてください。
以下は、ビューを更新するためのコードです。
/////////////////////////////////////////////////
// Tree の作成・編集
void CLeftView::OnUpdate(CView* pSender, LPARAM lHint, CObject* pHint)
{
char pszText[80] = _T("");
char pszPjName[80] = _T("");
CTreeCtrl& treeCtrl = GetTreeCtrl();
CAutoACJDoc* pDoc = GetDocument();
SetIconImage( treeCtrl );
if( pSender != this && pHint != NULL ){
// メインアイテム挿入
if( !pDoc->m_TreeArray.GetSize( )){
treeCtrl.DeleteAllItems(); // 既存のツリーアイテムを削除
CString str = pDoc->m_szPjname; // プロジェクト名取得
strcpy_s( pszPjName, str );
InsertItem( NULL, _T( pszPjName ), NULL, -1 );
}
CAirconData* pData = ( CAirconData* )pHint;
CString strFloor = pData->m_storey;
CString strRoom = pData->m_roomname;
// 階数アイテム挿入
m_hTreeFloor = SetFloorItem(treeCtrl, pHint, strFloor);
strcpy_s(pszText, strRoom);
BOOL bLeftView = FALSE;
HTREEITEM hTreeRoom;
if( pDoc->m_Result.GetSize()) bLeftView = pDoc->m_Result[lHint].m_bIndex;
if( bLeftView == TRUE ){// 負荷計算が終了した条件でアイコンを変更する
hTreeRoom = InsertItem( m_hTreeFloor, pszText, TREE_ROOM_DONE, lHint );
}
else {
hTreeRoom = InsertItem( m_hTreeFloor, pszText, TREE_ROOM, lHint );
}
InsertItem( hTreeRoom, _T("1 Wall"), TREE_WALL, lHint );
InsertItem( hTreeRoom, _T("2 Glass"), REE_GLASS, lHint );
InsertItem( hTreeRoom, _T("3 Other"), TREE_OTHER, lHint );
}
}
ツリービューのノードをマウスクリックした時の動作
参考にしたMFCのサンプルでは、TVN_SELCHANGED(OnSelchanged) 関数でこのへんを行っていますが、残念ながらマウス左クリックにしか対応しません。 右クリックは、WM_RBUTTONDOWN(OnRButtonDown) 関数で行いました。
しかしこの関数で得られるのは、マウスの指している座標と、マウスが指しているポイントが、アイコンかラベルかの判断するフラグのみです。 ヘルプやメーリングリストを読み漁り、HitTest
と言う関数が見つかりました。 これでマウスがクリックしたノードの種類を調べることで機能出来ることになりました。
/////////////////////////////////////////////////
// Left View でマウス右ダウンの操作p
void CLeftView::OnRButtonDown(UINT nFlags, CPoint point)
{
UINT flag; // マウスが指しているポイントがアイコンかラベルかなどの判断
int nImage = -1;
int nSelectedImage = -1;
BOOL bRet;
CTreeCtrl& Tree = GetTreeCtrl();
HTREEITEM hItem = Tree.HitTest( point, &flag );
CDWordArray contents;
int count = GetContents( hItem, &contents );
CAutoACJDoc* pDoc = GetDocument();
int n = 0;
if( flag & TVHT_ONITEMLABEL ){ // ラベルをクリックした時のみ
switch( nImage ){
case 0: // トップノード選択
pDoc->AllACLoad();
break; 省略
ページ先頭に戻る
最終更新日: 2020/02/04