質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
MVVM

MVVM(Model View ViewModel)は構築上のデザインパターンで、表現ロジック(ViewModel)によってデータ(Model)からページ(View)を分離させます。

WPF

Windows Presentation Foundation (WPF) は、魅力的な外観のユーザー エクスペリエンスを持つ Windows クライアント アプリケーションを作成するための次世代プレゼンテーション システムです

Q&A

解決済

2回答

1990閲覧

MVVMにおけるアカウント登録時に、既存アカウント重複時のエラーを通知する方法は

退会済みユーザー

退会済みユーザー

総合スコア0

MVVM

MVVM(Model View ViewModel)は構築上のデザインパターンで、表現ロジック(ViewModel)によってデータ(Model)からページ(View)を分離させます。

WPF

Windows Presentation Foundation (WPF) は、魅力的な外観のユーザー エクスペリエンスを持つ Windows クライアント アプリケーションを作成するための次世代プレゼンテーション システムです

0グッド

0クリップ

投稿2021/06/07 14:11

編集2021/06/07 14:18

環境

.NET 5
WPF
Prism 8.0.0

前提・実現したいこと

MVVMの設計に従ってアカウント登録の実装をしているのですが、
アカウント登録時のValidation処理をどう行うか困っています…

ModelでValidationを実行する場合には、
どうエラーメッセージを通知するのがよいのでしょうか。

シナリオ

ユーザは、画面上にある

  • ユーザ名
  • パスワード
  • パスワードの入力確認

上記三つの項目を入力し、「登録」ボタンをクリックします。

その際、下記条件に当てはまらない場合に、PrismのErrorContainerを介して、画面上にエラーメッセージが表示されます。

  • ユーザ名

1文字以上入力されている
登録されていないアカウント名である

  • パスワードの入力確認

パスワードと一致する

発生している問題・エラーメッセージ

ユーザ名が1文字以上であるかはRequiredAttribute、
パスワードの入力確認はCompareAttributeで判定できますが、
既存ユーザアカウント名との重複判定とエラー通知をModelで行う場合は、
Actionやdelegateを使用するものなのでしょうか。

例えば、ViewModelのコンストラクタ内で、エラー時に呼び出されるModelの関数にErrorContainerへの処理を追加し、
エラー判定時に下記処理をModel内で呼び出すようなイメージです。

C#

1delegate void OnValidationError(string errorMessage)

新たに、特定のユーザ名を使用できないようにし、エラーメッセージ「使用できないユーザ名です」と表示させたり、
エラーの内容によって、通知先のプロパティが異なる場合(今回でいうと、Passwordにmanager側で判定するような処理が追加された場合)は、
別のdelegate(もしくはActionなど)を定義するのでしょうか。

コード

C#

1 2public class ValidatableBase : Prism.Mvvm.BindableBase, INotifyDataErrorInfo 3{/* 処理は省略、ErrorContainerはerrorsとしてprivate定義 */} 4 5public class VM : ValidatableBase 6{ 7 [Required] 8 public string UserName{ get => _account.UserName; set => _account.UserName = value; } 9 10 public string Password{ get => _account.Password; set => _account.Password = value; } 11 12 [Compare(nameof(Password))] 13 public string Confirm{ get; set; } 14 15 // Accountは 16 public ICommand RegisterCommand { get; } 17 18 public VM(AccountManager manager) 19 { 20 RegisterCommand = new DelegateCommand(()=> manager.Register(_account)); 21 manager.OnValidationError += message => errors.SetErrors(nameof(UserName), new[]{ message }); 22 } 23 24 private Account _account; 25} 26 27public class AccountManager 28{ 29 // このようなdelegateが、ケース毎に用意される…? 30 public delegate void OnValidationError(string errorMessage); 31 32 public List<Account> Accounts{ get; private set; } 33 34 public void Register(Account account) 35 { 36 if (Accounts.Contain(account)) 37 { 38 OnValidationError("既に存在しているユーザ名です"); 39 return; 40 } 41 if(/* 例えば、既存パスワードとの重複を禁止する場合 */) 42 { 43 /* 新しいdelegateを呼び出す? */ 44 return; 45 } 46 } 47} 48 49public Account 50{ 51 public string UserName{ get => _userName; set => SetProperty(ref _userName, value); } 52 public string Password{ get => _password; set => SetProperty(ref _password, value); } 53 54 /* 比較時にUserNmaeで確認する処理 */ 55 56 private string _userName; 57 private string _password; 58} 59

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

TN8001

2021/06/13 14:48

> 別のdelegate(もしくはActionなど)を定義するのでしょうか。 私はアマチュアなのでこういった「業務でー」とか「一般的にー」みたいのはスルーしているのですが、何に困っているのかが見えてきません。 例えば string errorMessage は別にstringじゃなくてもいいわけですし、 雑に例外を投げる仕様もあってもいいでしょうし、 カスタムValidationRule・ValidationAttributeでもかなりのことができるはずです。 つまり「R0KUさんの好きな方法でやったらどうですか」となってしまいそうです。 > 私の知らない素敵な実装方法 をご所望なのは伝わっていますが、なにを「素敵」というかは主観ですので^^;
退会済みユーザー

退会済みユーザー

2021/06/13 16:30

ご質問いただきありがとうございます。 知りたい内容は、 「Modelにエラー通知イベントを実装する場合、エラー内容に応じてイベントは分けるべきかどうか。 そのうえで、後述の問題を解決する手立ては何か。」 です。 分ける場合は、ViewModel上に各イベントごとの処理追加に関する記述が増えるので面倒ですし、 分けない場合(TN8001さんの1番目の提案)は、ViewModel上のプロパティ名とエラーメッセージを紐づける必要がありますが、VM上のプロパティ名をリテラルでない状態で渡すためにnameofを使うと、引数がどんどん増えていきますし、 タプルや配列で渡すとしても、順番が処理に影響するので、これも微妙です。 個人的には、分ける場合での実装を取り、デメリットは仕方ないものとするしかないか、とも思いますが、 今回のようなアカウント登録に関する処理は、必ず私以外の方も実装しているはずです。 ある種デザインパターン、もしくは部分的な共通項があると確信しています。
guest

回答2

0

ベストアンサー

R0KUさんの所望の回答になっていないだろうという自覚はあります^^;

「それはModel内のステートの不完全な意味のないコピーでしかありません。」
(括弧内は、某MVVMに関するブログより引用。)

有用ならば別に伏せる必要もないでしょう。尾上雅則さんですよね(ここでいいのかな??
MVVMのModelにまつわる誤解 - the sea of fertility

上記引用のすぐ上ですが、

Modelのメソッド呼び出しは何をもたらすのでしょうか?。それはModel内状態の変化(あるいは外部サービス呼び出しとそれに伴うModel内状態の変化)と、なんらかのイベント発生(通信エラー発生とか)しかないのです。ViewModelがModelの影であれば当然それしかないのです。

と、質問の例ですとVMAccountがVM・Mとなって、AccountManagerは(外部サービス呼び出しのための)サービス層としてAccountがアクセスするように読めました(そうでもないです?あまり自信はありません^^;

サービスは戻り値があってもいいですし、例外でもいいわけです(いいんですよね?^^; AccountManager#Registerの話)
エラーをUserNamePasswordに紐づけられればそうしますし、それ以外のエラーはログイン状態のようなプロパティに充ててもいいでしょう(あるいはMessenger的なもの?)

つまりActiondelegateは使いません。INotifyDataErrorInfoの範囲内で処理します。

なんにしろ丁寧なエラー通知をしようとすれば、どこかで面倒なことになるのは避けられないんじゃないでしょうか。

投稿2021/06/13 21:29

編集2023/07/27 15:19
TN8001

総合スコア9862

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

質問の回答になっているかどうか分かりませんが、自分の見解を書きますね。(外していたらすみません)

MVVMの考え方ですと、通知を表示するのはViewの役割です。
ご存じだと思いますが、ViewModel(以下VM)は、そのViewとModelを繋ぐコントローラー的なもので、Modelがロジックです。

Modelで判定を持つにしても、その戻り値をVMで判定して、別のViewでエラーメッセージを出す、、、のような形が、MVVM的な設計かなぁと考えます。その為、Delegateなどを定義するというよりは、エラー時のViewへの処理をVMに作る、、、という考えの方が、しっくりくると考えます。(その際、元のViewは操作不能にする必要があるなど、工夫をする必要はありそうですが・・)

参考になれば幸いです。

投稿2021/06/13 10:13

TEC_S

総合スコア79

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2021/06/13 14:14

ご回答いただきありがとうございます。 身近にこういった話を相談できる相手がいないので、本当にありがたいです。 エラー通知とその表示を実現する場合、Modelのイベントによって通知するか、 私の知らない素敵な実装方法によって実現するかの二つが想像されます。 Modelから呼び出される別のModelで戻り値を設定するなら良いかと思いますが、 直接ViewModelに伝えてしまうのは… ViewModelはModelのメソッドを呼び出し、 Modelはメソッド内でModelの状態を変更します。 Modelの状態の変更はObserverパターンにのっとって、ViewModel、Viewへと伝播していきます。 Modelのメソッドに戻り値を持たせた場合、 「それはModel内のステートの不完全な意味のないコピーでしかありません。」 (括弧内は、某MVVMに関するブログより引用。) 実際、ViewModelに公開される状態が複数変更される場合を考えると、 その全てを戻り値で返すというのも、なんだか保守が大変そうです。 (その状態が、いろいろなメソッドから変更されるとすると、呼び出すメソッドすべてが戻り値を持つ…?) WPFでは、エラー通知の方法はいくつかありますが、 ViewModelおよびModelが、INotifyDataErrorInfoを実装する方法があります。 Prismでは、実装された通知イベントをErrorContainerでラップして、イベント処理を楽にしています。 (ErrorContainerについては、実装から推測しているので、認識間違いがあるかもしれません。) つまるところ、 WPFにおけるModelのエラー通知は、イベントを使用するものなのではないかということです。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問