私はMVVMアプリケーションを構築するためにリアクティブプログラミングを使用しています。私のビューモデルがどのように質問を出し、ユーザーに回答を促すダイアログが出るのを待っていますか?IObservableを使用したダイアログのやりとり要求
たとえば、ユーザーが名前の変更ボタンをクリックすると、ユーザーがテキストを変更できるダイアログが表示されます。私のアプローチは、ビューモデルがIObservable<string>
プロパティを公開することです。ビュー内のコードビハインドは放出された値をリッスンし、UWP ContentDialogを表示することがあります。ユーザーがテキストを変更してOKをクリックすると、そのダイアログのコードはビューモデルでReportResult(string newText)
を呼び出します。どのように動作するかを示すために、いくつかのコードがあります。 2つの質問:
これはユーザーから情報を収集するための妥当なアプローチですか?
また、私はこれを構築するための2つの微妙に異なるアプローチがあり、どれが良いか分かりません。
interface IServiceRequest<TSource, TResult> : ISubject<TResult, TSource> { }
// Requests for information are just 'passed through' to listeners, if any.
class ServiceRequestA<TSource, TResult> : IServiceRequest<TSource, TResult>
{
IObservable<TSource> _requests;
Subject<TResult> _results = new Subject<TResult>();
public ServiceRequestA(IObservable<TSource> requests)
{
_requests = requests;
}
public IObservable<TResult> Results => _results;
public void OnCompleted() => _results.OnCompleted();
public void OnError(Exception error) => _results.OnError(error);
public void OnNext(TResult value) => _results.OnNext(value);
public IDisposable Subscribe(IObserver<TSource> observer) => _requests.Subscribe(observer);
}
// Requests for information are 'parked' inside the class even if there are no listeners
// This happens when InitiateRequest is called. Alternately, this class could implement
// IObserver<TSource>.
class ServiceRequestB<TSource, TResult> : IServiceRequest<TSource, TResult>
{
Subject<TSource> _requests = new Subject<TSource>();
Subject<TResult> _results = new Subject<TResult>();
public void InitiateRequest(TSource request) => _requests.OnNext(request);
public IObservable<TResult> Results => _results;
public void OnCompleted() => _results.OnCompleted();
public void OnError(Exception error) => _results.OnError(error);
public void OnNext(TResult value) => _results.OnNext(value);
public IDisposable Subscribe(IObserver<TSource> observer) => _requests.Subscribe(observer);
}
class MyViewModel
{
ServiceRequestA<string, int> _serviceA;
ServiceRequestB<string, int> _serviceB;
public MyViewModel()
{
IObservable<string> _words = new string[] { "apple", "banana" }.ToObservable();
_serviceA = new ServiceRequestA<string, int>(_words);
_serviceA
.Results
.Subscribe(i => Console.WriteLine($"The word is {i} characters long."));
WordSizeServiceRequest = _serviceA;
// Alternate approach using the other service implementation
_serviceB = new ServiceRequestB<string, int>();
IDisposable sub = _words.Subscribe(i => _serviceB.InitiateRequest(i)); // should dispose later
_serviceB
.Results
.Subscribe(i => Console.WriteLine($"The word is {i} characters long."));
WordSizeServiceRequest = _serviceB;
}
public IServiceRequest<string, int> WordSizeServiceRequest { get; set; }
// Code outside the view model, probably in the View code-behind, would do this:
// WordSizeServiceRequest.Select(w => w.Length).Subscribe(WordSizeServiceRequest);
}
リーキャンベルのコメントに基づき、ここでは異なるアプローチがあります。たぶん彼はそれを好きになるだろう?実際にIRenameDialogを構築する方法がわかりません。これは、ビューのコードビハインドのほんの一部にすぎませんでした。
public interface IRenameDialog
{
void StartRenameProcess(string original);
IObservable<string> CommitResult { get; }
}
public class SomeViewModel
{
ObservableCommand _rename = new ObservableCommand();
BehaviorSubject<string> _name = new BehaviorSubject<string>("");
public SomeViewModel(IRenameDialog renameDialog,string originalName)
{
_name.OnNext(originalName);
_rename = new ObservableCommand();
var whenClickRenameDisplayDialog =
_rename
.WithLatestFrom(_name, (_, n) => n)
.Subscribe(n => renameDialog.StartRenameProcess(n));
var whenRenameCompletesPrintIt =
renameDialog
.CommitResult
.Subscribe(n =>
{
_name.OnNext(n);
Console.WriteLine($"The new name is {n}");
};
var behaviors = new CompositeDisposable(whenClickRenameDisplayDialog, whenRenameCompletesPrintIt);
}
public ICommand RenameCommand => _rename;
}
このアプローチは、私がプリズムがしたと考えたものに基づいています。 [link](https://msdn.microsoft.com/en-us/library/gg431432(v = pandp.50).aspx)と[link](https://msdn.microsoft。)の「InteractionRequest」を参照してください。 com/ja-us/library/gg405494(v = pandp.40).aspx)。ビューに何かをさせるためにイベントを使用するのではなく、Observable を使用しています。私は、通知オブジェクトがその中にコールバックメカニズムを持っていた場合に起こる可能性がありますが、Viewが何らかの応答を返すためのメカニズムを持つことが有用であると考えました。 –
JustinM
名前変更の場合、私のViewModelは 'IObservable <(string originalText、Action onCommit)>'を公開する可能性があります。ビュー内のコードはかなり小さいです。サブスクライブし、値を受け取るとContentDialogを作成し、DataContextをサブスクリプションで受け取った値に関連付けるように設定し、OKボタンを押してonCommitデリゲートを呼び出し、ViewModelが結果を取得するようにします。 –
JustinM
コマンドはどこにありますか?ユーザーが(「名前を変更する」などのボタンをクリックするなど)通知すると、UIはコマンドを呼び出す必要があります。あなたの下にある層に反応したいのであなたについて知りませんので、コールバックを登録することができます(IObservableに登録する) –