1

I have an application implementing the WPF WebBrowser control. It loads a page containing some JS functions that have to be called from my application, possibly from other threads. Preferrably, I would like to stick to the MVVM pattern and keep the code for parsing the function return in the model. Calling the InvokeScript method on the WebBrowser object should happen on the Dispatcher thread (and thus in the view), since it is a UI element.

The steps I currently take to get this job done is (roughly in pseudo):

- 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)

This results in quite some back-and-forth communication between the model and the view (through the viewmodel). Since I am not that experienced with WPF (or MVVM in that matter), I am wondering whether there is a neater way of accomplishing this task (and by neater I mean: less calls and events between the model, viewmodel and view).

Bart
  • 21
  • 2

2 Answers2

0

I know this is not exactly the answer to your question, but it might help you even more in the future. Take a look at the component called CefSharp. That's a c# wrapper around Chrome Embedded Framework. It is very MVVM friendly, it is open source, free and it's reasonably easy to develop for. I've recently moved to it from another Chrome wrapper (Awesomium) and very happy with it.

CefSharp allows you to call js functions from the page and it even supports async/await for those calls.

Nick Sologoub
  • 724
  • 1
  • 7
  • 21
  • That a nice suggestion. However, what are the actual advantages of CefSharp compared to either Awasomium or the WPF WebBrowser then? – Bart Apr 03 '16 at 02:48
  • The advantage over Awesomium is that CefSharp is opensource and free. The support for Awesomium is very lacking. They want you to believe they are a big and serious company, but the truth is - it's just one guy. Since the code is not open you are at the mercy of the developer to fix the bugs. And the project moves VERY slowly. I believe they are still using something like chrome 16. – Nick Sologoub Apr 03 '16 at 15:45
  • When it comes to native WPF WebBrowser control I will admit I don't have much experience with it. We kinda went the way of 3rd party controls from the start. But there are some things that come to mind. Like for example the fact that CefSharp is built with MVVM pattern very much in mind. Pretty much everything is a Command or DependencyProperty. Plus a lot interaction with the browser is done via async/await patter. That is also a positive thing over native WPF control. – Nick Sologoub Apr 03 '16 at 15:48
  • The community support here at SO for CefSharp is quite strong too. And you can always go to github and raise issue directly with people working on it, or even take a look at the code yourself =) – Nick Sologoub Apr 03 '16 at 15:49
0

So main point of MVVM is to separate interface (which is platform specific: windows\android\ios\windows phone etc etc) from everything else, so that you could reuse logic (viewmodel\model) on different platforms. So it's clear you cannot call InvokeScript directly from viewmodel - but not because of dispatcher. Dispatcher you can abstract out (for example), since in some cases you need it in your view model. So usually when viewmodel needs to perform operation on view - events (or just delegates) are used:

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
    }
}

And in your view:

public class MyView {
    private void OnViewModelChanged(MyViewModel vm) {
        vm.InvokeScript = text => Dispatcher.Invoke(() => browser.InvokeScript(text));
        vm.InvokeScriptAsync = text => browser.InvokeScriptAsync(text); // for example
    }
}
Community
  • 1
  • 1
Evk
  • 98,527
  • 8
  • 141
  • 191
  • Yes, I completely understand. What I am trying to accomplish is indeed to separate view from (view)model. So what you are saying is that I should just use events (or delegates) for the communication from viewmodel to view? – Bart Apr 03 '16 at 02:43
  • Yes, and not bother with dispatcher in vm unless necessary (I.e. when you use ObservableCollection). Usually most logic (except business logic) are in viewmodel, not in model, so there should not be many interactions from model to view. – Evk Apr 03 '16 at 08:42