locked
IValueConverter.ConvertBackでの変換失敗時の対処方法 RRS feed

  • 質問

  • 変換前に検証を行いたいのですが、うまくできずに困っています。

     

    変換(型変換)は、Binding.ConverterプロパティにIValueConverterを実装したクラスを設定することで行おうとしています。

    検証は、Binding.ValidatesOnExceptionsプロパティにTrueを設定し、プロパティのsetterに検証属性を付与し、例外を発生することで行おうとしています。

     

    どうやら、これらの方法をとる場合に、検証より前に変換がかかってしまうようです(任意のValidationRulesが追加できれば良いのですが……)。

    ConvertBackメソッドに変換できない引数が渡された場合(形式エラー)に、任意の検証エラーを通知することができません。

     

    DependencyProperty.UnsetValueを返すとプロパティのsetterが呼ばれませんし、そのままの値(異なる型)を返すと「入力の形式が正しくありません。」が表示されます。

     

    以下の内容について、教えていただければ幸いです。

    1. 変換(IValueConverter.ConvertBack)の前に検証する方法

    2. 変換失敗時の対処方法

     

     

    2010年6月6日 2:01

回答

  • Target ⇔ (Converter) ⇔ Source ←このSetter中に検証

    という図式なので変換が検証の前にかかるのは仕様なのだと思います(個人的見解ですが)。

     

    変換失敗時は検証でエラーがでるような値を返してやるといいんじゃないでしょうか?

    • 回答としてマーク bouzuya 2010年6月8日 14:46
    2010年6月7日 0:17
  • ようするに「UI からの入力値を、エラーがあろうとなかろうとそのまま受け取る。」か否かの選択の問題だと思います。
    IDataErrorInfo インタフェース を考えてみたらどうでしょうか?

    いい記事があります。
    とあるコンサルタントのつぶやき Part 2. スマートクライアントにおける単体入力データ検証
    http://blogs.msdn.com/b/nakama/archive/2009/02/26/part-2.aspx


    えムナウ@わんくま同盟 Microsoft MVP Visual Studio C# Since 2005/01-2010/12
    • 回答としてマーク bouzuya 2010年6月8日 14:46
    2010年6月8日 6:35
  • ありがとうございます。ためしに作成してみます。これを機会にSilverlightの理解が深まりそうです。

    私の中での結論は以下のようになりました。

    1. 変換前に検証する良い方法はありません。通知についても同様です。
    2. バインドするプロパティの型は、型変換が起こらないようにし、変換エラーを回避します(string)。
    3. 本来必要な型はバッキングフィールドや別クラスのプロパティなどの形で保持します。
    4. プロパティのsetterにて必要なら検証→変換を行い、3.に格納します。
    5. (UIからの不正な入力値をそのまま保持するのも、UIと同期が取れるなどの点で良い手です。この場合は4.の変換は別のタイミングで走らせることになります。IDataErrorInfoやINotifyDataErrorInfoの導入も検討した方が良いかもしれません。)
    • 回答としてマーク bouzuya 2010年6月8日 14:47
    2010年6月8日 14:46

すべての返信

  • Target ⇔ (Converter) ⇔ Source ←このSetter中に検証

    という図式なので変換が検証の前にかかるのは仕様なのだと思います(個人的見解ですが)。

     

    変換失敗時は検証でエラーがでるような値を返してやるといいんじゃないでしょうか?

    • 回答としてマーク bouzuya 2010年6月8日 14:46
    2010年6月7日 0:17
  • 返信ありがとうございます。そうですか、仕様ですか。

    他の方がどうやって回避しているか気になったのですが……。

     

    何もせずにそのままvalueを返せば、バインディングエンジンによる型変換に失敗することによる

    エラーメッセージ「入力の形式が正しくありません。」が表示されます。これで妥協するのも手ですね。

     

    意図的にエラーが出る値を返すのは、意外と難しいです。

    たとえば、変換不可な場合にnullを返してしまうと、

    RequiredAttribute(必須入力のエラー)など意図しないエラーになります。

    そもそも、パースに失敗しているが型変換(オブジェクトが生成)できる時点で不自然ですし、

    なおかつ、そのオブジェクトをsetterの検証にかけると形式エラーが返るというのは、

    かなり難しいです。

    (ものによっては範囲外エラーなどを発生させることはできそうですが、それは意図しないエラーだと思います)。

     

    いっそ、

    Source(プロパティの型)を、stringやobjectなど型変換に失敗しない型に、

    バッキングフィールド(フィールドの型)を、変換したい型にして、

    setterでの検証→変換がいいのかもしれません。

    (これだとIValueConverterの存在価値が分かりませんね……)

     

     

    private MyData myData; // 本来、保持したい型
    
    // 検証属性(略)
    public string MyData // 変換に失敗しない型
    {
     get { this.myData.ToString(); }
     set
     {
      Validate(value, "MyData");
      this.myData = Convert(value);
      OnPropertyChanged("MyData");
     }
    }
    

     

     

    他にも何か案があるかもしれませんので、もうしばらく回答済みには致しません。

    2010年6月7日 11:17
  • ようするに「UI からの入力値を、エラーがあろうとなかろうとそのまま受け取る。」か否かの選択の問題だと思います。
    IDataErrorInfo インタフェース を考えてみたらどうでしょうか?

    いい記事があります。
    とあるコンサルタントのつぶやき Part 2. スマートクライアントにおける単体入力データ検証
    http://blogs.msdn.com/b/nakama/archive/2009/02/26/part-2.aspx


    えムナウ@わんくま同盟 Microsoft MVP Visual Studio C# Since 2005/01-2010/12
    • 回答としてマーク bouzuya 2010年6月8日 14:46
    2010年6月8日 6:35
  • 返信ありがとうございます。(本件とは関係ありませんが、ブログ拝見しております。Twitterでもよろしくお願いします。)

     

    良い記事ですね。この記事でいうところの、以下の部分の結論と同じ形に私も近づいているようです。

    「突き詰めて考えていって、要するに、DTOとUIバインドオブジェクトは本質的に違うものだ、という結論に自分は達しました。」

     

    変換前に入力値を検証できない時点で、本来保持しておきたいデータとバインディングするプロパティとで、

    型を同一にする方法が私には思い浮かびません。少なくとも、バインディングするプロパティの型を変換エラーの発生しない

    (エラーがあろうとなかろうと受け取れる)型にしなければならないわけです。

    そして、これは結果(クラス分割)として、先に引用した記事の結論と同じ形をとることになります。

     

    UIとバインドオブジェクトとの間で発生する非同期(エラーである値をそのまま保持するか)については、

    関連する問題ではありますが、本件とは異なるように思うので、あまり言及しないでおきます。

    私としては、現状、非同期(例外を投げ、保持しない)で良いのではないかと考えています。

    (おそらく別スレッドを立てると思います。その際はまたよろしくお願いします。)

     

    個人的には、環境がSilverlight4ならINotifyDataErrorInfo(またはIDataErrorInfo)にしたいと考えているのですが、

    (記載しておらず大変申し訳ないのですが)私の対象としている環境であるSilverlight3だとBinding.ValidatesOnDataErrorsが存在しません。

     

    もし、私の知識不足でSilverlight 3でもIDataErrorInfoが十分に使えるのであれば、教えていただけると幸いです。

    • 回答としてマーク bouzuya 2010年6月8日 14:48
    • 回答としてマークされていない bouzuya 2010年6月8日 14:48
    2010年6月8日 11:08
  • >もし、私の知識不足でSilverlight 3でもIDataErrorInfoが十分に使えるのであれば、教えていただけると幸いです。

    Interface なんて、なきゃ作ればいいんです。
    かずきさんの Blog (4連載) が参考になります。
    http://blogs.wankuma.com/kazuki/archive/2009/03/21/170005.aspx


    えムナウ@わんくま同盟 Microsoft MVP Visual Studio C# Since 2005/01-2010/12
    2010年6月8日 11:30
  • ありがとうございます。ためしに作成してみます。これを機会にSilverlightの理解が深まりそうです。

    私の中での結論は以下のようになりました。

    1. 変換前に検証する良い方法はありません。通知についても同様です。
    2. バインドするプロパティの型は、型変換が起こらないようにし、変換エラーを回避します(string)。
    3. 本来必要な型はバッキングフィールドや別クラスのプロパティなどの形で保持します。
    4. プロパティのsetterにて必要なら検証→変換を行い、3.に格納します。
    5. (UIからの不正な入力値をそのまま保持するのも、UIと同期が取れるなどの点で良い手です。この場合は4.の変換は別のタイミングで走らせることになります。IDataErrorInfoやINotifyDataErrorInfoの導入も検討した方が良いかもしれません。)
    • 回答としてマーク bouzuya 2010年6月8日 14:47
    2010年6月8日 14:46