以下のような仕様があるとします。
全・1・2・3というチェックボックスが 4 つある。
全をチェックすると、1・2・3のチェックボックスが全てチェックされる。
全のチェックを外すと、1・2・3のチェックボックスの全てのチェックが外れる。
1・2・3のチェックの状態が変更された場合、1・2・3の全てがチェックされていたら全はチェックされ、そうでなければ全のチェックは外れる。
これを何も考えずに書くと、以下のコードになります。
(ちなみに、checkBox1 が全、それ以外が1・2・3だと思って下さい)
var a = new List<CheckBox> { checkBox2, checkBox3, checkBox4 };
foreach(var x in a) {
x.CheckedChanged += (sender, e) => {
checkBox1.Checked = (a.Where(p => p.Checked).Count() == 3);
};
}
checkBox1.CheckedChanged += (sender, e) => {
a.ForEach(x => x.Checked = checkBox1.Checked);
};
しかし、これでは正常に動作しません。
何故なら、Checked の値を更新する都度、更新されたコントロールの CheckedChanged イベントが連鎖的に発生するからです。
つまりは、最初に発生したイベントによって Checked プロパティを変更した場合、そのコントロールで CheckedChanged イベントが発生しなければ良いのです。
方法は、いくつか考えられますが、この程度の規模なら僕はフラグを使ってしまうことが多いです。
var flag = true;
InitializeComponent();
var a = new List<CheckBox> { checkBox2, checkBox3, checkBox4 };
foreach(var x in a) {
x.CheckedChanged += (sender, e) => {
if (!flag) return;
flag = false;
checkBox1.Checked = (a.Where(p => p.Checked).Count() == 3);
flag = true;
};
}
checkBox1.CheckedChanged += (sender, e) => {
if (!flag) return;
flag = false;
a.ForEach(x => x.Checked = checkBox1.Checked);
flag = true;
};
フラグを多用する方法は決して推奨できるものではありません。
しかし、最初に発生したイベントによって
二次的に発生したイベントを無視する方法
としては、これ以上複雑にはならないので僕としてはギリギリセーフだと思っています。
他にも、イベントの登録と削除を動的に行う方法や、1・2・3のチェックボックスを一つのコントロールとして独立させるべくユーザーコントロールにする方法も考えられますね。
皆さんは、イベントの連鎖発生をコントロールする場合は、どのようにしているのでしょうか?