我有一个实现WPF WebBrowser控件的应用程序。它加载一个包含一些JS函数的页面,这些函数必须从我的应用程序中调用,也可能从其他线程中调用。更可取的是,我希望坚持MVVM模式,并在模型中保留用于解析函数返回的代码。在WebBrowser对象上调用InvokeScript方法应该发生在Dispatcher线程上(因此也发生在视图中),因为它是一个UI元素。
我目前采取的步骤来完成这项工作是(大致在伪):
- subscribe to the LoadCompleted event of the browser (view)
- set the browser source (model -> viewmodel -> view)
- catch the LoadCompleted event (view -> viewmodel -> model)
- some logic (model)
- invoke script (model -> viewmodel -> view)
- get script result (view -> viewmodel -> model)
- some logic (model)
这导致了模型和视图之间(通过视图模型)的一些来回通信。由于我对WPF(或MVVM)没有那么丰富的经验,我想知道是否有一种更整洁的方式来完成这项任务(我所说的更整洁是指:模型、视图模型和视图之间的调用和事件更少)。
我知道这不是你问题的确切答案,但它可能在未来对你有更多帮助。看看名为CefSharp的成分。这是一个围绕Chrome嵌入式框架的c#包装器。它对MVVM非常友好,它是开源的,免费的,而且开发起来相当容易。我最近从另一个Chrome包装(Awesomium)转到了它,对此我很满意
CefSharp允许您从页面调用js函数,它甚至支持异步/等待这些调用。
所以MVVM的要点是将接口(特定于平台:windows\android\ios\windowsphone等)与其他一切分离,这样您就可以在不同的平台上重用逻辑(viewmodel\model)。所以很明显,您不能直接从视图模型调用InvokeScript,但这并不是因为调度程序。Dispatcher,您可以抽象出来(例如),因为在某些情况下,您需要在视图模型中使用它。因此,通常当视图模型需要对视图执行操作时,会使用事件(或仅委托):
public class MyViewModel
{
public Func<string, object> InvokeScript { get; set; }
public Func<string, Task<object>> InvokeScriptAsync { get; set; }
public async void Something() {
var result = await InvokeScriptAsync("my script");
// do something
}
}
在你看来:
public class MyView {
private void OnViewModelChanged(MyViewModel vm) {
vm.InvokeScript = text => Dispatcher.Invoke(() => browser.InvokeScript(text));
vm.InvokeScriptAsync = text => browser.InvokeScriptAsync(text); // for example
}
}