C++ Builder XE4
関連 http://qiita.com/7of9/items/ea52f618de6b465299ab
背景
- フォームに1000個のコンポーネントがある
- そのどれかが変更されたら、「初期値と異なる」とする
1000個のコンポーネントのonChangeメソッドを使うのは面倒そうだ。
方法
ある時点のスナップショットを取る。
そのスナップショットと比較することで、変更があったかどうかを知る。
code v0.1
TEdit, TRadioButton, TCheckBoxの3種類のコンポーネントに対応。
各項目の最大数は100としているが用途によって増やせばいい。
//---------------------------------------------------------------------------
# ifndef Unit1H
# define Unit1H
//---------------------------------------------------------------------------
# include <System.Classes.hpp>
# include <Vcl.Controls.hpp>
# include <Vcl.StdCtrls.hpp>
# include <Vcl.Forms.hpp>
//---------------------------------------------------------------------------
class TForm1 : public TForm
{
__published: // IDE で管理されるコンポーネント
TEdit *Edit1;
TCheckBox *CheckBox1;
TRadioButton *RadioButton1;
TButton *B_save;
TCheckBox *CheckBox2;
TEdit *Edit2;
TRadioButton *RadioButton2;
TButton *B_check;
void __fastcall B_saveClick(TObject *Sender);
void __fastcall FormShow(TObject *Sender);
void __fastcall B_checkClick(TObject *Sender);
private: // ユーザー宣言
public: // ユーザー宣言
__fastcall TForm1(TComponent* Owner);
};
//---------------------------------------------------------------------------
extern PACKAGE TForm1 *Form1;
//---------------------------------------------------------------------------
# endif
//---------------------------------------------------------------------------
# include <vcl.h>
# pragma hdrstop
# include "Unit1.h"
//---------------------------------------------------------------------------
# pragma package(smart_init)
# pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
//---------------------------------------------------------------------------
static const int kMaxnumItems = 100;
typedef struct tag_Config {
bool checkBoxChecked[kMaxnumItems];
int numCheckbox;
bool radioButtonChecked[kMaxnumItems];
int numRadioButton;
String editText[kMaxnumItems];
int numEdit;
} Config_t;
static Config_t s_config;
static initConfig(void)
{
s_config.numCheckbox = 0;
s_config.numRadioButton = 0;
s_config.numEdit = 0;
}
void __fastcall TForm1::B_saveClick(TObject *Sender)
{
initConfig();
for(int idx = 0; idx < ComponentCount; idx++) {
TComponent *ptarget = Components[idx];
if (dynamic_cast<TCheckBox *>(ptarget) != NULL) {
TCheckBox *chkPtr = (TCheckBox *)ptarget;
int itmidx = (s_config.numCheckbox)++;
s_config.checkBoxChecked[itmidx] = chkPtr->Checked;
}
if (dynamic_cast<TRadioButton *>(ptarget) != NULL) {
TRadioButton *rdbPtr = (TRadioButton *)ptarget;
int itmidx = (s_config.numRadioButton)++;
s_config.radioButtonChecked[itmidx] = rdbPtr->Checked;
}
if (dynamic_cast<TEdit *>(ptarget) != NULL) {
TEdit *edPtr = (TEdit *)ptarget;
int itmidx = (s_config.numEdit)++;
s_config.editText[itmidx] = edPtr->Text;
}
}
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormShow(TObject *Sender)
{
initConfig();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::B_checkClick(TObject *Sender)
{
int chkbxidx = 0; // index for TCheckBox
int rdbidx = 0; // index for TRadioButton
int edidx = 0; // index for TEdit
bool changed = false;
for(int idx = 0; idx < ComponentCount; idx++) {
TComponent *ptarget = Components[idx];
if (dynamic_cast<TCheckBox *>(ptarget) != NULL) {
TCheckBox *chkPtr = (TCheckBox *)ptarget;
if (s_config.checkBoxChecked[chkbxidx] != chkPtr->Checked) {
changed = true;
break;
}
chkbxidx++;
}
if (dynamic_cast<TRadioButton *>(ptarget) != NULL) {
TRadioButton *rdbPtr = (TRadioButton *)ptarget;
if (s_config.radioButtonChecked[rdbidx] != rdbPtr->Checked) {
changed = true;
break;
}
rdbidx++;
}
if (dynamic_cast<TEdit *>(ptarget) != NULL) {
TEdit *edPtr = (TEdit *)ptarget;
if (s_config.editText[edidx] != edPtr->Text) {
changed = true;
break;
}
edidx++;
}
}
if (changed) {
ShowMessage(L"Changed");
} else {
ShowMessage(L"Same as usual");
}
}
//---------------------------------------------------------------------------
UI例
B_saveを押した時にスナップショットを取り、B_check押下時に変更があったかどうかを知る。
変更があったら「Changed」が表示され、変更がなかったら「Same as usual」が表示される。
関連
dynamic_cast<>の使用はRemy Lebeauさんに教わった。
http://stackoverflow.com/questions/33060775/how-to-know-the-control-is-tlabel
@shiracamus さんに教わった書き方も拝借。
http://qiita.com/7of9/items/2e6e3c7880fafe8014d4#comment-ae913b64fdeba065f885
備考
Components[]のハッシュ値をとればもっと簡単になるかもしれない。
code v0.2
異なるフォームにて使いまわせるように外部ファイル化した。
外部ファイル
//---------------------------------------------------------------------------
# ifndef UtilSettingModifiedH
# define UtilSettingModifiedH
//---------------------------------------------------------------------------
/*
フォーム上のコンポーネントの値が変更されたかをチェックする。
*/
static const int kMaxnumItems = 300;
class CUtilSettingModified {
private:
typedef struct tag_Config {
bool checkBoxChecked[kMaxnumItems];
int numCheckbox;
bool radioButtonChecked[kMaxnumItems];
int numRadioButton;
String editText[kMaxnumItems];
int numEdit;
} Config_t;
Config_t m_config;
void __fastcall initItems(void);
public:
void __fastcall DoSave(TForm *formPtr);
bool __fastcall HasChanged(TForm *formPtr);
// コンストラクタ
__fastcall CUtilSettingModified();
};
# endif
//---------------------------------------------------------------------------
# include <vcl.h>
# pragma hdrstop
# include "UtilSettingModified.h"
//---------------------------------------------------------------------------
# pragma package(smart_init)
# define DEBUG_DO_PRINT // デバッグ出力
/*
v0.1 2016/07/28
- HasChanged()追加
- DoSave()追加
- initItems()追加
- コンストラクタ追加
- debug_outputDebugString()追加
*/
//---------------------------------------------------------------------------
static void debug_outputDebugString(String prefix, String msg)
{
// あとでまとめてデバッグ出力をコメントアウトするため、関数にしている。
# ifdef DEBUG_DO_PRINT
String work = L"UtilSettingModified >" + prefix + L": " + msg;
OutputDebugString(work.c_str()); // @ debug_outputDebugString
# endif
}
//---------------------------------------------------------------------------
__fastcall CUtilSettingModified::CUtilSettingModified()
{
initItems();
}
//---------------------------------------------------------------------------
void __fastcall CUtilSettingModified::initItems(void)
{
m_config.numCheckbox = 0;
m_config.numRadioButton = 0;
m_config.numEdit = 0;
}
//---------------------------------------------------------------------------
void __fastcall CUtilSettingModified::DoSave(TForm *formPtr)
{
initItems();
for(int idx = 0; idx < formPtr->ComponentCount; idx++) {
TComponent *ptarget = formPtr->Components[idx];
if (dynamic_cast<TCheckBox *>(ptarget) != NULL) {
TCheckBox *chkPtr = (TCheckBox *)ptarget;
int itmidx = (m_config.numCheckbox)++;
m_config.checkBoxChecked[itmidx] = chkPtr->Checked;
}
if (dynamic_cast<TRadioButton *>(ptarget) != NULL) {
TRadioButton *rdbPtr = (TRadioButton *)ptarget;
int itmidx = (m_config.numRadioButton)++;
m_config.radioButtonChecked[itmidx] = rdbPtr->Checked;
}
if (dynamic_cast<TEdit *>(ptarget) != NULL) {
TEdit *edPtr = (TEdit *)ptarget;
int itmidx = (m_config.numEdit)++;
m_config.editText[itmidx] = edPtr->Text;
}
}
}
bool __fastcall CUtilSettingModified::HasChanged(TForm *formPtr)
{
int chkbxidx = 0; // index for TCheckBox
int rdbidx = 0; // index for TRadioButton
int edidx = 0; // index for TEdit
bool changed = false;
for(int idx = 0; idx < formPtr->ComponentCount; idx++) {
TComponent *ptarget = formPtr->Components[idx];
if (dynamic_cast<TCheckBox *>(ptarget) != NULL) {
TCheckBox *chkPtr = (TCheckBox *)ptarget;
if (m_config.checkBoxChecked[chkbxidx] != chkPtr->Checked) {
changed = true;
break;
}
chkbxidx++;
}
if (dynamic_cast<TRadioButton *>(ptarget) != NULL) {
TRadioButton *rdbPtr = (TRadioButton *)ptarget;
if (m_config.radioButtonChecked[rdbidx] != rdbPtr->Checked) {
changed = true;
break;
}
rdbidx++;
}
if (dynamic_cast<TEdit *>(ptarget) != NULL) {
TEdit *edPtr = (TEdit *)ptarget;
if (m_config.editText[edidx] != edPtr->Text) {
changed = true;
break;
}
edidx++;
}
}
return changed;
}
使用コード
//---------------------------------------------------------------------------
# ifndef Unit1H
# define Unit1H
//---------------------------------------------------------------------------
# include <System.Classes.hpp>
# include <Vcl.Controls.hpp>
# include <Vcl.StdCtrls.hpp>
# include <Vcl.Forms.hpp>
# include "UtilSettingModified.h"
//---------------------------------------------------------------------------
class TForm1 : public TForm
{
__published: // IDE で管理されるコンポーネント
TCheckBox *CheckBox1;
TCheckBox *CheckBox2;
TCheckBox *CheckBox3;
TCheckBox *CheckBox4;
TCheckBox *CheckBox5;
TCheckBox *CheckBox6;
TButton *B_save;
TButton *B_check;
void __fastcall B_saveClick(TObject *Sender);
void __fastcall B_checkClick(TObject *Sender);
private: // ユーザー宣言
CUtilSettingModified *m_settingModified;
public: // ユーザー宣言
__fastcall TForm1(TComponent* Owner);
__fastcall ~TForm1();
};
//---------------------------------------------------------------------------
extern PACKAGE TForm1 *Form1;
//---------------------------------------------------------------------------
# endif
//---------------------------------------------------------------------------
# include <vcl.h>
# pragma hdrstop
# include "Unit1.h"
//---------------------------------------------------------------------------
# pragma package(smart_init)
# pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
m_settingModified = new CUtilSettingModified();
}
__fastcall TForm1::~TForm1()
{
if (m_settingModified != NULL) {
delete m_settingModified;
m_settingModified = NULL;
}
}
//---------------------------------------------------------------------------
void __fastcall TForm1::B_saveClick(TObject *Sender)
{
m_settingModified->DoSave(this);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::B_checkClick(TObject *Sender)
{
if (m_settingModified->HasChanged(this)) {
ShowMessage(L"Changed");
} else {
ShowMessage(L"Same as before");
}
}
//---------------------------------------------------------------------------
外部ファイル化することでUnit1自体の実装はすっきりするようになった。
ロジックをUIと分離できたかと思う。