Reputation: 42354
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:
IsEnabled
property of my Back and Forward buttons to correctly bind to the CanGoBack
and CanGoForward
properties of the WebBrowser
.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?
Upvotes: 11
Views: 8413
Reputation: 328
For anyone who comes across this question and wants a complete solution, here it is. It combines all of the suggestions made in this thread and the linked threads (and others those link to).
XAML: http://pastebin.com/aED9pvW8
C# class: http://pastebin.com/n6cW9ZBB
Example XAML usage: http://pastebin.com/JpuNrFq8
Note: The example assumes your view binds to a ViewModel that provides the source URL to the browser. A very rudimentary navigation bar with back, forward, and refresh buttons and address bar is provided just for demonstration.
Enjoy. I have set the expiration on those pastebin's to never, so they should be available for as long as pastebin exists.
Upvotes: 5
Reputation: 106816
Your question seems to imply that in order to correctly implement an MVVM pattern you are not allowed to have any code-behind. But perhaps adding some code-behind to your view will make it much easier to hook it up with your view-model. You can add dependency properties to the view and let it listen for INotifyPropertyChanged
events.
Upvotes: 0
Reputation: 39600
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);
}
Upvotes: 4