在之前的question中,我问过如何以惯用方式为F#应用程序实现观察者模式。我的应用程序现在使用MailboxProcessor作为推荐,我已经创建了一些辅助函数来创建子邮箱处理器等。但是,当涉及特定案例场景w.r.t时,我处于精神障碍。 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)
现在,想象一下修改命令现在从DocumentObject
中删除MyDocument
。顶级MailboxProcessor
现在使用IMainWindowViewModel
回复IEvent<MyDocument>
的更改。这就是我的问题开始的地方。
我的IMainWindowViewModel
并非知道哪个DocumentObject
已被删除。只有那里有一个新的Document
,它必须处理它。可能有一些方法可以搞清楚,但它从来没有直接知道。这可能会迫使我不得不为所有Control
重新创建所有DocumentObject
的安全(效率低下)。还有其他问题(例如悬空subSvc
),为简洁起见,我在这里也没有提及。
通常情况下,这些动态变化会被处理为ObservableCollection<DocumentObject>
,然后映射到ObservableCollection<Control>
。这伴随着共享可变状态的所有警告,并且有点'hackish';但它确实可以胜任。
理想情况下,我想要一个'纯'模型,不受PropertyChanged
和ObservableCollections
的限制,F#中的哪种模式可以满足这种需求?在惯用和现实之间划清界线是恰当的吗?
答案 0 :(得分:3)
您是否考虑过使用Reactive Extensions(以及Reactive UI进一步推进)以便以功能方式对可变状态(读取:您的模型属性随时间变化)进行建模?
在技术上我没有看到在模型中使用ObservableCollection
的任何错误。毕竟,您需要来跟踪收藏更改。你可以自己做,但看起来你可以省去重新发明可观察集合的麻烦,除非你有一个非常具体的理由要避免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 ... *))
这将获取您的documents
可观察集合,并从中创建另一个可观察集合(实际上是ReactiveCollection
),将文档映射到控件。