前回までの記事。
履歴検索条件画面
今回は履歴検索。
検索をかけるために必要な条件を設定する画面を作成します。
画面追加
4度目の画面追加。
リソースビューからDialogを右クリック、新規追加します。

プロパティから、ダイアログIDはIDD_DIALOG_LOG_CONDITIONに変更。
今回検索条件として、
商品ID、カテゴリー、ログ時間を設定します。
で、その条件を付けて検索をかけるのか・かけないのかの判定で、
チェックボックスを追加させます。
ざっくり、こんな感じに。

本当は、商品IDや年月日は2種類用意して、
範囲検索した方がいいのですが、(商品ID○○○○○~△△△△△)
ひとまず面倒なのでこれでご勘弁…。
準備したのは、
・チェックボックス3種
・エディットボックス4種
・コンボボックス4種
・スタティックテキスト3種
です。
(ボタンはデフォルトで存在します)
プロパティ設定で必要なのは
・コンボボックス⇒並び替えFalse、種類はドロップダウンリスト
・エディットボックス⇒数字True
コード表示するとこんな感じです。
IDD_DIALOG_LOG_CONDITION DIALOGEX 0, 0, 167, 168
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,140,50,14
PUSHBUTTON "キャンセル",IDCANCEL,90,140,50,14
CONTROL "商品ID条件",IDC_CHECK_LOGCONDITION_PRODUCTID,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,20,20,100,10
EDITTEXT IDC_EDIT_LOGCONDITION_PRODUCTID,20,35,120,14,ES_AUTOHSCROLL | ES_NUMBER
CONTROL "カテゴリー条件",IDC_CHECK_LOGCONDITION_CATEGORY,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,20,65,100,10
COMBOBOX IDC_COMBO_LOGCONDITION_CATEGORY,20,80,120,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
CONTROL "操作年月日条件",IDC_CHECK_LOGCONDITION_TIME,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,20,100,100,10
EDITTEXT IDC_EDIT_LOGCONDITION_TIME_Y,20,115,25,14,ES_AUTOHSCROLL | ES_NUMBER
LTEXT "年",IDC_STATIC_LOGCONDITION_TIME_Y,45,115,8,8
EDITTEXT IDC_EDIT_LOGCONDITION_TIME_M,60,115,15,14,ES_AUTOHSCROLL | ES_NUMBER
LTEXT "月",IDC_STATIC_LOGCONDITION_TIME_M,75,115,8,8
EDITTEXT IDC_EDIT_LOGCONDITION_TIME_D,90,115,15,14,ES_AUTOHSCROLL | ES_NUMBER
LTEXT "日",IDC_STATIC_LOGCONDITION_TIME_D,105,115,8,8
ENDクラス作成
リソースビューの画面から、
適当にコントロールをダブルクリック、
クラス作成画面を呼びます。

今回のクラス名はCLogConditionにします。
OKボタンで作成。
初期準備
クラス作成ができたら、
クラスビューを開きます。
CLogConditionができているので、
選択後、プロパティへ。
オーバーライドを選択し、
OnInitDialogをAdd。
続いて、リソースビューから再度画面を開き、
OKボタン、チェックボックス3種をダブルクリック。
操作したときに呼ばれる関数を自動追加します。
これで、今5種類の関数が追加されました。

ヘッダー定義
さ、LogCondition.hで必要な変数・関数を定義します。
class CLogCondition : public CDialogEx
{
// 略
private:
CButton m_ctrlCheckProductID; // 商品ID条件チェックボックス
CButton m_ctrlCheckCategory; // カテゴリー条件チェックボックス
CButton m_ctrlCheckTime; // 時間条件チェックボックス
CString m_strProductID; // 商品ID
CString m_strTimeYear; // 時間 年
CString m_strTimeMonth; // 時間 月
CString m_strTimeDay; // 時間 日
CEdit m_ctrlEditProductID; // 商品IDエディットボックス
CEdit m_ctrlEditTimeYear; // 時間 年 エディットボックス
CEdit m_ctrlEditTimeMonth; // 時間 月 エディットボックス
CEdit m_ctrlEditTimeDay; // 時間 日 エディットボックス
CComboBox m_ctrlComboCategory; // コンボボックス
private:
void SetEnableWindow(); // 各コントロール活性非活性切り替え
};処理を作成するとなると、後でもう少し関数など必要ですが、
ひとまずこれだけで。
コンストラクタ
ここからLogCondition.cppへ。
CString型のメンバ変数は初期化します。
CLogCondition::CLogCondition(CWnd* pParent /*=nullptr*/)
: CDialogEx(IDD_DIALOG_LOG_CONDITION, pParent)
{
m_strProductID = _T("");
m_strTimeYear = _T("");
m_strTimeMonth = _T("");
m_strTimeDay = _T("");
}DoDataExchange
コントロールIDと、各メンバ変数を紐づけます。
void CLogCondition::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
DDX_Control(pDX, IDC_CHECK_LOGCONDITION_PRODUCTID, m_ctrlCheckProductID);
DDX_Control(pDX, IDC_CHECK_LOGCONDITION_CATEGORY, m_ctrlCheckCategory);
DDX_Control(pDX, IDC_CHECK_LOGCONDITION_TIME, m_ctrlCheckTime);
DDX_Control(pDX, IDC_EDIT_LOGCONDITION_PRODUCTID, m_ctrlEditProductID);
DDX_Control(pDX, IDC_COMBO_LOGCONDITION_CATEGORY, m_ctrlComboCategory);
DDX_Control(pDX, IDC_EDIT_LOGCONDITION_TIME_Y, m_ctrlEditTimeYear);
DDX_Control(pDX, IDC_EDIT_LOGCONDITION_TIME_M, m_ctrlEditTimeMonth);
DDX_Control(pDX, IDC_EDIT_LOGCONDITION_TIME_D, m_ctrlEditTimeDay);
DDX_Text(pDX, IDC_EDIT_LOGCONDITION_PRODUCTID, m_strProductID);
DDX_Text(pDX, IDC_EDIT_LOGCONDITION_TIME_Y, m_strTimeYear);
DDX_Text(pDX, IDC_EDIT_LOGCONDITION_TIME_M, m_strTimeMonth);
DDX_Text(pDX, IDC_EDIT_LOGCONDITION_TIME_D, m_strTimeDay);
}まぁここは特記なし。
OnInitDialog
画面初期表示処理です。
BOOL CLogCondition::OnInitDialog()
{
CDialogEx::OnInitDialog();
// TODO: ここに初期化を追加してください
SetWindowText(_T("履歴条件画面"));
//初期状態は、チェックボックスにチェックなし状態
m_ctrlCheckProductID.SetCheck(BST_UNCHECKED);
m_ctrlCheckCategory.SetCheck(BST_UNCHECKED);
m_ctrlCheckTime.SetCheck(BST_UNCHECKED);
// カテゴリーセット
CInventoryManagementSystemApp* pApp = static_cast<CInventoryManagementSystemApp*>(AfxGetApp());
for (int iComboId = 0; iComboId < pApp->iGetCategoryCnt(); iComboId++)
{
// コンボボックスにテキスト追加
m_ctrlComboCategory.AddString(pApp->strGetCategory(iComboId));
}
// 文字数制限
m_ctrlEditProductID.SetLimitText(5); // 商品IDは5桁まで
m_ctrlEditTimeYear.SetLimitText(4); // 年は4桁まで
m_ctrlEditTimeMonth.SetLimitText(2); // 月は2桁まで
m_ctrlEditTimeDay.SetLimitText(2); // 日は2桁まで
UpdateData(FALSE);
SetEnableWindow(); // 活性非活性切り替え呼応
return TRUE; // return TRUE unless you set the focus to a control
// 例外 : OCX プロパティ ページは必ず FALSE を返します。
}- 9~11行目:チェックボックスはチェックしない状態(多分必要ない処理)
- 14~19行目:コンボボックスに項目追加
- 21~24行目:エディットボックスに入る最大桁数設定
- 28行目:制御関数呼応
という感じです。
コントロール制御関数
チェックボックスにチェックが入っていない場合、
その項目のコントロールは非活性にし、
チェックがついている場合は活性化させる算段です。
/**
* @fn
* @brief 各コントロールを活性・非活性を切り替える
* @param なし
* @return なし
*/
void CLogCondition::SetEnableWindow()
{
// 変数更新
UpdateData(TRUE);
// 商品ID条件
if (m_ctrlCheckProductID.GetCheck() == BST_CHECKED)
{
// チェックが入っている⇒入力するので活性化
m_ctrlEditProductID.EnableWindow(TRUE);
}
else
{
// チェックが入っていないので非活性
m_ctrlEditProductID.EnableWindow(FALSE);
m_strProductID = _T(""); // 初期化
}
// カテゴリー条件
if (m_ctrlCheckCategory.GetCheck() == BST_CHECKED)
{
// チェックが入っている⇒選択するので活性化
m_ctrlComboCategory.EnableWindow(TRUE);
m_ctrlComboCategory.SetCurSel(0); //最初の項目
}
else
{
// チェックが入っていないので非活性
m_ctrlComboCategory.EnableWindow(FALSE);
m_ctrlComboCategory.SetCurSel(-1); //初期値に設定
}
// 時間条件
if (m_ctrlCheckTime.GetCheck() == BST_CHECKED)
{
// チェックが入っている⇒入力するので活性化
m_ctrlEditTimeYear.EnableWindow(TRUE);
m_ctrlEditTimeMonth.EnableWindow(TRUE);
m_ctrlEditTimeDay.EnableWindow(TRUE);
}
else
{
// チェックが入っていないので非活性
m_ctrlEditTimeYear.EnableWindow(FALSE);
m_ctrlEditTimeMonth.EnableWindow(FALSE);
m_ctrlEditTimeDay.EnableWindow(FALSE);
m_strTimeYear = _T(""); // 初期化
m_strTimeMonth = _T(""); // 初期化
m_strTimeDay = _T(""); // 初期化
}
UpdateData(FALSE);
}大したことはやってなく、チェック状態によって
EnableWindow(TRUE)かEnableWindow(FALSE)を切り替えてるだけです。
チェックが外れたら、エディットボックスの中身は初期化。
コンボボックスも同様。
関数呼応箇所
で、この関数。
いつ呼ぶんだい、って話ですが。
OnInitDialogで呼ぶのは前述したとして、
チェックボックスに変更があるたびに呼応させます。
つまり、さっき自動追加した関数内で呼びます。
/**
* @fn
* @brief 商品ID条件チェックボックスが操作されると呼ばれる
* @param なし
* @return なし
*/
void CLogCondition::OnBnClickedCheckLogconditionProductid()
{
// TODO: ここにコントロール通知ハンドラー コードを追加します。
SetEnableWindow();
}
/**
* @fn
* @brief カテゴリー条件チェックボックスが操作されると呼ばれる
* @param なし
* @return なし
*/
void CLogCondition::OnBnClickedCheckLogconditionCategory()
{
// TODO: ここにコントロール通知ハンドラー コードを追加します。
SetEnableWindow();
}
/**
* @fn
* @brief 操作年月日条件チェックボックスが操作されると呼ばれる
* @param なし
* @return なし
*/
void CLogCondition::OnBnClickedCheckLogconditionTime()
{
// TODO: ここにコントロール通知ハンドラー コードを追加します。
SetEnableWindow();
}これだけ。
ダイアログ呼応
最後に、このダイアログを呼ぶために呼び元を修正。
InventoryManagmentSystemDlg.cppへ移動。
LogCondition.hをインクルードします。
#include "pch.h"
#include "framework.h"
#include "InventoryManagementSystem.h"
#include "InventoryManagementSystemDlg.h"
#include "afxdialogex.h"
#include "MySQLManager.h" // MySQL使用
#include "EditSelect.h" // 編集選択画面
#include "EditMain.h" // 編集画面
#include "LogCondition.h" // 履歴条件画面9行目追加。
そして、履歴ボタンが押されると呼ばれる関数
OnBnClickedButtonMainLogの中を変更します。
/**
* @fn
* @brief 履歴ボタンが押された
* @param なし
* @return なし
*/
void CInventoryManagementSystemDlg::OnBnClickedButtonMainLog()
{
// TODO: ここにコントロール通知ハンドラー コードを追加します。
CLogCondition dlgLogCondition;
dlgLogCondition.DoModal();
}ひとまずこれだけで大丈夫です。
動作確認
メイン画面から履歴ボタンを押し、初期表示は。

チェックが入ってなく、
エディットボックスやコンボボックスは非活性です。
ここで、商品ID条件にチェックを付けると

触れるようになりました。
文字を入れ、再度チェックを外すと
エディットボックスの中の文字が消えました。
コンボボックスの場合、チェックを付けると

項目が一番最初に設定され、
変更できるようになりました。
チェックを外すと、非活性に。
入力チェック
この画面で行う処理は、入力チェックだけです。
テーブルからSELECTするのはもう一つ先の、
履歴画面なので。
LogCondition.hを開き、
入力チェック用の関数を作ります。
private:
BOOL bInputCheck(); // 入力チェックLogCondition.cppにて、処理を書きます。
今回はそんな大した話ではなく。
長ったらしい処理書いてますが、
日付チェックが面倒なだけです。
こういうの、標準のAPI関数あってもいいと思うんだけどなぁ…。
/**
* @fn
* @brief エディットボックス入力チェック
* @param なし
* @return BOOL TRUE チェックOK
* FALSE チェックNG
*/
BOOL CLogCondition::bInputCheck()
{
// メンバ変数更新
UpdateData(TRUE);
// 商品IDのチェックボックスにチェックがある場合、入力チェック
if (m_ctrlCheckProductID.GetCheck() == BST_CHECKED)
{
//5桁未満はダメ
if (m_strProductID.GetLength() < 5)
{
AfxMessageBox(_T("Idが5桁ではありません。"), MB_ICONERROR);
return FALSE;
}
}
// コンボボックスは何も制御必要なし
// 操作年月日のチェックボックスにチェックがある場合、入力チェック
if (m_ctrlCheckTime.GetCheck() == BST_CHECKED)
{
BOOL bCheckResult = TRUE;
if (m_strTimeYear == _T("") || m_strTimeMonth == _T("") || m_strTimeDay == _T(""))
{
// 未入力だった
bCheckResult = FALSE;
}
// 年については何もチェックしない
// 西暦1年とかでも入力可。
int iMonth = _ttoi(m_strTimeMonth);
// 月は1~12まで。
if (bCheckResult == TRUE)
{
if (iMonth < 0 || 12 < iMonth)
{
// 存在しない月
bCheckResult = FALSE;
}
}
// 日はその月ごと
if (bCheckResult == TRUE)
{
int iYear = _ttoi(m_strTimeYear);
int iDay = _ttoi(m_strTimeDay);
switch (iMonth)
{
case 1:
case 3:
case 5:
case 7:
case 8:
case 10:
case 12:
if (iDay < 0 || 31 < iDay)
{
// 存在しない日
bCheckResult = FALSE;
}
break;
case 4:
case 6:
case 9:
case 11:
if (iDay < 0 || 30 < iDay)
{
// 存在しない日
bCheckResult = FALSE;
}
break;
case 2:
//うるう年判定
if ((iYear % 4 == 0 && iYear % 100 != 0) || (iYear % 400 == 0))
{
// うるう年だった
if (iDay < 0 || 29 < iDay)
{
// 存在しない日
bCheckResult = FALSE;
}
}
else
{
// うるう年じゃなかった
if (iDay < 0 || 28 < iDay)
{
// 存在しない日
bCheckResult = FALSE;
}
}
break;
default:
return FALSE;
}
}
if (bCheckResult == FALSE)
{
AfxMessageBox(_T("存在する日付が入力されていません。"), MB_ICONERROR);
return FALSE;
}
}
return TRUE;
}そしてこの関数は、OKボタンが押されたときに発動させます。
/**
* @fn
* @brief OKボタンが押された
* @param なし
* @return なし
*/
void CLogCondition::OnBnClickedOk()
{
// TODO: ここにコントロール通知ハンドラー コードを追加します。
//CDialogEx::OnOK();
if (bInputCheck() == TRUE)
{
// ここに、履歴ダイアログの処理を書く
}
}デフォルトである「CDialogEx::OnOK();」ですが、
これ実行されちゃうと画面が閉じちゃいます。
今回は、ボタンが押されると履歴画面が出てきて、
条件画面は残したいのでここはコメントアウト。
ついでにですが、OKボタン。
プロパティからキャプションを「検索」に変えておきます。
検索ボタンに。
履歴画面
ダイアログ作成
今度は履歴画面を作成。
同じようにダイアログを新規作成。
IDはIDD_DIALOG_LOG_MAINにします。
今回必要なのは、リストコントロールのみ。
このように配置。

リストコントロールのプロパティで、
ビューを「レポート」にします。
デフォルトで存在したOKボタンは「条件画面に戻る」に、
キャンセルボタンは「メイン画面に戻る」にしてもらいます。
つまり、メイン画面に戻るを押すと、
条件画面も同時に閉じるようにします。
ソースだと
IDD_DIALOG_LOG_MAIN DIALOGEX 0, 0, 339, 176
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 "条件画面に戻る",IDOK,60,150,60,14
PUSHBUTTON "メイン画面に戻る",IDCANCEL,190,150,60,14
CONTROL "",IDC_LIST_LOGMAIN,"SysListView32",LVS_REPORT | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,15,15,310,120
ENDクラス作成
続いてクラス作成。
適当にコントロールをダブルクリック。

クラス名はCLogMainにします。
これでOKボタン。
毎度おなじみ、
初期準備を行います。
今回は、クラスビューでCLogMainを選択後、
オーバーライドでOnInitDialgoをAddするだけ。
変数定義
さて、変数と関数定義です。
必要なのは、リストコントロール操作変数、検索の条件、リストの中身作成関数
そして今回はコンストラクタも作ります。
履歴検索条件画面から、抽出条件をこちらに持ってくるのですが、
3つ情報を渡してこなければいけないため、
流石に3つセット関数作るのは大変だと思った次第。
コンストラクタの引数に入れて渡します。
LogMain.hにて定義
class CLogMain : public CDialogEx
{
DECLARE_DYNAMIC(CLogMain)
public:
CLogMain(CWnd* pParent = nullptr); // 標準コンストラクター(デフォルトで存在)
//新規作成
CLogMain(CString strProductID, CString strCategory, CString strTime, CWnd* pParent = nullptr);
// 略
private:
CListCtrl m_ctrlListCtrlLog; // リストコントロール
CString m_strProductID; // 商品ID
CString m_strCateory; // カテゴリー
CString m_strTime; // 操作年月日
private:
void SetLogList(); // 履歴リスト作成
};8行目のコンストラクタを定義しました。
インクルード
ここからLogMain.cppへ。
まず、CMySQLManagerを使いたいため、ヘッダーインクルード
#include "MySQLManager.h"コンストラクタ
今回標準コンストラクタには何も処理を加えません。
代わりに、先ほどのコンストラクタを定義。
/**
* @fn
* @brief コンストラクタ
* @param なし
* @return なし
*/
CLogMain::CLogMain(CString strProductID, CString strCategory, CString strTime, CWnd* pParent)
: CDialogEx(IDD_DIALOG_LOG_MAIN, pParent)
{
m_strProductID = strProductID; // 商品ID
m_strCateory = strCategory; // カテゴリー
m_strTime = strTime; // 時間
}DoDataExchange
リストコントロールのIDと変数を紐づけ。
void CLogMain::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
DDX_Control(pDX, IDC_LIST_LOGMAIN, m_ctrlListCtrlLog);
}OnInitDialog
ひとまずここは、リストの列だけ作っておきます。
BOOL CLogMain::OnInitDialog()
{
CDialogEx::OnInitDialog();
SetWindowText(_T("履歴画面"));
// TODO: ここに初期化を追加してください
m_ctrlListCtrlLog.InsertColumn(0, _T("ログ番号"), LVCFMT_LEFT, 80);
m_ctrlListCtrlLog.InsertColumn(1, _T("操作内容"), LVCFMT_LEFT, 100);
m_ctrlListCtrlLog.InsertColumn(2, _T("商品ID"), LVCFMT_LEFT, 80);
m_ctrlListCtrlLog.InsertColumn(3, _T("カテゴリー"), LVCFMT_LEFT, 100);
m_ctrlListCtrlLog.InsertColumn(4, _T("在庫増減"), LVCFMT_LEFT, 100);
m_ctrlListCtrlLog.InsertColumn(5, _T("操作後在庫"), LVCFMT_LEFT, 100);
m_ctrlListCtrlLog.InsertColumn(6, _T("操作日時"), LVCFMT_LEFT, 150);
return TRUE; // return TRUE unless you set the focus to a control
// 例外 : OCX プロパティ ページは必ず FALSE を返します。
}大きさはお好みで。
ダイアログ呼び出し
履歴検索条件画面でOKボタン(検索ボタン)を押すと呼び出します。
まず、LogCondition.cppにて、ヘッダーをインクルード。
#include "LogMain.h" // 履歴画面続いて、OKボタンが押されると呼ばれる関数へ。
先ほど、入力チェック関数を置いたところです。
/**
* @fn
* @brief OKボタンが押された
* @param なし
* @return なし
*/
void CLogCondition::OnBnClickedOk()
{
// TODO: ここにコントロール通知ハンドラー コードを追加します。
//CDialogEx::OnOK();
if (bInputCheck() == TRUE)
{
// ここに、履歴ダイアログの処理を書く
// 商品ID
CString strProductID = m_strProductID;
// カテゴリー
CString strCategory = _T("");
if (m_ctrlCheckCategory.GetCheck() == BST_CHECKED)
{
// 条件あり
GetDlgItemText(IDC_COMBO_LOGCONDITION_CATEGORY, strCategory);
}
// 操作年月日
CString strTime = _T("");
if (m_ctrlCheckTime.GetCheck() == BST_CHECKED)
{
// 条件アリ
//年は4桁に
while (m_strTimeYear.GetLength() < 4)
{
m_strTimeYear = _T("0") + m_strTimeYear;
}
//月は2桁に
while (m_strTimeMonth.GetLength() < 2)
{
m_strTimeMonth = _T("0") + m_strTimeMonth;
}
//日は2桁に
while (m_strTimeDay.GetLength() < 2)
{
m_strTimeDay = _T("0") + m_strTimeDay;
}
strTime = m_strTimeYear + _T("-") + m_strTimeMonth + _T("-") + m_strTimeDay;
}
CLogMain dlgLogMain(strProductID, strCategory, strTime);
if (dlgLogMain.DoModal() == IDOK)
{
// OKボタン=条件画面に戻るが押された
// ⇒何もしない
}
else
{
// キャンセルボタン=メイン画面に戻るが押された
// ⇒このダイアログも閉じる
CDialogEx::OnCancel();
}
}
}長く見えるだけですが、
コンストラクタの引数を頑張って作ってるだけです。
いったんビルド。
動作確認
今回は処理の確認ではなく、
ダイアログがちゃんと出現するか、です。
履歴検索条件画面から検索を押すと。

特に問題なく、表示できました。
戻るボタンも、ちゃんと機能しています。
終わりに
今回は、
履歴検索条件画面と履歴画面を作りました。
おそらく次回で完結見込みです。
このリストコントロールに、logsテーブルの内容を出力します。
今回はここまで。

コメント