前回までの記事。

メイン画面 コントロール配置
さて、メイン画面に必要なコントロールを置いていきます。
今回必要なのは、
・リストコントロール
・カテゴリー用のコンボボックス
・編集ボタン
・履歴ボタン
・コンボボックス用チェックボックス
・ログイン情報用スタティックテキスト
です。
設計の時点で、チェックボックスはつけてなかったのですが、
「全件検索」するのに必要だと判断して、急遽追加です。
カテゴリーの中に「全件」ってつけてもいいですが、
なんかあまり見栄えよろしくないなぁって思っちゃいました…。
中央にあるデフォルトのスタティックテキストは邪魔なので削除。
ついでに、今回OKボタンも使わないので削除。
Deleteで消せます。
こんな感じに仕上げます。

設定関連で少しだけ。
コンボボックスは、デフォルトの「種類」が「ドロップダウン」なので、
「ドロップダウンリスト」に変更。
要は、コンボボックスの中身編集不可にします。
リストコントロールは、デフォルトの「ビュー」が「Icon」なので、
「レポート」に変更。
後は特に変えてないですかね。
キャンセルボタンは、キャプションだけ「ログアウト」に変更しました。
リソースファイルだとこんな感じです。
IDD_INVENTORYMANAGEMENTSYSTEM_DIALOG DIALOGEX 0, 0, 320, 200
STYLE DS_SETFONT | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME
EXSTYLE WS_EX_APPWINDOW
FONT 9, "MS UI Gothic", 0, 0, 0x1
BEGIN
PUSHBUTTON "ログアウト",IDCANCEL,240,170,50,14
LTEXT "ここにユーザ情報が入ります",IDC_STATIC_MAIN_USERINFO,20,20,90,8
CONTROL "全件検索",IDC_CHECK_MAIN_ALL,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,150,20,45,10
COMBOBOX IDC_COMBO_MAIN_CATEGORY,200,20,75,30,CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP
CONTROL "",IDC_LIST_MAIN_PRODUCT,"SysListView32",LVS_REPORT | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,20,50,280,110
PUSHBUTTON "編集",IDC_BUTTON_MAIN_EDIT,40,170,50,14
PUSHBUTTON "履歴",IDC_BUTTON_MAIN_LOG,140,170,50,14
ENDメイン画面制御
ユーザ関連制御
今回は、管理者ユーザと一般ユーザがいますので、その制御を。
「編集」ボタンは管理者しか使えません。
一般ユーザは、ボタンを非表示に。
後、ユーザ情報のスタティックテキスト。
ここも、「Hello! ○○」のように変更しましょう。
ユーザ名は既に前回、ゲットできてるのでね。
InventoryManagementSystemDlg.cppを開きます。
(InventoryManagementSystem.cppではないです)
OnInitDialogがあるので、初期化処理を入れます。
BOOL CInventoryManagementSystemDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
// 略
// TODO: 初期化をここに追加します。
// 画面キャプション
SetWindowText(_T("在庫管理システム"));
CInventoryManagementSystemApp* pApp = static_cast<CInventoryManagementSystemApp*>(AfxGetApp());
// ユーザ名表示
CString strText = _T(""); // ひとまず初期化
strText.Format(_T("Hello! %s"), pApp->strGetUserName()); // ユーザ名取得
CStatic* pStatic = (CStatic*)GetDlgItem(IDC_STATIC_MAIN_USERINFO); // ポインタ取得
pStatic->SetWindowText(strText); // スタティックに、さっきのテキストを入れる
//ボタン制御
CButton* pButton = (CButton*)GetDlgItem(IDC_BUTTON_MAIN_EDIT); // 編集ボタンポインタ取得
if (pApp->bGetUserAdmin() == TRUE)
{
// 管理者ユーザだった
pButton->ShowWindow(SW_SHOW); // ボタン表示
}
else
{
// 一般ユーザだった
pButton->ShowWindow(SW_HIDE); // ボタン非表示
}
return TRUE; // フォーカスをコントロールに設定した場合を除き、TRUE を返します。
}- 10行目:画面キャプションを入れてます。
- 12行目:ポインタゲット。
- 16行目:ユーザ名を取得、テキストを作ってます。
- 17~18行目:スタティックテキストのポインタ取得、テキストをセットしています。
- 21~31行目:同じくボタンのポインタゲット。
管理者の場合はSW_SWOWで表示、一般の場合はSW_HIDEで非表示。
ボタンはデフォルトで表示されるので、正直25行目はいらないのですが、
明示的に入れてます。
ひとまずビルド。
管理者ユーザ(ID=00000)と、一般ユーザ(ID=12345)でそれぞれ入ってみました。
管理者ユーザ

一般ユーザ

ちゃんと見た目、別れてくれてますね。
なお、これが実現したかっただけなので、
もう一般ユーザでログインすることはないかもしれません…。
カテゴリー作成
カテゴリーには、ヘッダーに定義したものをコンボボックスに入れます。
チェックボックスにチェックが入ってる場合、
コンボボックスに触れることができず、
チェックが入っていない場合は操作可能に。
まず、InventoryManagementSystemDlg.hに定義を少し書きます。
class CInventoryManagementSystemDlg : public CDialogEx
{
// 略
private:
CButton m_ctrlCheckSearchAll; // チェックボックス
CComboBox m_ctrlComboCategory; // コンボボックス
private:
void SetComboStatus(); // コンボボックス状態設定
};関数は、次のような動きを予定。
・チェックが入っている場合⇒コンボボックスは操作不可、何も選択されていない状態に(リセット)
・チェックが入っていない場合⇒コンボボックスは操作可、一番上の項目をセット
続きまして、リソースビューからダイアログを表示し、
チェックボックス、コンボボックスをダブルクリックでもして関数を自動挿入。
ついでに、編集ボタンと履歴ボタンも今のうちに自動追加。

こんな感じに4つ追加されます。
InventoryManagementSystemDlg.cppのDoDataExchangeに、
さっきの変数とコントロールIDを紐づけ。
void CInventoryManagementSystemDlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
DDX_Control(pDX, IDC_CHECK_MAIN_ALL, m_ctrlCheckSearchAll); // チェックボックス
DDX_Control(pDX, IDC_COMBO_MAIN_CATEGORY, m_ctrlComboCategory); // コンボボックス
}ログインの時は、エディットボックスの中身のテキストとCString変数を紐づけだったので
DDX_Text()でしたが、
今回はコントロール操作をしたいのでDDX_Control()。
続いて、OnInitDialogに初期設定を。
コンボボックスに項目を入れ、初期は全件検索にチェックが入っている状態にします。
さっきのユーザ情報の直下にでも入れましょう。
BOOL CInventoryManagementSystemDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
// 略
for (int iComboId = 0; iComboId < pApp->iGetCategoryCnt(); iComboId++)
{
// コンボボックスにテキスト追加
m_ctrlComboCategory.AddString(pApp->strGetCategory(iComboId));
}
m_ctrlCheckSearchAll.SetCheck(BST_CHECKED); // チェックしました
SetComboStatus();
return TRUE; // フォーカスをコントロールに設定した場合を除き、TRUE を返します。
}pAppは、さっきユーザ関連制御で追加した12行目の変数を使用しています。
一気に書いちゃってますが、for文のpApp->iGetCategoryCnt()は3が返ってきます。
pApp->strGetCategory(iComboId)で、配列iComboId番目のカテゴリーを返し、
AddStringでコンボに追加、って感じです。
13行目でチェックボックスにチェックを入れます。
因みにチェックを外したいときは、引数をBST_UNCHECKEDにします。
後は、コンボボックスの状態を設定する関数を呼応。
続いて、その関数作成です。
自動関数4つ追加したので、その真下でいいでしょう。
/**
* @fn
* @brief コンボボックスの状態を制御します
* @param なし
* @return なし
*/
void CInventoryManagementSystemDlg::SetComboStatus()
{
// メンバ変数を更新
UpdateData(TRUE);
if (m_ctrlCheckSearchAll.GetCheck() == BST_CHECKED)
{
// チェックが入った
m_ctrlComboCategory.EnableWindow(FALSE); // 操作不可
m_ctrlComboCategory.SetCurSel(-1); // これで、何も選択されていない状態に
}
else
{
// チェックが入っていない
m_ctrlComboCategory.EnableWindow(TRUE); // 操作可
m_ctrlComboCategory.SetCurSel(0); // 一番先頭が選択されます
}
//メンバ変数の状態を、コントロールに反映
UpdateData(FALSE);
}さっきからたまに書いてあるUpdateDataについて今更。
これはDoDataExchangeで紐づけしたIDと変数を更新させる作業です。
引数をTRUEにすると、コントロールの状態を変数に反映させます。
すなわち今回だと、チェックボックスの変数m_ctrlCheckSearchAllに
「いま紐づけされてるコントロール(チェックボックス)の状態を差し上げます」
みたいなやり取りが行われ、
m_ctrlCheckSearchAllに最新のチェック状態が反映されます。
なので、12行目のようなif文で正しい分岐に入れます。
引数FALSEはその逆。
メンバ変数の状態を、実際のコントロールへ反映させます。
16行目で操作不可をメンバ変数に作用させてますが、
UpdateData(FALSE)によりコントロールが操作不可状態へとできます。
EnableWindowは操作可能不可能を設定します。
SetCurSelは、何番目の項目をコンボボックスにセットするか。
-1にすると、何も選択されていない状態になります。
最後に、チェックボックスが押されるたびに呼ばれる関数、
OnBnClickedCheckMainAll()を修正。
さっき自動追加された奴ですね。
たった1文追加します。
/**
* @fn
* @brief チェックボックスが変更されるたびに呼ばれる
* @param なし
* @return なし
*/
void CInventoryManagementSystemDlg::OnBnClickedCheckMainAll()
{
// TODO: ここにコントロール通知ハンドラー コードを追加します。
SetComboStatus(); // コンボボックスの状態を変更
}10行目を追加。
さっき作った関数を読んでるだけです。
これで、チェックボックスが操作されるたびSetComboStatusが呼ばれ、
コンボボックスの状態が遷移されます。
さ、ビルド。
まず初期状態。

チェックが入り、コンボボックスは操作不可。
何も選択されてませんね。
ここでチェックを外すと。

コンボボックスが触れるようになりました。
初期値は一番最初の項目です。
コンボボックスの中身も、前回設定した値が入ってます。

もう一度チェックを付けると、
コンボボックスは操作不可能、項目はリセットされました。
注意すべきなのは、項目の順番が違いますね。
配列では、Food、Stationery、Electronic equipment、の順でしたが。
これはコンボボックスさん、
自動で昇順にする機能があるからです。
(これデフォルトで必要かねぇ…??)
忘れてました…。
今回別に順番はどうでもいいのですが、
一応直しておきます。
コンボボックスのプロパティを開き、
「並び替え」を「False」に設定します。
再度ビルド。

よろし。
編集選択画面作成
メイン画面はひとまず置いときます。
処理を作ったところで、今なにもデータがないので…。
なので、データを作成する画面を作ります。
まずは、作成・編集・削除の選択画面から。
Enum追加
最初にしておきたいことを。
MySQLManager.hを開き、enumを定義します。
作成、編集、削除の列挙です。
クラスの外に定義。
typedef enum
{
enCreate = 0, // 作成
enEdit, // 編集
enDelete, // 削除
}EditSelectMode;
class CMySQLManager
{
//略これを、ダイアログ間で共有して、
どのモードで編集するのか(INSERT? UPDATE?)決めます。
ダイアログ作成
ログイン画面と同様、リソースビューからDialogを右クリック、
リソースの追加から「新規作成」をクリック。
ダイアログが作成されます。
一番最初にすることは、ID変更。
今回はIDD_DIALOG_EDIT_SELECTにしておきます。
で、今回必要なのは
・ラジオボタン3つ(作成、編集、削除)
・OKボタン
・キャンセル(戻る)ボタン
ボタンは2つ、作成時に自動であるのでこのまま流用します。
ラジオボタンだけ配置。
ダイアログの大きさも小さめで大丈夫です。
こんな感じに。

お好みでキャプション付けてもいいですかね。
各ID名などは
IDD_DIALOG_EDIT_SELECT DIALOGEX 0, 0, 142, 115
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Dialog"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
DEFPUSHBUTTON "OK",IDOK,15,80,50,14
PUSHBUTTON "キャンセル",IDCANCEL,75,80,50,14
CONTROL "作成",IDC_RADIO_EDITSELECT_CREATE,"Button",BS_AUTORADIOBUTTON,20,20,30,10
CONTROL "編集",IDC_RADIO_EDITSELECT_EDIT,"Button",BS_AUTORADIOBUTTON,20,40,30,10
CONTROL "削除",IDC_RADIO_EDITSELECT_DELETE,"Button",BS_AUTORADIOBUTTON,20,60,30,10
ENDクラス、ファイル作成
ボタンでもクリックして、クラス作成のダイアログを出現させます。

これでOKボタン。
EditSelect.hとEditSelect.cppが作成されます。
表示メニューから「クラスビュー」を選択。
CEditSelectを選択し、プロパティ画面へ。
オーバーライド項目を選択し、OnInitDialogをAdd。
(ログイン画面作成時参照)
最後に準備として、リソースビューから画面を開き、
OKボタンをダブルクリック。
OKボタンが押されたときに呼ばれる関数OnBnClickedOkが出来上がります。

さぁ、少し処理を書きます。
この画面は、さほどの処理を書きません。
どの状態が選択されたか知りたいだけなので。
include
MySQLManager.hをインクルードします。
とは言っても、このダイアログでSQLコンストラクタは作りません。
先の、enumを使いたいためだけです。
(だったら、別のところにenum定義するべきかなぁ…?)
今回はEditSelect.hにインクルードさせます。
#include "MySQLManager.h"ヘッダーにメンバ変数を定義するために、
先のenumが必要だからです。
メンバ変数定義
コントロールの変数定義です。
そして、どのラジオボタンが選択されたかのゲット関数。
class CEditSelect : public CDialogEx
{
// 略
private:
CButton m_ctrlRadioESCreate; // 作成ラジオボタン
CButton m_ctrlRadioESEdit; // 編集ラジオボタン
CButton m_ctrlRadioESDelete; // 削除ラジオボタン
EditSelectMode m_enSelectMode; // ラジオボタン選択状態
public:
EditSelectMode enGetSelectMode() { return m_enSelectMode; } //選択状態取得
};今回はこれだけ。
コンストラクタ
enumのメンバ変数を用意したので、初期化します。
初期値は作成。
CEditSelect::CEditSelect(CWnd* pParent /*=nullptr*/)
: CDialogEx(IDD_DIALOG_EDIT_SELECT, pParent)
{
m_enSelectMode = enCreate; // 作成
}DoDataExchange
例のごとく、紐づけ開始です。
void CEditSelect::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
DDX_Control(pDX, IDC_RADIO_EDITSELECT_CREATE, m_ctrlRadioESCreate); // 作成
DDX_Control(pDX, IDC_RADIO_EDITSELECT_EDIT, m_ctrlRadioESEdit); // 編集
DDX_Control(pDX, IDC_RADIO_EDITSELECT_DELETE, m_ctrlRadioESDelete); // 削除
}OnInitDialog
画面キャプションと、初期選択状態を設定します。
初期は、「作成」に選択させます。
BOOL CEditSelect::OnInitDialog()
{
CDialogEx::OnInitDialog();
// TODO: ここに初期化を追加してください
SetWindowText(_T("編集選択画面"));
m_ctrlRadioESCreate.SetCheck(BST_CHECKED); // 作成にチェック
//コントロールに反映
UpdateData(FALSE);
return TRUE; // return TRUE unless you set the focus to a control
// 例外 : OCX プロパティ ページは必ず FALSE を返します。
}チェックボックスと同様、SetCheck(BST_CHECKED)でチェックを付けれます。
OKボタン押下時
最後に、OKボタン押下時。
enumメンバ変数を決定し、メインダイアログへ状態を渡します。
/**
* @fn
* @brief OKボタン押下時の処理
* @param なし
* @return なし
*/
void CEditSelect::OnBnClickedOk()
{
// TODO: ここにコントロール通知ハンドラー コードを追加します。
// まずはメンバ変数更新
UpdateData(TRUE);
if (m_ctrlRadioESCreate.GetCheck() == BST_CHECKED)
{
// 作成にチェックがついていた
m_enSelectMode = enCreate;
}
else if (m_ctrlRadioESEdit.GetCheck() == BST_CHECKED)
{
// 編集にチェックがついていた
m_enSelectMode = enEdit;
}
else if (m_ctrlRadioESDelete.GetCheck() == BST_CHECKED)
{
// 削除にチェックがついていた
m_enSelectMode = enDelete;
}
else
{
// 何もチェックが入っていない→ありえない
AfxMessageBox(_T("システムエラー!!\r\n管理者へ連絡を!!!"), MB_ICONERROR);
return; // 抜ける
}
CDialogEx::OnOK();
}チェックボックスなので、どれか一つにしかチェックが入らない仕様です。
チェックがついている状態に応じてメンバ変数状態を変更させます。
else分岐、この作りだとまず入らないですが、
念のため作りましょう。
ダイアログ呼び出し
このダイアログを呼び出します。
InventoryManagementSystemDlg.cppへ。
ヘッダーをインクルードします。
#include "pch.h"
#include "framework.h"
#include "InventoryManagementSystem.h"
#include "InventoryManagementSystemDlg.h"
#include "afxdialogex.h"
#include "MySQLManager.h" // MySQL使用
#include "EditSelect.h" // 編集選択画面6,7行目追加です。
今回、6行目は必要ないのですが、
どうせ後々インクルードする羽目になるので、
今のうちに作っておきます。
そして、編集画面が押された時に呼ばれる関数
OnBnClickedButtonMainEdit内を編集。
/**
* @fn
* @brief 編集ボタンが押された
* @param なし
* @return なし
*/
void CInventoryManagementSystemDlg::OnBnClickedButtonMainEdit()
{
// TODO: ここにコントロール通知ハンドラー コードを追加します。
BOOL bSelectOK = FALSE; // 選択画面でOKを押された?
EditSelectMode enSelectMode= enCreate; // 選択状態初期化
CEditSelect dlgEditSelect;
if (dlgEditSelect.DoModal() == IDOK)
{
bSelectOK = TRUE; // OKが押された
enSelectMode = dlgEditSelect.enGetSelectMode(); // 向こうのメンバ変数を取ってくる
}
}DoModal関数で画面を開き、OKボタンが押されればif文内に入ります。
今回、メイン画面から選択画面を開き、
OKを押されると一度選択画面を閉じる仕様にします。
で、続けて編集画面が出現するように。
つまり、
メイン画面⇔編集選択画面⇒編集画面
です。
(編集画面からメインにも戻れます。)
なんで
メイン画面⇔編集選択画面⇔編集画面
にしないのか、ですが。
履歴画面ではそのように作ります。
2つとも同じ動きすると
ま、早い話。
作るのに飽きちゃいます。
なのでモチベを保つために、
編集と履歴、敢えて違う作りにしました。
実際の業務では、
お客さんが求めるものを作りましょうね。
終わりに
メイン画面の機能を少し追加と、
編集選択画面を作りました。
1記事1画面が限界ですかね。
これ位だったら、30分もかけずに製造できるレベルになれてるの、
昔と比べたらだいぶ進歩してるよなぁってちょっと感慨深い。
次回は編集画面から、
実際に商品情報を操作します。

今回はここまで。
コメント