[C# / WPF] WindowChromeを利用したカスタムウィンドウで、Window.ResizeModeと連動するキャプションボタンを作ってみよう

ニアです、こんにちはー！

今回は、Window.Chromeクラスを利用したカスタムウィンドウ用のキャプションボタン（最小化・最大化・閉じるボタン）を、WindowクラスにあるResizeModeプロパティと連動させる方法を紹介していきます。

![cc-00.png](https://qiita-image-store.s3.amazonaws.com/0/78881/ec2bb076-f14a-c4de-bb21-917abc8f8b99.png)



#1. Window.ResizeModeとは
WindowクラスにあるResizeModeプロパティは、ウィンドウのサイズ変更の可否を設定するものであり、4種類のオプションがあります。

|ResizeMode|ウィンドウのサイズ変更|最小化・最大化ボタン||
|:---:|:---:|:---:|:---:|
|NoResize|不可|非表示|![cc-02.PNG](https://qiita-image-store.s3.amazonaws.com/0/78881/bf99af30-deec-76f7-3daa-959eb71b95b5.png)|
|CanMinimize|不可|表示（但し、最大化ボタンは無効）|![cc-03.PNG](https://qiita-image-store.s3.amazonaws.com/0/78881/90b781c2-2d28-6f74-0557-cf442e8f4a84.png)|
|CanResize|可|表示|![cc-04.PNG](https://qiita-image-store.s3.amazonaws.com/0/78881/6a045eda-0b87-a6a5-434c-48b66118ce04.png)|
|CanResizeWithGrip|可（右下にリサイズグリップが表示）|表示|![cc-05.PNG](https://qiita-image-store.s3.amazonaws.com/0/78881/30714902-910e-f1b2-7c64-6b6528759272.png)|


#2. Window.ResizeModeとデータバインディング
ボタンの表示・非表示は**Visibility**プロパティで、有効・無効は**IsEnabled**プロパティで設定することができます。すなわち、ResizeModeがNoResizeの時は、最小化・最大化ボタンのVisibilityを**Collapsed**に、CanMinimizeの時は、最大化ボタンのIsEnabledを**false**にすればよいのです。

ということで、**ボタンのVisibility及びIsEnabledをウィンドウのResizeModeとデータバインディング**をする方法で実装していきます。

![cc-06.png](https://qiita-image-store.s3.amazonaws.com/0/78881/f47b5e3a-2cda-621e-cdef-549c1cc81e4c.png)

##2.1. Converterクラスの作成
まず、ResizeModeの値からVisibility及びIsEnabledの値に変換するConverterクラスを作成します。

|Converterクラス名|ターゲット|変換後の値|
|:---:|:---:|---|
|**ResizeCaptionButtonVisibilityConverter**|Button.Visibility|ResizeModeがNoResizeの時は**Collapsed**、それ以外の時は**Visible**|
|**MaximizeCaptionButtonEnableConverter**|Button.IsEnabled|ResizeModeがCanMinimizeの時は**false**、それ以外の時は**true**|

```csharp:CaptionButtonConverter.cs
using System;
using System.Windows;
using System.Windows.Data;
using System.Globalization;

namespace CustomCaption {

	/// <summary>
	///		ウィンドウのリサイズモードに応じて最大化・最小化ボタンの表示・非表示を設定します。
	/// </summary>
	public sealed class ResizeCaptionButtonVisibilityConverter : IValueConverter {
		public object Convert( object value, Type targetType, object parameter, CultureInfo culture ) =>
			// ResizeModeがNoResizeの時はVisibility.Collapsedに、それ以外の時はVisibility.Visibleにします。
			value is ResizeMode && ( ResizeMode )value != ResizeMode.NoResize ? Visibility.Visible : Visibility.Collapsed;

		public object ConvertBack( object value, Type targetType, object parameter, CultureInfo culture ) => null;
	}

	/// <summary>
	///		ウィンドウのリサイズモードに応じて最大化ボタンの有効・無効を設定します。
	/// </summary>
	public sealed class MaximizeCaptionButtonEnableConverter : IValueConverter {
		public object Convert( object value, Type targetType, object parameter, CultureInfo culture ) =>
			// ResizeModeがCanMinimizeの時はfalseに、それ以外の時はtrueにします。
			value is ResizeMode && ( ResizeMode )value != ResizeMode.CanMinimize;

		public object ConvertBack( object value, Type targetType, object parameter, CultureInfo culture ) => null;
	}

	// ...

}
```

※ボタンからWindow.ResizeModeへの設定はしないので、ConvertBackは使用しません。

##2.2. 最小化・最大化ボタンにデータバインディング
次に、XAMLで最小化・最大化ボタンとWindow.ResizeModeを先ほど作成したConverterクラスを使ってバインディングします。

先ほど作成したConverterクラスをWindow.Resoursesに登録します。

```xml:Window.Resources
<!-- Windowに名前空間 xmlns:local="clr-namespace:CustomCaption" が定義されているとします。 -->
<Window.Resources>
        <!-- Converter -->
        <local:MaximizeCaptionButtonEnableConverter x:Key="MaximizeCaptionButtonEnableConverterKey"/>
        <local:ResizeCaptionButtonVisibilityConverter x:Key="ResizeCaptionButtonVisibilityConverterKey"/>
</Window.Resources>
```

最小化ボタンのVisibilityをResizeModeとバインディングし、ConverterにはResizeCaptionButtonVisibilityConverterを、ElementNameにはウィンドウの名前（MainWindow1）を設定します。

```xml:最小化ボタン
<!-- Windowの名前（x:Name）が MainWindow1 であるとします。 -->
<Button Content="0"
	    Style="{StaticResource CustomCaptionButtonKey}"
	    Visibility="{Binding ResizeMode, Converter={StaticResource ResizeCaptionButtonVisibilityConverterKey}, ElementName=MainWindow1}"
	    ToolTip="最小化"
	    Command="{x:Static SystemCommands.MinimizeWindowCommand}" />
```

最小化ボタンでは、Visibility及びResizeModeとバインディングし、ElementNameにはウィンドウの名前（MainWindow1）を設定します。Converterには、Visibility側はResizeCaptionButtonVisibilityConverterを、IsEnabled側はMaximizeCaptionButtonEnableConverterを設定します。

```xml:最大化ボタン
<!-- Windowの名前（x:Name）が MainWindow1 であるとします。 -->
<Button Content="{Binding WindowState,Converter={StaticResource MaximizeCaptionButtonContentConverterKey}, ElementName=MainWindow1}"
	    Style="{StaticResource CustomCaptionButtonKey}"
	    IsEnabled="{Binding ResizeMode, Converter={StaticResource MaximizeCaptionButtonEnableConverterKey}, ElementName=MainWindow1}"
	    Visibility="{Binding ResizeMode, Converter={StaticResource ResizeCaptionButtonVisibilityConverterKey}, ElementName=MainWindow1}"
	    ToolTip="{Binding WindowState, Converter={StaticResource MaximizeCaptionButtonTooltipConverter}, ElementName=MainWindow1}"
	    Command="{x:Static SystemCommands.MaximizeWindowCommand}"/>
```

これで、Window.ResizeModeと連動するキャプションボタンの出来上がりです。


|Window.ResizeMode|通常のウィンドウ|カスタムウィンドウ|
|:---:|:---:|:---:|
|NoResize|![cc-02.PNG](https://qiita-image-store.s3.amazonaws.com/0/78881/bf99af30-deec-76f7-3daa-959eb71b95b5.png)|![cc-07.PNG](https://qiita-image-store.s3.amazonaws.com/0/78881/c16659cf-4fc4-3170-4d89-bb1890258712.png)|
|CanMinimize|![cc-03.PNG](https://qiita-image-store.s3.amazonaws.com/0/78881/90b781c2-2d28-6f74-0557-cf442e8f4a84.png)|![cc-08.PNG](https://qiita-image-store.s3.amazonaws.com/0/78881/e4da5e33-ab9f-18c5-dbba-0c650bb0e01d.png)|
|CanResize|![cc-04.PNG](https://qiita-image-store.s3.amazonaws.com/0/78881/6a045eda-0b87-a6a5-434c-48b66118ce04.png)|![cc-09.PNG](https://qiita-image-store.s3.amazonaws.com/0/78881/1b6b86af-c372-bf24-a753-436d47855cb7.png)|
|CanResizeWithGrip|![cc-05.PNG](https://qiita-image-store.s3.amazonaws.com/0/78881/30714902-910e-f1b2-7c64-6b6528759272.png)|![cc-10.PNG](https://qiita-image-store.s3.amazonaws.com/0/78881/94f76052-1704-2091-8bca-84913dafd1ec.png)|

#3. おわりに
今回作成したカスタムウィンドウのプログラムは、Gistにアップしておきます。
https://gist.github.com/Nia-TN1012/e9207e7fe02d046b3d4d

そのプログラムでは、今回扱った「キャプションボタンをWindow.ResizeModeと連動させる方法」の他に、最大化ボタンの表示文字列とツールチップをウィンドウの最大化状態と連動させたり、タイトルバーの色をウィンドウのアクティブ状態と連動させたりしています。いずれも、Converterクラスを使ったデータバインディングで実装することができます。

それでは、See you next time!


#追記（2016/03/25）
Gistにアップしていたプログラムですが、キャプションボタンのスタイルを設定しているリソースディクショナリーのファイルをアップし忘れていましたので、追加しました。
ご不便をおかけしたことをお詫び申し上げます。