Anchor によるコントロールの位置・サイズ変化のバリエーション

# はじめに

Anchor プロパティに設定できる値（Top、Bottom、Left、Right）の組み合わせは 16 パターンあります。

|     | Top | Bottom | Left | Right | 
| :-: | :-: | :----: | :--: | :---: | 
| 1   |     |        |      |       | 
| 2   | ✓  |        |      |       | 
| 3   |     | ✓     |      |       | 
| 4   | ✓  | ✓     |      |       | 
| 5   |     |        | ✓   |       | 
| 6   | ✓  |        | ✓   |       | 
| 7   |     | ✓     | ✓   |       | 
| 8   | ✓  | ✓     | ✓   |       | 
| 9   |     |        |      | ✓    | 
| 10  | ✓  |        |      | ✓    | 
| 11  |     | ✓     |      | ✓    | 
| 12  | ✓  | ✓     |      | ✓    | 
| 13  |     |        | ✓   | ✓    | 
| 14  | ✓  |        | ✓   | ✓    | 
| 15  |     | ✓     | ✓   | ✓    | 
| 16  | ✓  | ✓     | ✓   | ✓    | 

パターン毎にコントロールの相対位置（Anchor は親コントロールに対して作用するため、親に対する位置が変わる。という意味です）とサイズが変わりますが、考えるべきパターンを見落としたり、パターン毎の動きがぱっと思い浮かばないことがよくあります。そこで、このパターンのすべての挙動をまとめました。

# Anchor の挙動

Windows フォーム（`Form1`）に置いたボタン（`button 1`）に対して Anchor を設定していきます。

![00_default.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14838/a11cfc3f-231a-8840-c995-37e80f55cf01.png)

このようなサンプルアプリを用意しました。ウィンドウサイズを変更（`300 x 200` → `450 x 400`）した際、Anchor を設定したボタンの「位置とサイズの何が変わったのか？」を分かりやすくするため、変化したプロパティ（`[X]`　`[Y]`　`[W(idth)]`　`[H(eight)]`）を <font color="Red">赤く</font> 表示します。

## 1. None

![01_None.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14838/94b9da62-3429-a669-8f01-c50235f720a6.png)

## 2. Top

![02_Top.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14838/bc96994f-0b3a-ac59-70f0-f39e8831ea4d.png)

## 3. Bottom

![03_Bottom.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14838/ad555acf-125a-876f-b959-f7959b328822.png)

## 4. Top, Bottom

![04_Top, Bottom.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14838/6c833d63-c352-481a-acfa-481e9f8294ae.png)

## 5. Left

![05_Left.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14838/c59ddcfe-80d7-074c-a9bf-3c21dab095d7.png)

## 6. Top, Left

![06_Top, Left.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14838/22539aeb-44d6-2236-d6f9-243df617fdfe.png)

## 7. Bottom, Left

![07_Bottom, Left.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14838/c4592c1b-7d40-ee2b-89b2-3a174f090cad.png)

## 8. Top, Bottom, Left

![08_Top, Bottom, Left.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14838/51338c9a-8c91-0d80-382a-48cd1b2f7709.png)

## 9. Right

![09_Right.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14838/c844385e-a78e-ca61-3bc7-675c2c035330.png)

## 10. Top, Right

![10_Top, Right.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14838/4fc5b8d7-c3e7-b2d4-7b74-d5c1d799aee6.png)

## 11. Bottom, Right

![11_Bottom, Right.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14838/bdac6fdc-05bb-6f23-8fcf-634dd303bfd1.png)

## 12. Top, Bottom, Right

![12_Top, Bottom, Right.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14838/a9f1e3a7-7510-2798-266c-3a234e72bbac.png)

## 13. Left, Right

![13_Left, Right.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14838/8c47edad-101b-db98-bedd-bc6b60396261.png)

## 14. Top, Left, Right

![14_Top, Left, Right.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14838/45b59898-b9f4-0e42-3883-80ed22e0b13d.png)

## 15. Bottom, Left, Right

![15_Bottom, Left, Right.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14838/5420445e-ae78-2e84-5da2-f5472b8215ee.png)

## 16. Top, Bottom, Left, Right

![16_Top, Bottom, Left, Right.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14838/2f089c3e-5f41-591b-6843-46ed06a789d2.png)

## サンプルアプリのソースコード

サンプルアプリは .NET 8 環境の Windows フォームアプリケーションとして作成しました。

```Form1.cs
namespace AnchorSample;

public partial class Form1 : Form
{
	private const int X = 180;
	private const int Y = 110;
	private const int W = 90;
	private const int H = 40;

	public Form1()
	{
		InitializeComponent();

		DoubleClick += (_, _) =>
		{
			Size = Width == 300
				? new Size(450, 400)
				: new Size(300, 200);

			ShowProperties();
		};

		ShowProperties();
	}

	private void ShowProperties()
	{
		labelA.Text = @"[Anchor] " + button1.Anchor;

		labelX.Text = $@"[X] {button1.Location.X}";
		labelY.Text = $@"[Y] {button1.Location.Y}";
		labelW.Text = $@"[W] {button1.Size.Width}";
		labelH.Text = $@"[H] {button1.Size.Height}";

		labelX.ForeColor = button1.Location.X == X
			? Color.Black
			: Color.Red;
		labelY.ForeColor = button1.Location.Y == Y
			? Color.Black
			: Color.Red;
		labelW.ForeColor = button1.Size.Width == W
			? Color.Black
			: Color.Red;
		labelH.ForeColor = button1.Size.Height == H
			? Color.Black
			: Color.Red;
	}
}
```

# Anchor 設定の階層

次に、コントロールに親子関係がある場合の Anchor 設定について考えてみます。

![00_default.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14838/9e5683bf-221a-9a0f-bc36-dbee7574fafb.png)

例えば、ウィンドウの下部にフッター領域を作り、ボタンを３つ配置する場合、フッターをウィンドウ下部に固定しようとすると、`None` と `Top, Left` ではうまくいきません。

![01_None.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14838/7368d14a-6272-cbda-48d2-098c7ccf28ff.png)

![02_Top, Left.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14838/52d661df-e1ba-f479-66e8-6d8b1a6a9a5f.png)

`Bottom, Left, Right` とすると意図した配置となりました。

![A2_None-None-None.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14838/cbd971d4-02b9-a831-05ea-3f37ee18cb6a.png)

次に、ボタンを「均等に並べたい」とします。ボタンの幅を変えずにボタン間の間隔を伸ばす対応であれば、下記のように設定することで実現できます。

![A3_Left-None-Right.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14838/5339675b-1d96-296f-f498-2dc845f8bc71.png)

しかしながら、ボタンの幅も伸ばしたい！と考え、ボタンの Anchor を `Left, Right` に設定すると、下記のようになってしまいます。

![C_LeftRight-LeftRight-LeftRight.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14838/f2428d77-f79d-0377-0430-b9916ecb2dd0.png)

Anchor 設定の階層を対象とすると、ボタンのどの部分の余白を伸ばすか？だったり、ボタンの幅をどのようなルールで伸ばすか？だったり、考えなければならないことは意外と多いです。また実装の際も、他のコントロールと組み合わせたり、計算して幅を求めたり、という対応が必要になります。

# おわりに

Anchor 設定はすぐに「あれ？」となってしまうので、ことあるごとに見直そうと思います。また、階層構造を対象とする場合は、どのような対応とするか？をよくよく考える必要があります。