Есть ли MVVM-удобный способ использования элемента управления WebBrowser в WPF?

Благодаря этот вопрос (щелкните по мне!), у меня есть свойство Source моего привязки WebBrowser к моей ViewModel.

Теперь я хотел бы достичь еще двух целей:

  • Получите свойство IsEnabled моих кнопок Back и Forward для правильной привязки к свойствам CanGoBack и CanGoForward WebBrowser.
  • Выясните, как вызвать методы 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;" />

Я уверен, проблема в том, что CanGoBack и CanGoForward не являются свойствами зависимостей (и не реализуют INotifyChanged), но я не совсем уверен, как обойти это.

Вопросы:

  • Есть ли способ подключить прикрепленные свойства (как это было в случае с Source) или что-то подобное, чтобы заставить привязки CanGoBack и CanGoForward работать?

  • Как написать GoBackCommand и GoForwardCommand, чтобы они были независимы от кода и ViewModel и могут быть объявлены в разметке?

Ответ 1

Я использовал это в своей привязываемой оболочке 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.

Таким образом, вы можете использовать навигационные команды по умолчанию в своем представлении. Используемые обработчики:

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

Ответ 2

Для тех, кто сталкивается с этим вопросом и хочет полного решения, вот оно. Он сочетает в себе все предложения, сделанные в этом потоке и связанных потоках (и другие ссылки, на которые ссылаются).

XAML: http://pastebin.com/aED9pvW8

Класс С#: http://pastebin.com/n6cW9ZBB

Пример использования XAML: http://pastebin.com/JpuNrFq8

Примечание. В примере предполагается, что ваше представление привязывается к ViewModel, который предоставляет URL-адрес источника для браузера. Для демонстрации предусмотрена очень рудиментарная панель навигации с кнопками "назад", "вперед" и "Обновить" и адресная строка.

Enjoy. Я поставил истечение на те пастебины, чтобы никогда, поэтому они должны быть доступны до тех пор, пока существует пастебин.

Ответ 3

В вашем вопросе подразумевается, что для правильного внедрения шаблона MVVM вам не разрешается иметь какой-либо код. Но, возможно, добавление некоторого кода для вашего взгляда упростит его подключение к вашей модели представлений. Вы можете добавить свойства зависимостей в представление и позволить ему прослушивать события INotifyPropertyChanged.