15

いくつかのReactiveUIサンプルを見回しましたが、ユーザーにメッセージを表示する必要がある例外を処理する方法の簡単な例がわかりません。(良い例があれば、誰かが私にそれを指摘できますか?)

私の最初の質問は、ReactiveCommandとToPropertyで例外を処理する方法です。たとえば、次のコードがあります。

public class MainWindowViewModel : ReactiveObject
{
    public ReactiveCommand CalculateTheAnswer { get; set; }

    public MainWindowViewModel()
    {
        CalculateTheAnswer = new ReactiveCommand();

        CalculateTheAnswer
            .SelectMany(_ => AnswerCalculator())
            .ToProperty(this, x => x.TheAnswer);

        CalculateTheAnswer.ThrownExceptions
            .Select(exception => MessageBox.Show(exception.Message));
    }

    private readonly ObservableAsPropertyHelper<int> _theAnswer;
    public int TheAnswer
    {
        get { return _theAnswer.Value; }
    }

    private static IObservable<int> AnswerCalculator()
    {
        var task = Task.Factory.StartNew(() =>
        {
            throw new ApplicationException("Unable to calculate answer, because I don't know what the question is");
            return 42;
        });

        return task.ToObservable();
    }
}

上記のコードを実行すると、このオブザーバブルはアイテムを受け取らないため、ThrownExceptionsを誤解しているに違いないと思います。私は何が間違っているのですか?

2番目の質問は、MVVMに適した方法でこれをどのように行うかです。このブログエントリでは、ユーザーエラー機能について言及していますが、その使用方法に関するドキュメントが見つかりません。上記の例にどのように実装しますか?

編集:以下のPaulの回答に基づいて、githubでソリューションの例を公開しました。

4

1 に答える 1

31

あなたは理解していますThrownExceptionsが、それは間違った人にあり_theAnswer.ThrownExceptions、例外を受け取ります。しかし、トリッキーな部分は、ボタンが機能しなくなったことです。ObservableがOnErrorを終了すると、それは永久に完了します。

ここでは、次のようなバックフリップをいくつか実行する必要があります。

static IObservable<int?> AnswerCalculator()

CalculateTheAnswer
    .SelectMany(_ => AnswerCalculator())
    .Catch(Observable.Return(null))
    .Where(x => x != null)
    .Select(x => x.Value)
    .ToProperty(this, x => x.TheAnswer);

この場合、呼び出しごとReactiveAsyncCommandに新しいものが作成されるため、はるかに簡単IObservableです。次のようにします。

// ReactiveAsyncCommand handles exceptions thrown for you
CalculateTheAnswer.RegisterAsyncTask(_ => AnswerCalculator())
    .ToProperty(this, x => x.TheAnswer);

CalculateTheAnswer.ThrownExceptions.Subscribe(ex => MessageBox.Show("Aieeeee"));

UserErrorの使用方法

つまり、UserErrorユーザーにスローされることを目的とした例外のようなものです(つまり、プログラマーのテキストではなく、わかりやすいテキストが含まれています)

を使用するUserErrorには、2つのことを行う必要があります。最初に、ThrownExceptionsを変更します。

CalculateTheAnswer.ThrownExceptions
    .SelectMany(ex => UserError.Throw("Something bad happened", ex))
    .Subscribe(result => /* Decide what to do here, either nothing or retry */);

そして、ビューコードビハインドで「RegisterHandler」を呼び出します。

UserError.RegisterHandler(err => {
    MessageBox.Show(err.ErrorMessage);

    // This is what the ViewModel should do in response to the user's decision
    return Observable.Return(RecoveryOptionResult.CancelOperation);
});

クールな部分は、これによりエラーダイアログがテスト可能になることです-単体テストで:

var fixture = new MainWindowViewModel();
bool errorCalled;

using (UserError.OverrideHandlersForTesting(_ => { errorCalled = true; return RecoveryOptionResult.CancelOperation })) { 
    CalculateTheAnswer.Execute(null);
}

Assert.True(errorCalled);
于 2012-12-20T06:52:42.733 に答える