WebBrowser.NavigateToString を使ったビヘイビアを用意する。
先日のエントリ 「WPF で Google Map API を使ってルート検索してみる(Livet版)」 では、View のコードビハインドで WebBrowser.NavigateToString メソッドを呼び出してたわけですが、昨日 MSDN フォーラム見てたら、以前私がビヘイビア書いてたスレ見つけました。すっかり忘れてた・・・orz
関連記事 :WPFにはWebBrowser.DocumentTextはないのでしょうか
というわけで、先日のエントリのビヘイビア使ったバージョンです。
ビヘイビア
まずビヘイビアを用意し、WebBrowser.NavigateToString を View のコードビハインド内でコールしなくてもいいようにします。
WebBrowserBehaiviors.vb
Option Explicit On Option Strict On Public Class WebBrowserBehaiviors #Region "DocumentText" Public Shared ReadOnly DocumentTextProperty As DependencyProperty = DependencyProperty.RegisterAttached( "DocumentText", GetType(String), GetType(WebBrowserBehaiviors), New UIPropertyMetadata(String.Empty, AddressOf OnDocumentTextChanged) ) <AttachedPropertyBrowsableForType(GetType(WebBrowser))> _ Public Shared Function GetDocumentText(obj As DependencyObject) As String Return obj.GetValue(DocumentTextProperty).ToString() End Function <AttachedPropertyBrowsableForType(GetType(WebBrowser))> _ Public Shared Sub SetDocumentText(obj As DependencyObject, value As String) obj.SetValue(DocumentTextProperty, value) End Sub Private Shared Sub OnDocumentTextChanged(sender As DependencyObject, e As DependencyPropertyChangedEventArgs) Dim browser = TryCast(sender, WebBrowser) If (browser Is Nothing) Then Return If (e.NewValue Is Nothing) Then Return Dim document = e.NewValue.ToString() browser.NavigateToString(document) End Sub #End Region End Class
Livet 調べるとかなり大量にビヘイビアが用意されていて、WebBrowser 用のビヘイビアも幾つか存在します。ただし WebBrowser.NavigateToString 用のビヘイビアは見当たりませんでしたね。これもそのうち提供されるでしょう・・・と勝手に期待しときますw
Model
モデルは先日のと変わりません。
Customer.vb
Public Class Customer Inherits NotificationObject Public Property Address As String End Class
Customers.vb
Imports System.Collections.ObjectModel Public Class Customers Inherits NotificationObject Private _customers As ObservableCollection(Of Customer) Default Public Property Items(index As Integer) As ObservableCollection(Of Customer) Get Return _customers End Get Set(ByVal value As ObservableCollection(Of Customer)) _customers = value End Set End Property End Class
ViewModel
ViewModel はビヘイビアと通信できるよう Document プロパティを追加し、View のコマンドを ViewModel に移動します。
#Region "Document変更通知プロパティ" Private _Document As String Public Property Document() As String Get Return _Document End Get Set(ByVal value As String) 'If (_Document = value) Then Return _Document = value RaisePropertyChanged("Document") End Set End Property #End Region ・・・・・・・・・・ #Region "NavigateCommand" Private _NavigateCommand As ViewModelCommand Public ReadOnly Property NavigateCommand() As ViewModelCommand Get If _NavigateCommand Is Nothing Then _NavigateCommand = New ViewModelCommand(AddressOf Navigate, AddressOf CanNavigate) End If Return _NavigateCommand End Get End Property Private Function CanNavigate() As Boolean Return True End Function Private Sub Navigate() Me.Document = GetNavigateString() End Sub #End Region
ViewModel のコード全文です。
MainWindowViewModel.vb
Imports System.Collections.ObjectModel Public Class MainWindowViewModel Inherits ViewModel #Region "コンストラクタ" Public Sub New() _Customers.Add(New Customer() With {.Address = "東京駅"}) End Sub #End Region #Region "Address変更通知プロパティ" Private _Address As String Public Property Address() As String Get Return _Address End Get Set(ByVal value As String) If (_Address = value) Then Return _Address = value RaisePropertyChanged("Address") Me.AddItemCommand = New ViewModelCommand(AddressOf AddItem, AddressOf CanAddItem) End Set End Property #End Region #Region "Customer変更通知プロパティ" Private _Customer As Customer Public Property Customer() As Customer Get Return _Customer End Get Set(ByVal value As Customer) _Customer = value RaisePropertyChanged("Customer") End Set End Property #End Region #Region "Customers変更通知プロパティ" Private _Customers As New ObservableCollection(Of Customer) Public Property Customers() As ObservableCollection(Of Customer) Get Return _Customers End Get Set(ByVal value As ObservableCollection(Of Customer)) _Customers = value RaisePropertyChanged("Customers") End Set End Property #End Region #Region "Document変更通知プロパティ" Private _Document As String Public Property Document() As String Get Return _Document End Get Set(ByVal value As String) 'If (_Document = value) Then Return _Document = value RaisePropertyChanged("Document") End Set End Property #End Region #Region "AddItemCommand" Private _AddItemCommand As ViewModelCommand Public Property AddItemCommand() As ViewModelCommand Get If _AddItemCommand Is Nothing Then _AddItemCommand = New ViewModelCommand(AddressOf AddItem, AddressOf CanAddItem) End If Return _AddItemCommand End Get Private Set(value As ViewModelCommand) _AddItemCommand = value RaisePropertyChanged("AddItemCommand") End Set End Property Private Function CanAddItem() As Boolean If (String.IsNullOrEmpty(Me.Address)) Then Return False End If Return _Customers.Count <= 25 End Function Private Sub AddItem() _Customers.Add(New Customer() With {.Address = Me.Address}) Me.Address = String.Empty End Sub #End Region #Region "DeleteCommand" Private _DeleteCommand As ViewModelCommand Public ReadOnly Property DeleteCommand() As ViewModelCommand Get If _DeleteCommand Is Nothing Then _DeleteCommand = New ViewModelCommand(AddressOf Delete, AddressOf CanDelete) End If Return _DeleteCommand End Get End Property Private Function CanDelete() As Boolean Return True End Function Private Sub Delete() _Customers.Remove(Me.Customer) End Sub #End Region #Region "NavigateCommand" Private _NavigateCommand As ViewModelCommand Public ReadOnly Property NavigateCommand() As ViewModelCommand Get If _NavigateCommand Is Nothing Then _NavigateCommand = New ViewModelCommand(AddressOf Navigate, AddressOf CanNavigate) End If Return _NavigateCommand End Get End Property Private Function CanNavigate() As Boolean Return True End Function Private Sub Navigate() Me.Document = GetNavigateString() End Sub #End Region #Region "メソッド" Public Function GetNavigateString() As String Dim html As New System.Text.StringBuilder() With html .AppendLine("<!DOCTYPE html '-//W3C//DTD XHTML 1.0 Strict//EN' ") .AppendLine(" 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd'> ") .AppendLine("<!-- saved from url=(0017)http://localhost/ -->") .AppendLine("<html xmlns='http://www.w3.org/1999/xhtml'> ") .AppendLine(" <head> ") .AppendLine(" <meta http-equiv='content-type' content='text/html; charset=utf-8'/> ") .AppendLine(" <script type='text/javascript' src='http://maps.google.com/maps?file=api&key=(key)&sensor=false'></script> ") .AppendLine(" <script type='text/javascript'> ") .AppendLine(" var map; ") .AppendLine(" var directions; ") .AppendLine() .AppendLine(" function initialize() { ") .AppendLine(" if (GBrowserIsCompatible()) { ") .AppendLine(" map = new GMap2(document.getElementById('map_canvas')); ") .AppendLine(" map.enableScrollWheelZoom(); ") .AppendLine(" map.setCenter(new GLatLng(35.681379, 139.765577), 13); ") .AppendLine(" directions = new GDirections(map, document.getElementById('route')); ") .AppendFormat(" var pointArray = [{0}]; ", GetPointList()) .AppendLine() .AppendLine(" directions.loadFromWaypoints(pointArray,{ locale: 'ja_JP' }); ") .AppendLine(" } ") .AppendLine(" } ") .AppendLine(" </script> ") .AppendLine("</head> ") .AppendLine(" <body onload='initialize()'> ") .AppendLine(" <div id='map_canvas' style='width: 100%; height: 400px'></div> ") .AppendLine(" <div id='route' style='width: 100%; height: Auto'></div> ") .AppendLine(" </body> ") .AppendLine("</html> ") End With Return html.ToString() End Function Private Function GetPointList() As String Dim ret As String = String.Empty For Each item In _customers ret += "'" + item.Address + "'," Next If (Not String.IsNullOrEmpty(ret)) Then ret = ret.Substring(0, ret.Length - 1) End If Return ret End Function #End Region End Class
View
これでコードビハインドが綺麗になくなりました。MainWindow.xaml.vb も不要になったので Class を Window に変えてしまいます。こうすることでコードビハインドが使用不能になり、強制的に ViewModel を使わざるを得なくなります。
MainWindow.xaml
<Window x:Class="Window" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions" xmlns:l="http://schemas.livet-mvvm.net/2011/wpf" xmlns:local="clr-namespace:LivetWPFApplication1" Title="MainWindow" Height="630" Width="900" WindowStartupLocation="CenterScreen" > <Window.DataContext> <local:MainWindowViewModel /> </Window.DataContext> <i:Interaction.Triggers> <i:EventTrigger EventName="Loaded"> <i:InvokeCommandAction Command="{Binding NavigateCommand}" /> </i:EventTrigger> </i:Interaction.Triggers> <Grid> <Grid.RowDefinitions> <RowDefinition Height="8" /> <RowDefinition Height="135" /> <RowDefinition Height="8" /> <RowDefinition /> <RowDefinition Height="8" /> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="8" /> <ColumnDefinition /> <ColumnDefinition Width="8" /> </Grid.ColumnDefinitions> <Grid Grid.Column="1" Grid.Row="1" > <Grid.ColumnDefinitions> <ColumnDefinition /> <ColumnDefinition Width="8" /> <ColumnDefinition /> </Grid.ColumnDefinitions> <ListBox Grid.Row="0" Grid.Column="0" DisplayMemberPath="Address" ItemsSource="{Binding Customers}" SelectedValue="{Binding Customer}" > <ListBox.InputBindings> <KeyBinding Key="Delete" Command="{Binding DeleteCommand}" /> </ListBox.InputBindings> </ListBox> <StackPanel Grid.Column="2"> <Label Height="24" Content="ルート検索する住所を入力してください" /> <TextBox Height="24" VerticalContentAlignment="Center" InputMethod.PreferredImeState="On" InputMethod.PreferredImeConversionMode="FullShape,Native" Text="{Binding Address, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/> <Button Width="80" Height="24" Margin="0,12,0,0" Content="追加" HorizontalAlignment="Right" Command="{Binding AddItemCommand}"/> <Button Width="80" Height="24" Margin="0,12,0,0" Content="検索" HorizontalAlignment="Right" Command="{Binding NavigateCommand}" /> </StackPanel> </Grid> <WebBrowser x:Name="browser" Grid.Row="3" Grid.Column="1" local:WebBrowserBehaiviors.DocumentText="{Binding Document}" /> </Grid> </Window>
実行するとこうなる・・・
おまけ
ビヘイビアのサンプルとしてはやや冗長なサンプルになってしまいました。同じ WebBrowser.NavigateToString を使ったビヘイビアのサンプルでは、id:trapemiya さんの方がよりシンプルで判りやすいです。
関連記事 :Blendのビヘイビアを使った簡単な例。ViewにあるコントロールのメソッドをViewModelから実行する。