7

以前の質問で、F# アプリケーションのオブザーバー パターンを慣用的に実装する方法を尋ねました。私のアプリケーションは推奨されるようにMailboxProcessorを使用するようになり、サブ MailboxProcessor などを作成するヘルパー関数をいくつか作成しました。しかし、GUI バインディングに関する特定のケース シナリオになると、私は精神的ブロックに陥っています。

私はそのようなモデルを持っているとしましょう:

type Document = {
    Contents : seq<DocumentObject>
}

また、GUI (WPF、XAML) には次のようなバインドが必要です。

interface IMainWindowViewModel
{
    IEnumerable<Control> ContentViews { get; }
}

each ViewModelfor eachには、 (その基礎となるモデル)と、それが変更されたかどうかを知る方法Controlが必要です。変更が正しく反映されるようにDocumentObject、これをサブとして提供します。このパターンが機能することはある程度確信しています。MailboxProcessor<DocumentObject>基本的に、サービス出力をマッピングし、変更要求をラップします (以下の外部インターフェイスの例)。

let subSvc = generateSubSvc svc (fun doc -> doc.Contents[0]) (fun f -> fun oldDoc -> { oldDoc with Contents[0] = f Contents[0] })
let viewModel = new SomeDocObjViewModel(docObjSvc)
new DocObjView(viewModel)

DocumentObjectここで、変更コマンドがfrom を削除すると想像してくださいMyDocument。トップレベルは、それを使用するようMailboxProcessorに変更をエコーし​​ます。そして、ここから私の問題が始まります。IMainWindowViewModelIEvent<MyDocument>

どれが削除されたのか、私にはよくわかりIMainWindowViewModelません。新しいものがあり、それに対処しなければならないということだけです。理解する方法はあるかもしれませんが、実際に直接知ることは決してありません。これにより、すべての を安全 (非効率) にするために、すべての を再作成しなければならないという道を余儀なくされる可能性があります。簡潔にするためにここでは触れていない追加の問題 ( dangling など) もあります。DocumentObjectDocumentControlDocumentObjectsubSvc

通常、この種の動的な変更ObservableCollection<DocumentObject>は、 にマップされる のようなもので処理されますObservableCollection<Control>。これには、変更可能な共有状態のすべての警告が伴い、少し「ハック」です。ただし、それは仕事をします。

PropertyChanged理想的には、との罠から解放された「純粋な」モデルObservableCollectionsが欲しいのですが、F# のどのようなパターンがこのニーズを満たすでしょうか? 慣用的なものと現実的なもののどこに線を引くのが適切ですか?

4

1 に答える 1

4

機能的な方法で変更可能な状態 (時間の経過に伴うモデル プロパティ) をモデル化する目的で、 Reactive Extensions (およびさらに先のReactive UI )を使用することを検討しましたか?

ObservableCollectionモデルでを使用するのに技術的に問題があるとは思いません。結局のところ、コレクションの変更を追跡する必要があります。自分でそれを行うこともできますが、ObservableCollectionクラスを回避する非常に具体的な理由がない限り、監視可能なコレクションを再発明する多くの手間を省くことができるようです。

また、 (Rx から) a を使用してパブリッシュし、 「メッセージ」をサブスクライブするために an として公開MailboxProcessorできるため、使用は少し過剰に思えます。SubjectIObservable

type TheModel() =
    let charactersCountSubject = new Subject()
    let downloadDocument (* ... *) = async {
        let! text = // ...
        charactersCountSubject.OnNext(text.Length)
    }        

    member val CharactersCount = charactersCountSubject.AsObservable() with get

type TheViewModel(model : TheModel) =
    // ...
    member val IsTooManyCharacters = model.CharactersCount.Select((>) 42)

もちろん、WPF について話しているので、view-model は を実装する必要がありますINPC。さまざまなアプローチがありますが、どれをとってもReactiveUI便利なツールがたくさんあります。

たとえば、CreateDerivedCollectionあなたが言及した問題の1つを解決する拡張メソッド:

documents.CreateDerivedCollection(fun x -> (* ... map Document to Control ... *))

これにより、documents監視可能なコレクションが取得され、そこから別の監視可能なコレクション (実際にはReactiveCollection) が作成され、ドキュメントがコントロールにマップされます。

于 2013-11-11T21:17:20.287 に答える