7

在上一个问题中,我询问了如何以惯用方式为 F# 应用程序实现观察者模式。我的应用程序现在使用推荐的MailboxProcessor,并且我创建了一些辅助函数来创建子 MailboxProcessor 等。但是,当涉及到 GUI 绑定的特定案例场景时,我处于精神障碍。

可以说我有一个这样的模型:

type Document = {
    Contents : seq<DocumentObject>
}

GUI(WPF、XAML)需要像这样的绑定:

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

ViewModel一个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)

现在,想象一个修改命令现在删除了一个DocumentObjectfrom MyDocument。顶层现在呼应了使用它的MailboxProcessor更改。这就是我的问题开始的地方。IMainWindowViewModelIEvent<MyDocument>

IMainWindowViewModel真的不知道 哪个DocumentObject被删除了。只是有一个新的Document,它必须处理它。它可能有办法弄清楚,但它永远不会真正直接知道。这可能迫使我不得不重新创建所有Control' 以确保所有DocumentObject' 的安全(效率低下)。subSvc为简洁起见,我也没有在这里提到其他问题(例如悬空的)。

通常情况下,这些动态变化会被处理类似 anObservableCollection<DocumentObject>然后映射到ObservableCollection<Control>. 这带有共享可变状态的所有警告,并且有点“hackish”;但是,它确实可以完成这项工作。

理想情况下,我想要一个“纯”模型,没有 and 的陷阱,PropertyChangedF ObservableCollections# 中什么样的模式可以满足这种需求?在惯用语现实主义之间划清界限在哪里合适?

4

1 回答 1

4

您是否考虑过使用响应式扩展(以及未来的响应式 UI)以功能方式对可变状态(阅读:您的模型属性随着时间的推移)进行建模?

ObservableCollection我认为在您的模型中使用 an 在技术上没有任何问题。毕竟,您需要跟踪集合更改。你可以自己做,但看起来你可以省去很多麻烦来重新发明 observable 集合,除非你有一个非常具体的理由来避免这个ObservableCollection类。

此外,使用MailboxProcessor似乎有点矫枉过正,因为您可以只使用Subject(来自 Rx)发布并将其公开为IObservable订阅“消息”:

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,视图模型应该实现INPC. 有不同的方法,但无论您采用哪种方法,ReactiveUI都有很多方便的工具。

例如CreateDerivedCollection解决您提到的问题之一的扩展方法:

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

This will take your documents observable collection, and make another observable collection out of it (actually a ReactiveCollection) that will have documents mapped to controls.

于 2013-11-11T21:17:20.287 回答