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="< Back" /> <Button Style="{StaticResource Button_Style}" Grid.Column="4" IsEnabled="{Binding ElementName=_instructionsWebBrowser, Path=CanGoForward}" Command="{Binding GoForwardCommand}" Content="Forward >" /> ``` 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?