どうも、「○NAKA」です。
Webブラウザを常に最画面表示しながら、パソコンで作業できるアプリケーションを作成する続きです。
前回はとりあえず、webブラウザを表示して最画面表示ボタンを押すと、アプリケーションが常に最画面に表示されるところまで実装できました。
今回は「進む」ボタンや「戻る」ボタン、webサイトのURLを入力できるテキストボタン等を追加していきます。
前回の記事をまだ読んでない方は、こちらから読むことができますので、是非ご覧下さい。

プログラム実装
- 開発環境:visual studio2019
 - 開発プラットフォーム:WPF
 - ソリューション名:CefWebBrowser
 
UI画面の作成
2行に分割しているGridの1行目にDockpanelを配置します。
Dockpanelは、枠内の上下左右に整列して配置させてくれるコントロールです。DockPanel.Dockで上下左右のどこにコントロールを張り付けるかを指定することができます。
このDockPanelの中に7つのボタンとテキストボックスを追加します。テキストボックスは空いたスペースに配置するので、DockPanelの中の最下部に配置します。
<!--  分割しているGridの1行目に配置(Grid.Row="0")-->
<DockPanel x:Name="naviPanel" Grid.Row="0">
    <!--  戻るボタン  -->
    <Button x:Name="btnUndo" DockPanel.Dock="Left" Width="30" />
    <!--  進むボタン  -->
    <Button x:Name="btnRedo" DockPanel.Dock="Left" Width="30" />
    <!--  更新ボタン  -->
    <Button x:Name="btnReload" DockPanel.Dock="Left" Width="30" />
    <!--  ホームボタン  -->
    <Button x:Name="btnHome" DockPanel.Dock="Left" Width="30" />
    <!--  検索ボタン  -->
    <Button x:Name="btnMagnify" DockPanel.Dock="Right" Width="30" />
    <!--  更新中のプログレスバー  -->
    <ProgressBar x:Name="pgb" DockPanel.Dock="Right" Width="100" />
    <!--  再画面表示ボタン  -->
    <Button x:Name="btnPin" DockPanel.Dock="Right" Width="30" />
    <!--  閉じるボタン  -->
    <Button x:Name="btnClose"  DockPanel.Dock="Right" Width="30" />
    <!--  URL入力テキストボックス  -->
    <TextBox x:Name="txtUrl" />
</DockPanel>
これだけでもいいのですが、更に配置を指定します。
戻るボタン・進むボタン・更新ボタン・ホームボタンはStackpanelの中に配置します。対象コントロールは、左側横方向に整列するので、DockPanel.DockプロパティをLeftにして、OrientationプロパティをHorizontalにします。
同様に、検索ボタン・プログレスバー・最画面表示ボタン・閉じるボタンをStackPanelの中に配置します。対象コントロールは右側横方向に配置するので、DockPanel.DockプロパティをRightにして、OrientationプロパティをHorizontalにします。
<DockPanel x:Name="naviPanel" Grid.Row="0">
    <StackPanel DockPanel.Dock="Left" Orientation="Horizontal">
        <!--  戻るボタン  -->
        <Button x:Name="btnUndo" Width="30" />
        <!--  進むボタン  -->
        <Button x:Name="btnRedo" Width="30" />
        <!--  更新ボタン  -->
        <Button x:Name="btnReload" Width="30" />
        <!--  ホームボタン  -->
        <Button x:Name="btnHome" Width="30" />
    </StackPanel>
    <StackPanel DockPanel.Dock="Right"  Orientation="Horizontal">
        <!--  検索ボタン  -->
        <Button x:Name="btnMagnify" Width="30" />
        <!--  更新中のプログレスバー  -->
        <ProgressBar x:Name="pgb" Width="100" />
        <!--  再画面表示ボタン  -->
        <Button x:Name="btnPin" Width="30" />
        <!--  閉じるボタン  -->
        <Button x:Name="btnClose" Width="30" />
    </StackPanel>
    <!--  URL入力テキストボックス  -->
    <TextBox x:Name="txtUrl" />
</DockPanel>
ボタンは、直感でボタンの役割を理解するためにアイコンで表示します。
そのアイコンは「Material Design Icons」に登録されているアイコンを使うことで、アイコンを揃える面倒な作業を省く事ができます。また、アイコンの種類も豊富でとてもお手軽に使うことができちゃいます。
<!--  分割しているGridの1行目に配置(Grid.Row="0")-->
<DockPanel x:Name="naviPanel" Grid.Row="0">
    <StackPanel DockPanel.Dock="Left" Orientation="Horizontal">
        <!--  戻るボタン  -->
        <Button x:Name="btnUndo" Width="30" >
            <materialDesign:PackIcon Kind="UndoVariant" />
        </Button>
        <!--  進むボタン  -->
        <Button x:Name="btnRedo" Width="30" >
            <materialDesign:PackIcon Kind="RedoVariant" />
        </Button>
        <!--  更新ボタン  -->
        <Button x:Name="btnReload" Width="30" >
            <materialDesign:PackIcon Kind="Reload" />
        </Button>
        <!--  ホームボタン  -->
        <Button x:Name="btnHome" Width="30" >
            <materialDesign:PackIcon Kind="Home" />
        </Button>
    </StackPanel>
    <StackPanel DockPanel.Dock="Right"  Orientation="Horizontal">
        <!--  検索ボタン  -->
        <Button x:Name="btnMagnify" Width="30">
            <materialDesign:PackIcon Kind="Magnify" />
        </Button>
        <!--  更新中のプログレスバー  -->
        <ProgressBar x:Name="pgb" Width="100" />
        <!--  再画面表示ボタン  -->
        <Button x:Name="btnPin" Width="30" >
            <materialDesign:PackIcon x:Name="IcnPin" Kind="Pin" />
        </Button>
        <!--  閉じるボタン  -->
        <Button x:Name="btnClose" Width="30" >
            <materialDesign:PackIcon Kind="Close" />
        </Button>
    </StackPanel>
    <!--  URL入力テキストボックス  -->
    <TextBox x:Name="txtUrl" />
</DockPanel>
初期フォーカスをURL入力するテキストボックスに設定します。GridにFocusManager.FocusElementを設定してください。
その後にボタンやテキストボックスのサイズや背面色を調整します。
<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="30" />
        <RowDefinition />
    </Grid.RowDefinitions>
    <DockPanel x:Name="naviPanel" Grid.Row="0" Background="#E9E9E9" 
                   FocusManager.FocusedElement="{Binding ElementName=txtUrl}">
        <StackPanel DockPanel.Dock="Left" Orientation="Horizontal" >
            <!--  戻るボタン  -->
            <Button
                    x:Name="btnUndo" Width="30" Background="Transparent" 
                    BorderThickness="0" ToolTip="Undo">
                <materialDesign:PackIcon Kind="UndoVariant" />
            </Button>
            <!--  進むボタン  -->
            <Button
                    x:Name="btnRedo" Width="30" Background="Transparent"
                    BorderThickness="0" ToolTip="Redo">
                <materialDesign:PackIcon Kind="RedoVariant" />
            </Button>
            <!--  更新ボタン  -->
            <Button
                    x:Name="btnReload" Width="30" Background="Transparent"
                    BorderThickness="0" ToolTip="Reload">
                <materialDesign:PackIcon Kind="Reload" />
            </Button>
            <!--  ホームボタン  -->
            <Button
                    x:Name="btnHome" Width="30" Margin="5,0,0,0" Background="Transparent" BorderThickness="0"
                    Click="btnHome_Click" ToolTip="Home">
                <materialDesign:PackIcon Kind="Home" />
            </Button>
        </StackPanel>
        <StackPanel DockPanel.Dock="Right" Orientation="Horizontal">
            <!--  検索ボタン  -->
            <Button
                    x:Name="btnMagnify" Width="30" Background="Transparent" BorderThickness="0"
                    Click="btnMagnify_Click" ToolTip="Magnify">
                <materialDesign:PackIcon Kind="Magnify" />
            </Button>
            <!--  更新中のプログレスバー  -->
            <ProgressBar x:Name="pgb" Width="100" Margin="0,5,0,5" 
                             IsIndeterminate="{Binding WebBrowser.IsLoading}" />
            <!--  再画面表示ボタン  -->
            <Button
                    x:Name="btnPin" Width="30" Margin="5,0,0,0" Background="Transparent" BorderThickness="0"
                    Click="btnPin_Click" ToolTip="Pin">
                <materialDesign:PackIcon x:Name="IcnPin" Kind="Pin" />
            </Button>
            <!--  閉じるボタン  -->
            <Button
                    x:Name="btnClose" Width="30" Margin="5,0,0,0" Background="Transparent" BorderThickness="0"
                    Click="btnClose_Click" ToolTip="Close">
                <materialDesign:PackIcon Kind="Close" />
            </Button>
        </StackPanel>
        <!--  URL入力テキストボックス  -->
        <TextBox
                x:Name="txtUrl" BorderThickness="0" Text="{Binding WebBrowser.Address}"
                VerticalContentAlignment="Center" Margin="0,1,0,1"/>
    </DockPanel>
    <!--  Webブラウザコントロール  -->
    <cef:ChromiumWebBrowser
            x:Name="browser" Grid.Row="1" Margin="0,0,0,0" Address="{Binding WebBrowser.Address}" />
</Grid>
画面はこんな感じになります。必要な機能しかないので、とてもシンプルです。

コードの実装
WPFでのアプリケーション開発する場合は、MVVMパターン(Model-View-ViewModel-Pattern)を利用しての開発を推奨されています。アプリケーションをModel,View,ViewModelの3つの層に分割して開発する手法です。MVVMパターンを用いて開発するためにデータバインドなどの機能を使って実装をします。
ViewModelクラス
ソリューションエクスプロラーのプロジェクト名の上で右クリックします。「追加」>「新しい項目の追加(W)…」の順にクリックしてViewModelという名前のクラスを作成します。
ViewModelはINotifyPropertyChangedを実装したベースクラスを継承したクラスにします。INotifyPropertyChanged が参照できるようにusingディレクティブにSystem.ComponentModelを追加します。
CefSharpのWebBrowserのプロパティを作成します。このプロパティが変化したときに発生させるイベント(NotifyProperyChanged)を定義し、NotifyProperyChangedイベントを発火させるためのコードをsetter内に記述をします。
class ViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    private void NotifyProperyChanged(string name)
    {
        this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
    }
    private IWpfWebBrowser _WebBrowser;
    public IWpfWebBrowser WebBrowser
    {
        get { return _WebBrowser; }
        set
        {
            _WebBrowser = value;
            this.NotifyProperyChanged(nameof(WebBrowser));
        }
    }
}
DataContextにバインド
ViewとViewModelはDataContextプロパティを介してやりとりをします。先程作成したViewModelのインスタンスをDataContextプロパティに設定します。こうすることでViewとViewModelの紐付けができるようになります。
private ViewModel vm = new ViewModel();
public MainWindow()
{
    InitializeComponent();
    //DataContectにViewModelをバインド
    DataContext = vm;
}
CefSharpの初期設定
ブラウザの初期化処理を作成し、コンストラクタで呼び出しをします。戻る・進む・更新ボタンは、CefSharpで用意されているコマンドをそれぞれのボタンに設定します。ブラウザの言語設定を日本にします。
private void InitBrowser()
{
    //戻るボタンのコマンド設定
    btnUndo.Command = browser.BackCommand;
    //進むボタンのコマンド設定
    btnRedo.Command = browser.ForwardCommand;
    //更新ボタンのコマンド設定
    btnReload.Command = browser.ReloadCommand;
    //言語設定
    browser.BrowserSettings.AcceptLanguageList = "ja-JP";
}
Webサイトを開く処理
WebBrowserのプロパティをCefBroweserに設定します。browserOpenメソッドの引数はURLになるので、Loadメソッドの引数に渡してWebサイトにアクセスをします。
private void browserOpen(string url)
{
    vm.WebBrowser = browser;
    if (browser.IsLoaded) { browser.Load(url); }
    else browser.Address = url;
}
ホーム画面に移動する処理
ホームボタンがクリックされたときに、先程作成したbrowserOpenメソッドの引数にホーム画面のURLを渡します。
private void btnHome_Click(object sender, RoutedEventArgs e)
{
    //ホーム画面に移動
    browserOpen("https://www.google.co.jp/");
}
URLの検索処理
検索ボタンがクリックされたときに、先程作成したbrowserOpenメソッドの引数にテキストボックスに入力されたURLを渡します。
private void btnMagnify_Click(object sender, RoutedEventArgs e)
{
    //URL検索
    browserOpen(vm.WebBrowser.Address);
}
アプリケーションの起動
さっそく、アプリケーションを起動してみましょう。ホーム画面に表示されるWebサイトに指定したGoogleが問題なく表示されました。

Googleで適当に検索をしてみます。検索結果を更新中の時はプログレスバー内で一定幅の目盛りが繰り返し左から右へ動いて、更新が完了したら一定幅の目盛りが非表示になります。検索結果も無事表示されました。なかなかいい感じです。

さて、肝心の動画は視聴できるのでしょうか。YouTubeにアクセスして動画を再生してみます。ヒカキンの動画もサクサクと動きますね。

これで1つのWebブラウザを常に最画面表示しながら、パソコン上で作業ができるアプリケーションの完成です。動画を集中してみれるのでブログを書く効率も上がりそうです(笑)。
以上、最後までご覧いただきありがとうございました。

