WebView2でDevToolsを埋め込んでみた

## こんな事をした理由 
WebView2(Chromium)ではDevToolsが外に出てしまうのですがChromeとかだと同じウィンドウの中で区切られていて見やすかったので実装してみました.
## 完成品

<img width="600" alt="preview.gif" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1566451/14f9818f-cec3-306b-2efd-add8a92e292b.gif">

## 制作環境
* Visual Studio 2022
* WPF(.NET Framework 4.7.2)
* WebView2(Microsoft.Web.WebView2.1.0.1264.42)
## 作り方
まずベースのウィンドウとGridを作っていきます
```xml
<Window x:Class="WebView2SplitTest.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:WebView2SplitTest"
        xmlns:webview="clr-namespace:Microsoft.Web.WebView2.Wpf;assembly=Microsoft.Web.WebView2.Wpf"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        
    </Grid>
</Window>
```
次にGridの中にWebView2,GridSplitterとWindowsFormsHostを配置します.
そしてGrid.ColumnDefinitionsも書いていきます.(分かる方はお好みで変更してください)
(あえてWindowsFormsHostを使っている理由は,あとでHandleを取得してコードを書いていくのですが,WinFormsのコントロールはHandleが取得しやすいのでこのようにしています)
```xml
<Window x:Class="WebView2SplitTest.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:WebView2SplitTest"
        xmlns:webview="clr-namespace:Microsoft.Web.WebView2.Wpf;assembly=Microsoft.Web.WebView2.Wpf"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="5" />
            <ColumnDefinition MinWidth="325" MaxWidth="600" Width="Auto"  />
        </Grid.ColumnDefinitions>
        <webview:WebView2 Grid.Column="0" x:Name="webview21" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Source="https://google.com" />
        <GridSplitter Grid.Column="1" Background="#FF3E3E3E" Width="5" HorizontalAlignment="Stretch" />
        <WindowsFormsHost x:Name="windowsFormsHost1" Grid.Column="2" SizeChanged="windowsFormsHost1_SizeChanged"/>
    </Grid>
</Window>
```
ではコードの方を書いていきます  
最初に「ソリューションエクスプローラー」から「参照」を右クリックし「参照を追加」からSystem.Windows.Formsを追加してください.
次にusingをこのように記述し、
```c#
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using System.Windows;
```
その下に
```c#
private System.Windows.Forms.Panel _panel;
private Process _process;
const int SWP_NOZORDER = 0x0004;
const int SWP_NOACTIVATE = 0x0010;
const int WS_BORDER = 8388608;
const int WS_DLGFRAME = 4194304;
const int WS_CAPTION = WS_BORDER | WS_DLGFRAME;
const int WS_SYSMENU = 524288;
const int WS_THICKFRAME = 262144;
const int GWL_STYLE = -16;
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true)]
public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, int uFlags);
[DllImport("user32.dll", SetLastError = true)]
public static extern int GetWindowLong(IntPtr hWnd, int nIndex);
[DllImport("user32.dll")]
public static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);
[DllImport("user32.dll")]
public static extern IntPtr SetParent(IntPtr hWnd, IntPtr hWndParent);
```
と記入します

そして「public MainWindow()」の中の「InitializeComponent()」の一行下ににこの内容を追加します
```c#
_panel = new System.Windows.Forms.Panel();
windowsFormsHost1.Child = _panel;
this.Loaded += async (sender, e) =>
{
   await webview21.EnsureCoreWebView2Async();
   ShowDevTools();
};
```
最後に下記の内容を「public MainWindow()」の下に記述します
```c#
protected override void OnClosing(System.ComponentModel.CancelEventArgs e)
{
	base.OnClosing(e);
	if (_process != null)
	{
		_process.Refresh();
		_process.Close();
	}
}

private async void ShowDevTools()
{
	if (_process != null)
	{
		_process.CloseMainWindow();
	}
	webview21.CoreWebView2.OpenDevToolsWindow();
	await Task.Delay(300);
	//Get foreground window
	Process[] processes = Process.GetProcessesByName("msedgewebview2");
	foreach (Process p in processes)
	{
		if (p.MainWindowTitle.StartsWith("DevTools"))
		{
			_process = p;
			IntPtr windowHandle = p.MainWindowHandle;
			SetWindowLong(windowHandle, GWL_STYLE, (int)(GetWindowLong(windowHandle, GWL_STYLE) & (0xFFFFFFFF ^ WS_SYSMENU)));
			SetParent(p.MainWindowHandle, _panel.Handle);
			int style = GetWindowLong(_process.MainWindowHandle, GWL_STYLE);
			style = style & ~WS_CAPTION & ~WS_THICKFRAME;
			SetWindowLong(p.MainWindowHandle, GWL_STYLE, style);
			ResizeEmbeddedApp();
			break;
		}
	}
}

private void ResizeEmbeddedApp()
{
	if (_process == null)
		return;

	SetWindowPos(_process.MainWindowHandle, IntPtr.Zero, -10, -35, (int)_panel.ClientSize.Width + 18, (int)_panel.ClientSize.Height + 43, SWP_NOZORDER | SWP_NOACTIVATE);
}

private void windowsFormsHost1_SizeChanged(object sender, SizeChangedEventArgs e)
{
	ResizeEmbeddedApp();
}

protected override Size MeasureOverride(Size availableSize)
{
	Size size = base.MeasureOverride(availableSize);
	ResizeEmbeddedApp();
	return size;
}
```

完成したコード
```c#
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using System.Windows;

namespace WebView2SplitTest
{
    public partial class MainWindow : Window
    {
        private System.Windows.Forms.Panel _panel;
        private Process _process;
        const int SWP_NOZORDER = 0x0004;
        const int SWP_NOACTIVATE = 0x0010;
        const int WS_BORDER = 8388608;
        const int WS_DLGFRAME = 4194304;
        const int WS_CAPTION = WS_BORDER | WS_DLGFRAME;
        const int WS_SYSMENU = 524288;
        const int WS_THICKFRAME = 262144;
        const int GWL_STYLE = -16;

        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true)]
        public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, int uFlags);

        [DllImport("user32.dll", SetLastError = true)]
        public static extern int GetWindowLong(IntPtr hWnd, int nIndex);

        [DllImport("user32.dll")]
        public static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);

        [DllImport("user32.dll")]
        public static extern IntPtr SetParent(IntPtr hWnd, IntPtr hWndParent);
        public MainWindow()
        {
            InitializeComponent();
            _panel = new System.Windows.Forms.Panel();
            windowsFormsHost1.Child = _panel;
            this.Loaded += async (sender, e) =>
            {
                await webview21.EnsureCoreWebView2Async();
                ShowDevTools();
            };
        }

        protected override void OnClosing(System.ComponentModel.CancelEventArgs e)
        {
            base.OnClosing(e);
            if (_process != null)
            {
                _process.Refresh();
                _process.Close();
            }
        }

        private async void ShowDevTools()
        {
            if (_process != null)
            {
                _process.CloseMainWindow();
            }
            webview21.CoreWebView2.OpenDevToolsWindow();
            await Task.Delay(300);//エラーが出る場合は500ぐらいにしてください
            Process[] processes = Process.GetProcessesByName("msedgewebview2");
            foreach (Process p in processes)
            {
                if (p.MainWindowTitle.StartsWith("DevTools"))
                {
                    _process = p;
                    IntPtr windowHandle = p.MainWindowHandle;//DevToolsのHandleを取得
                    SetWindowLong(windowHandle, GWL_STYLE, (int)(GetWindowLong(windowHandle, GWL_STYLE) &
                          (0xFFFFFFFF ^ WS_SYSMENU)));
                    SetParent(p.MainWindowHandle, _panel.Handle);//DevToolsを_panelの子に設定
                    int style = GetWindowLong(_process.MainWindowHandle, GWL_STYLE);
                    style = style & ~WS_CAPTION & ~WS_THICKFRAME;
                    SetWindowLong(p.MainWindowHandle, GWL_STYLE, style);
                    ResizeEmbeddedApp();
                    break;
                }
            }
        }

        private void ResizeEmbeddedApp()
        {
            if (_process == null)
                return;
            SetWindowPos(_process.MainWindowHandle, IntPtr.Zero, -10, -35, (int)_panel.ClientSize.Width + 18, (int)_panel.ClientSize.Height + 43, SWP_NOZORDER | SWP_NOACTIVATE);
        }

        private void windowsFormsHost1_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            ResizeEmbeddedApp();
        }

        protected override Size MeasureOverride(Size availableSize)
        {
            Size size = base.MeasureOverride(availableSize);
            ResizeEmbeddedApp();
            return size;
        }
    }
}

```
これで完成です.お疲れ様でした.

## コードの解説
現在作成中