(BC/DAC)
注意点 2. バインドオブジェクトが、必然的に不整合状態に陥っている ことがありうる
例外ベースの検証=不正なデータをバインドオブジェクトが受け付けな いようになっている、ということ
しかし、そもそも入力フォームの表示直後のバインドオブジェクトは、何も 入力されていない=オブジェクトとして正しい状態ではない
あるいは、入荷予定日と出荷予定日を入力するような場合、フィールド間 の大小比較関係は、片方ずつデータ入力されてもうまくチェックできない
結果的に、インスタンス全体チェックは、イベントハンドラでの実装が必要
objCustomer オブジェクト
初期状態では何もデータが入っていない
=オブジェクトインスタンスとして正しい 状態ではない(例:非 null フィールド に対して null が入っていたりする)
不正なデータを入れられない
ようになっているが、そもそも
バインドオブジェクトの初期値
自体が不正なデータである
p.32
3. 単体入力エラーチェックの実装パターン
-② Silverlight 3, WPF 3 の場合
つまり、ここまでの解説をまとめると、例外ベースの双方向 データバインドの動作イメージは以下の通りになる
バインドエラーがない場合に限り、 UI からの入力がすべてバインドオ ブジェクトに反映されている
このためイベントハンドラ内では、まずバインドエラーのチェックが必要
仮にバインドエラーがなかったとしても、インスタンス単位のチェックを イベントハンドラ内で行う必要がある
このように、例外ベースの双方向データバインドは、実装が すっきりしないところがある
1973/06/07 Nobuyuki
1234
コードビハインド
( *.Forms.cs など)
バインドオブジェクト
同期できて いない場合が
ある
ビジネス ロジック部
( BC/DAC )
インスタンス 単位のエラーが
あることも
① バインドエラー有無のチェック
② インスタンス単位のチェック
③ 業務処理の呼び出し
3. 単体入力エラーチェックの実装パターン
-③ Windows フォーム 2.0, WPF 3.5 の場合
こうした問題を解決するため、 Windows フォーム 2.0 や WPF 3.5 では、 IDataErrorInfo 入力検証がサポートされた
IDataErrorInfo インタフェースは、オブジェクトインスタンス内部にエ ラーが含まれていることを、文字列情報として返すためのもの
これを使うことにより、前述の問題をきれいに解決することができる
1973/55/41 Nobuyuki
12345
objCustomer オブジェクト
IDataErrorInfoインタフェース
無理矢理 反映
内部に エラー値を
含む
顧客ID は半角英数大文字
4 文字でなければなりません。 エラー情報を
文字列として 外部に提供 UI コントロールが、
バインドオブジェクトの IDataErrorInfo から エラー情報を取り出して
UI に表示する
p.34
3. 単体入力エラーチェックの実装パターン
-③ Windows フォーム 2.0, WPF 3.5 の場合
IDataErrorInfo オブジェクトを使った双方向データバインドは、
下図のように動作する
入力値が正しかろうと間違 っていようと、とにかくオブ ジェクトに反映してしまう
オブジェクトインスタンスが 不正な状態にある場合には これを IDataErrorInfo イン タフェースから公開する
これにより、常に UI とオブ ジェクト内の値とが同期さ れる
具体的な実装 → 次ページ
Customer Input オブジェクト
Customer Input オブジェクト
Customer Input オブジェクト
整合正しくない 状態 エラー情報を
返す
public class CustomerInput : IDataErrorInfo {
private Dictionary<string, string> _errors = new Dictionary<string, string>();
private string _id;
public string ID {
get { return _id; } set
{
_id = value;
if (_id == null) {
_errors["ID"] = "ID は必須入力項目です。";
}
else if (Regex.IsMatch(value, @"^[0-9A-Z]{4}$") == false) {
_errors["ID"] = "ID は半角英数大文字 4 文字です。";
} else {
_errors["ID"] = null;
} } }
private string _name;
public string Name {
get { return _name; } set
{
_name = value;
if (_name == null || _name == "") {
_errors["Name"] = "名前は必須入力項目です。";
}
C#
単体入力エラーがある 値であっても、とりあえ ず受け付けてデータの 同期を図る
当該入力値が不適切 な場合には、エラー情 報をため込んでおく
IDataErrorInfo ベースの双方向 バインドオブジェクト
p.36
else {
_errors["Name"] = null;
} } }
private string _email;
public string Email {
get { return _email; } set
{
_email = value;
if (value == null || Regex.IsMatch(value, @"¥w+([-+.']¥w+)*@¥w+([-.]¥w+)*¥.¥w+([-.]¥w+)*")) {
_errors["Email"] = null;
} else {
_errors["Email"] = "電子メールアドレスとして有効な値を入力してください。";
} } }
private string _phone;
public string Phone {
get { return _phone; } set
{
_phone = value;
if (value == null || Regex.IsMatch(value, @"(0¥d{1,4}-|¥(0¥d{1,4}¥) ?)?¥d{1,4}-¥d{4}")) {
_errors["Phone"] = null;
} else
C#
IDataErrorInfo ベースの双方向 バインドオブジェクト
{
_errors["Phone"] = "電話番号は (03)1234-5678 のように入力してください。";
} } }
public DateTime? Birthday { get; set; } // 全体整合チェック
public string Error {
get {
if (_email == null && _phone == null) {
return "電子メールアドレスか電話番号かのいずれか一方は必須入力です。";
} else {
return null;
} } }
public bool HasErrors {
get { return (_errors.Count != 0 || Error != null); } }
public string this[string columnName]
{ get {
return (_errors.ContainsKey(columnName) ? _errors[columnName] : null);
} } }
C#
特定カラム(プロパティ)にエラーがある場 合には、そのエラーの情報をメッセージで返 す
※ここから入手されるエラー情報は、
ErrorProvider とBindingSource により自 動的にアイコンで表示される
※このメソッドは必須ではないが、実装して おくとUI 実装がラクになる
オブジェクト全体に対するデータ検証内容は、こ こに記述する
(エラーがない場合にはnull を返す)
IDataErrorInfo インタフェース経由で エラー情報を返すための処理
IDataErrorInfo ベースの双方向 バインドオブジェクト
p.38
3. 単体入力エラーチェックの実装パターン
-③ Windows フォーム 2.0, WPF 3.5 の場合
具体例) Windows フォーム 2.0 の場合の IDataErrorInfo 双方向データバインドの実装方法
前述のように作成した IDataErrorInfo オブジェクトを BindingSource コントロールにより UI コントロール群と接続する
さらに画面上に ErrorProvider コントロールを貼り付けておくと、これ が自動的にエラー情報をチェックし、アイコンなどの表示をしてくれる
電子メールアドレスとして有効なアドレスを入 力してください。
3. 単体入力エラーチェックの実装パターン
-③ Windows フォーム 2.0, WPF 3.5 の場合
IDataErrorInfo ベースの双方向データバインドには、以下の ようなメリットがある
単体入力チェック処理を、バインドオブジェクトに固めることができる
このため、モジュールの役割分担が明確になる( MVC 的なモデル)
しかも、単体入力チェックロジック部分だけを重点的に単体機能テストす ることもできる
1973/55/41 Nobuyuki
12345
コードビハインド
( *.Forms.cs など)
バインドオブジェクト
ビジネス ロジック部
( BC/DAC )
IDataErrorInfo インタフェース UI 表示
エラー表示
単体入力 チェックロジック
UI / BC との
接続処理 業務処理
常に同期 エラー
表示
p.40
3. 単体入力エラーチェックの実装パターン
-③ Windows フォーム 2.0, WPF 3.5 の場合
入力仕掛り状態の維持が簡単にできる
バインドオブジェクトをそのままシリアル化して保存すれば、入力しかけ のデータをそのまま保存しておくこともできる
コードビハインドの記述が簡単になる
コードビハインドのイベントハンドラでは、バインドオブジェクトだけを操作 すればよく、 UI コントロールを触る必要がない
このため、コードビハインドのコードの見通しも非常によくなる
→ 次ページ参照
1973/55/41 Nobuyuki
12345
コードビハインド
( *.Forms.cs など)
バインドオブジェクト
ビジネス ロジック部
( BC/DAC )
IDataErrorInfo インタフェース
常に同期
C#
public partial class Form1 : Form // ※ 一部コードを省略 {
private CustomerInput ci;
private void Form1_Load(object sender, EventArgs e) {
ci = new CustomerInput();
bindingSource1.DataSource = ci;
}
private void bindingSource1_BindingComplete(object sender, BindingCompleteEventArgs e) {
lblError.Text = ci.Error;
}
private void button1_Click(object sender, EventArgs e) {
// 単体入力チェックの結果を確認 if (ci.HasErrors)
{
MessageBox.Show("入力データに誤りがあります。修正してください。");
return;
}
// 単体入力チェックが OK なら、ビジネスロジックを呼び出す
localhost.CustomerService proxy = new localhost.CustomerService();
var result = proxy.ResistCustomer(ci.ID, ci.Name, ci.Phone, ci.Email, ci.Birthday);
// 正常終了と業務エラー(突き合わせエラー)を切り分けてメッセージ表示 switch (result)
{
case localhost.RegistCustomerResult.Success:
MessageBox.Show("正しく顧客登録を行いました。");
break;
case localhost.RegistCustomerResult.DuplicateCustomerIDError:
MessageBox.Show("指定された ID はすでに利用されています。");
break;
} } }