在WPF中,是否有一种MVVM友好的方式来使用WebBrowser控件?

11

感谢这个问题(点击我!)让我正确地将我的 WebBrowserSource 属性绑定到了我的 ViewModel。

现在,我想实现两个目标:

  1. 使我的后退和前进按钮的 IsEnabled 属性正确地绑定到 WebBrowserCanGoBackCanGoForward 属性上。
  2. 找出如何调用 GoForward()GoBack() 方法,而不必诉诸于代码后台,也不必让 ViewModel 知道 WebBrowser 的存在。

我目前拥有以下(不起作用的)XAML 标记:

<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;" />

我相当确定问题是因为CanGoBackCanGoForward不是依赖属性(也没有实现INotifyChanged),但我不太确定如何解决这个问题。

问题:

  1. 有没有办法连接附加属性(就像我使用Source那样)或类似的东西来使CanGoBackCanGoForward绑定正常工作?

  2. 如何编写GoBackCommandGoForwardCommand,使它们独立于代码和ViewModel,并可以在标记中声明?

3个回答

5
对于任何看到这个问题并想要完整解决方案的人,这里提供一个方案。它结合了此线程和链接线程中所有建议(以及其他链接到这些线程的内容)。

XAML: http://pastebin.com/aED9pvW8

C#类: http://pastebin.com/n6cW9ZBB

示例XAML用法: http://pastebin.com/JpuNrFq8

注意:示例假设您的视图绑定到一个ViewModel,该ViewModel为浏览器提供源URL。仅作演示,提供了非常基本的导航栏,包括后退、前进和刷新按钮以及地址栏。

享受吧。我已将这些pastebin的过期时间设置为永远,因此只要pastebin存在,它们就应该可用。


非常好,谢谢。我想知道_SkipSourceChange是用来做什么的? - Peter

4

我在可绑定的WebBrowser包装器中使用了这个:

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

请注意,我将我的可绑定WebBrowser创建为FrameworkElement,公开DependencyProperties并在实际浏览器元素上调用方法,因此我可以在其上设置CommandBindings。
这样,您可以在您的视图中使用默认的NavigationCommands。 使用的处理程序为:
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);
}

你实际上不需要一个UserControl,只要正确设置FrameworkElement就可以了。我的实现可能不是最好的,但如果你想看一下,控件可以在这里找到:http://pastebin.com/m492dbd3f(如果你想知道BrowserViewModel是什么,它是控件属性实际绑定的ViewModel,我相信你已经有自己的ViewModel了)。 - Botz3000
// 顺便提一下,如果你从FrameworkElement派生出来,你可以调用AddVisualChild和AddLogicalChild将WebBrowser或其他任何控件放在其中 - Botz3000
@Botz,我还尝试了简单的 Command="NavigationCommands.BrowseForward",但这也不起作用。 - devuxer
哦!是的,有一件事情漏了。CommandTarget="{Binding ElementName=_instructionsWebBrowser}" - Botz3000
@Botz3000:您介意将控件再放回pastebin几天吗?谢谢。 - VoidDweller
显示剩余3条评论

0

从你的问题中可以看出,你好像认为在正确实现MVVM模式时不允许有任何代码后台。但是也许在视图中添加一些代码后台会使其更容易与视图模型连接起来。你可以向视图添加依赖属性,并让它监听INotifyPropertyChanged事件。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接