2020年6月24日水曜日

WPFでChromium埋め込みブラウザを使用する [CefSharp]

自分のソフトの中でブラウザを埋め込みたいことってありますよね。
WPFではWebBrowserコントロールが用意されていますが、とにかく使いにくい上に、IE7ベースで動作するという非常に残念なものです。IE11/Edgeに切り替えることもできなくはないようですが…レジストリをいじることになるのでアプリケーションとして使うというよりか、システムとして設定を変更することになってしまいます。
どうせならGoogle Chromeなどが代表的なChromiumブラウザ系統の組み込みブラウザを使いたいですよね。最近、IE系統に対応せずにChrome対応を謳うサイトも増えてきたことですし。まあ私はFirefox派なんですが。

そこで今回紹介するのがCefSharpというライブラリです。
Chromium Embedded Framework (CEF)というChromiumブラウザの表示部分のみを抜き出したのフレームワークがあり、これをC#からアクセスできるようになったものがCefSharpです。

導入

導入は至って簡単です。Nugetから入れることができます。
今回はWPFで使用するので、CefSharp.WPFを入れましょう。そうするとついでにCefSharp.Commonも入ります。
注意点ですが、Targetをx86かx64に指定してやる必要があります。Any CPUだと動きません。

使用

これも簡単です。CefSharp.Wpf.ChromiumWebBrowserをXAML上で定義してあげるだけです。
<Window x:Class="WebBrowserTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WebBrowserTest"
        xmlns:cef="clr-namespace:CefSharp.Wpf;assembly=CefSharp.Wpf"
        mc:Ignorable="d"
        Title="MainWindow" Height="1080" Width="1920">
    <Grid>
        <cef:ChromiumWebBrowser Address="https://www.google.co.jp" />        
    </Grid>
</Window>
めでたくブラウザが表示されます。

Navigate

任意のURLのWebページに遷移したいことがありますよね。
標準のWebBrowserコントロールは厄介で、Sourceプロパティが依存関係プロパティではありません。そのため、バインディングをすることができず、ビヘイビアを使うなどして操作しないといけません。超面倒でした。
CefSharpはそこのところはちゃんと考えてあり、Addressプロパティにバインディングすることができます。
<Window x:Class="WebBrowserTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WebBrowserTest"
        xmlns:cef="clr-namespace:CefSharp.Wpf;assembly=CefSharp.Wpf"
        mc:Ignorable="d"
        Title="MainWindow" Height="1080" Width="1920">
    <DockPanel>
        <TextBox DockPanel.Dock="Top" x:Name="Address" Text="https://www.google.co.jp"/>
        <cef:ChromiumWebBrowser Address="{Binding ElementName=Address, Path=Text, Mode=TwoWay}" />        
    </DockPanel>
</Window>
TextBoxのTextをTwoWayでバインディングすることで、いわゆるブラウザのアドレスバーのような形で使用することができます。

スクリプトを実行

せっかく自分のプログラム上でブラウザを動かしているのですから、何かしらブラウザ上でアクションを起こしたいですよね。
そんな人の為にExecuteScriptAsyncという拡張メソッドが用意されています。実行するにはChromiumWebBrowserのインスタンスが必要ですので、MVVMを維持しながら作るならばビヘイビア等を活用して作る必要がありますが、今回は簡単のために、ボタンを押したらコードビハインドでスクリプトを実行するプログラムを作ってみます。
<Window x:Class="WebBrowserTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WebBrowserTest"
        xmlns:cef="clr-namespace:CefSharp.Wpf;assembly=CefSharp.Wpf"
        mc:Ignorable="d"
        Title="MainWindow" Height="1080" Width="1920">
    <DockPanel>
        <TextBox DockPanel.Dock="Top" x:Name="Address" Text="http://orteil.dashnet.org/cookieclicker/"/>
        <Button DockPanel.Dock="Bottom" Content="Earn a million cookies" Click="Button_Click" />
        <cef:ChromiumWebBrowser x:Name="browser" Address="{Binding ElementName=Address, Path=Text, Mode=TwoWay}" />        
    </DockPanel>
</Window>
public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        browser.ExecuteScriptAsync("Game.Earn(1000000)");
    }
}
こんなコードを用意すると、ボタンを押すだけでクッキーが100万個生産されます。
True NeverclickなんていうAchievementを解除してしまいました。

スクリプトを実行してその値をC#側で受け取りたいこともあると思います。
そういうときは、EvaluateScriptAsync拡張メソッドを使います。
private async void Button_Click(object sender, RoutedEventArgs e)
{
    var response = await browser.EvaluateScriptAsync("document.getElementById('cookies').innerText");

    if(response.Success && response.Result != null)
        MessageBox.Show(response.Result.ToString());
}
非同期メソッドであることに注意してください。
上のメソッドを実行すると、このように'cookies'というIDを持った要素のinnerTextプロパティの値を返してきます。
注意点として、返却値は配列と文字列はOKみたいですが、それ以外のオブジェクトはダメなようです。微妙に使いにくいですが…。


てなところです。
私自身も使い始めたばかりなので全然触れていない部分もありますが、まあ、少なくとも標準のWebBrowserコントロールよりかは使いやすそうです。
こういうの使って何とかC#のステージに引きずり下ろすより、とっととJavascriptをしっかり勉強してブラウザのアドオンとか作ったほうがいいのかなあ。

0 件のコメント:

コメントを投稿