Is there an MVVM-friendly way to use the WebBrowser control in WPF?

data-binding, mvvm, webbrowser-control, wpf, xaml

Solution

I used this in my bindable webbrowser wrapper:

    CommandBindings.Add(new CommandBinding(NavigationCommands.BrowseBack, BrowseBack, CanBrowseBack));
    CommandBindings.Add(new CommandBinding(NavigationCommands.BrowseForward, BrowseForward, CanBrowseForward));
    CommandBindings.Add(new CommandBinding(NavigationCommands.BrowseHome, GoHome, TrueCanExecute));
    CommandBindings.Add(new CommandBinding(NavigationCommands.Refresh, Refresh, TrueCanExecute));
    CommandBindings.Add(new CommandBinding(NavigationCommands.BrowseStop, Stop, TrueCanExecute));

Note that I created my bindable webbrowser as FrameworkElement that exposes DependencyProperties and calls methods on the actual browser element, so i can set CommandBindings on it.

That way, you can use the default NavigationCommands in your View. The used handlers are:

private void CanBrowseBack(object sender, CanExecuteRoutedEventArgs e) {
    e.CanExecute = webBrowser.CanGoBack;
}

private void BrowseBack(object sender, ExecutedRoutedEventArgs e) {
    webBrowser.GoBack();
}

private void CanBrowseForward(object sender, CanExecuteRoutedEventArgs e) {
    e.CanExecute = webBrowser.CanGoForward;
}

private void BrowseForward(object sender, ExecutedRoutedEventArgs e) {
    webBrowser.GoForward();
}

private void TrueCanExecute(object sender, CanExecuteRoutedEventArgs e) { e.CanExecute = true; }

private void Refresh(object sender, ExecutedRoutedEventArgs e) {
    try { webBrowser.Refresh(); }
    catch (Exception ex) { PmsLog.LogException(ex, true); }
}

private void Stop(object sender, ExecutedRoutedEventArgs e) {
    mshtml.IHTMLDocument2 doc = WebBrowser.Document as mshtml.IHTMLDocument2;
    if (doc != null)
        doc.execCommand("Stop", true, null);
}
private void GoHome(object sender, ExecutedRoutedEventArgs e) {
    Source = new Uri(Home);
}

Problem

Thanks to this question (click me!), I have the `Source` property of my `WebBrowser` binding correctly to my ViewModel. Now I'd like to achieve two more goals: - Get the `IsEnabled` property of my Back and Forward buttons to correctly bind to the `CanGoBack` and `CanGoForward` properties of the `WebBrowser`. - Figure out how to call the `GoForward()` and `GoBack()` methods without resorting to the code-behind and without the ViewModel having to know about the `WebBrowser`. I have the following (non-working) XAML markup at the moment: ``` <WebBrowser x:Name="_instructionsWebBrowser" x:FieldModifier="private" clwm:WebBrowserUtility.AttachedSource="{Binding InstructionsSource}" /> <Button Style="{StaticResource Button_Style}" Grid.Column="2" IsEnabled="{Binding ElementName=_instructionsWebBrowser, Path=CanGoBack}" Command="{Binding GoBackCommand}" Content="&lt; Back" /> <Button Style="{StaticResource Button_Style}" Grid.Column="4" IsEnabled="{Binding ElementName=_instructionsWebBrowser, Path=CanGoForward}" Command="{Binding GoForwardCommand}" Content="Forward &gt;" /> ``` I'm pretty sure the problem is that `CanGoBack` and `CanGoForward` are not dependency properties (and don't implement `INotifyChanged`), but I'm not quite sure how to get around that. Questions: Is there any way to hook up attached properties (as I did with `Source`) or something similar to get the `CanGoBack` and `CanGoForward` bindings to work? How do write the `GoBackCommand` and `GoForwardCommand` so they are independent of the code-behind and ViewModel and can be declared in markup?

Original source

Related problems