[C#/WPF] ユーザーコントロールのひな型

**もくじ**
⇒https://qiita.com/tera1707/items/4fda73d86eded283ec4f

# やりたいこと
とりあえず、自前でユーザーコントロールを作れるようにひな型と、作り方の流れを自分の中に持っておきたい。

# 前提
**.NET Framework 4.7.2**を使用して下記を作成した。

# ひな型

## お試しアプリのざっくり仕様

### 画面

画面は下図のようにする。赤で囲ったところがユーザーコントロール。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352601/c5f4344b-13b3-47f7-c702-52fc650bca68.png)


### ひな型サンプルの動作

- ユーザーコントロール側でやっていること
    - 「UserControl側で数字を＋＋」ボタンを押すと、ユーザーコントロールの中に持っているプロパティの値を＋＋する。
    - プロパティの値が変化時、値をテキストボックスに表示する。
- 画面側でやっていること
    - 「画面側で数字を＋＋」を押すと、ユーザーコントロールにバインドしている画面側のプロパティの数字を＋＋する。
    - その際、ユーザーコントロール側に＋＋したことが伝われば、ユーザーコントロール側のプロパティの変化により、テキストボックスの数字が更新される。
    - 画面側のプロパティは、ユーザーコントロールに「TwoWay」でバインドしている。

## ユーザーコントロール側のひな型サンプル

見た目(xaml)

```SimpleUserControl.xaml
<UserControl x:Class="WpfApp1.SimpleUserControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             mc:Ignorable="d" 
             x:Name="root">
    <Grid Width="350" Height="50">
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="100"/>
            <ColumnDefinition Width="250"/>
        </Grid.ColumnDefinitions>
        
        <!-- タイトル行 -->
        <TextBox Grid.Row="0" Grid.Column="0" Text="value" />

        <!-- 数字を表示する行 -->
        <TextBox Grid.Row="1" Grid.Column="0" Text="{Binding MyDoubleValue1, ElementName=root}" />
        
        <!-- 数字を＋＋するボタン -->
        <Button Grid.Row="0" Grid.Column="3" Grid.RowSpan="2" Content="UserControl側で数字を＋＋" Click="Button_Click"/>
    </Grid>
</UserControl>
```

コードビハインド

```SimpleUserControl.xaml.cs
using System;
using System.Windows;
using System.Windows.Controls;

namespace WpfApp1
{
    public partial class SimpleUserControl : UserControl
    {
        public double MyDoubleValue1
        {
            get => (double)GetValue(MyDoubleValue1Property);
            set { SetValue(MyDoubleValue1Property, value); }
        }
        public static readonly DependencyProperty MyDoubleValue1Property = DependencyProperty.Register(nameof(MyDoubleValue1), typeof(double), typeof(SimpleUserControl), new PropertyMetadata(0.0));
        
        public SimpleUserControl()
        {
            InitializeComponent();
        }
        private void Button_Click(object sender, RoutedEventArgs e)
        {
            MyDoubleValue1 += 1.0;
        }
    }
}
```

## 画面側のひな型サンプル

見た目(xaml)

```MainWindow.xaml
<Window x:Class="WpfApp1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp1"
        mc:Ignorable="d"
        Title="MainWindow" Height="200" Width="550"
        x:Name="root">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="2*"/>
            <ColumnDefinition Width="5*"/>
        </Grid.ColumnDefinitions>

        <Button Grid.Column="0" Content="画面側で数字を＋＋" Click="Button_Click"/>

        <TextBlock Grid.Column="1"  Text="UseControlのProperty" HorizontalAlignment="Center" VerticalAlignment="Center"/>
        <local:SimpleUserControl Grid.Column="2" Grid.Row="1" 
                                 MyDoubleValue1="{Binding DoubleValue1, ElementName=root, Mode=TwoWay}"/>
    </Grid>
</Window>

```

コードビハインド

```MainWindow.xaml.cs
using System;
using System.ComponentModel;
using System.Windows;

namespace WpfApp1
{
    public partial class MainWindow : Window, INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        protected void OnPropertyChanged(string propertyName) => this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));

        public double DoubleValue1
        {
            get { return _doubleValue1; }
            set { Console.WriteLine("* MainWindow DoubleValue1 = {0} ", value); _doubleValue1 = value; OnPropertyChanged(nameof(DoubleValue1)); }
        }
        private double _doubleValue1 = 0.0;

        public MainWindow()
        {
            InitializeComponent();
        }
        private void Button_Click(object sender, RoutedEventArgs e)
        {
            DoubleValue1++;
        }
    }
}
```

# ひな型の説明

#### 全体のイメージ

**基本的に**、下記のようなイメージで処理を書けばいい。

- 画面側でプロパティの値を変更すると、その変更が依存関係プロパティを**通じて**ユーザーコントロールのプロパティに伝わる。
- ユーザーコントロール側でプロパティの値を変更すると、その変更が依存関係プロパティを**通じて**画面のプロパティに伝わる。
- ただし、これはバインディングのModeが`TwoWay`の場合のみ。`OneWayToSource`や`OneWay`だと、片道一方通行になるので注意。(別途メモ書きまとめたい)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352601/0e7b1670-ea25-eef8-2e91-e604b0da8ea0.png)

### ユーザーコントロール側

#### 依存関係プロパティの作成

依存関係プロパティをつくる。

- ユーザーコントロールが、使う側のプロパティとバインドするためには、**依存関係プロパティ**というものを持つ必要がある。
- コードビハインドで`DependencyProperty.Register`を使って、自分はこういうものを持ってますよ、というのを文字通りRegister(登録)する。
- 依存関係プロパティは、外部(ここでは画面側)に、「自分はこんなプロパティをバインドできますよ」とお知らせするためのもの。(門番的な)
- 外部(画面)側は、そのお知らせされたプロパティに、自分のプロパティをバインドして、連動させて使う。

具体的には下記の部分。

```cs
public static readonly DependencyProperty MyDoubleValue1Property = DependencyProperty.Register(nameof(MyDoubleValue1), typeof(double), typeof(SimpleUserControl), new PropertyMetadata(0.0));
```

`DependencyProperty.Register()`に色々引数を指定して、`DependencyProperty`を作る。
引数は下記のようにする。

1. 名前
- プロパティに付けたい型を指定
- ユーザーコントロールの型を指定
- ユーザーコントロールの情報を指定するためのPropertyMetaDataを載せる  
(引数1個のPropertyMetaDataの引数で、初期値を指定できる。ほかにもいろいろ指定できるが、簡単のため今はこれだけにする)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352601/92679e4a-ef8d-e78b-2324-2b38914fe1c9.png)

#### 依存関係プロパティと対になるプロパティ作成

依存関係プロパティには、対になるプロパティが必要。それが下記の部分。

```cs
public double MyDoubleValue1
{
    get => (double)GetValue(MyDoubleValue1Property);
    set { SetValue(MyDoubleValue1Property, value); }
}
```

- getter
    - 依存関係プロパティの値をとってきてくれる`GetValue`メソッドを呼ぶ。
    - `GetValue`メソッドの引数は、先ほど作成した`DependencyProperty`を渡す。
    - `GetValue`メソッドは`object`を返すので、`DependencyProperty.Register`の第二引数で指定した型でキャストする。
- setter
    - 依存関係プロパティの値をセットしてくれる`SetValue`メソッドを呼ぶ。
    - `SetValue`メソッドの第一引数は、先ほど作成した`DependencyProperty`を渡す。
    - `SetValue`メソッドの第二引数は、valueを渡す。

このプロパティの中では、`GetValue`、`SetValue`を呼ぶこと以外には何もしない方がよい。
⇒[こちら参照](http://bbs.wankuma.com/index.cgi?mode=al2&namber=36371&KLOG=63)（CLRプロパティでは、setvalue,getvalue以外のことをしないほうがよい）

#### 内部処理を作成

必要に応じて、ユーザーコントロール内部で処理を行う。
上のひな型では、下記の部分で、プロパティの値を＋＋している。

```cs
private void Button_Click(object sender, RoutedEventArgs e)
{
    MyDoubleValue1 += 1.0;
}
```

この例だと、プロパティは画面に下記のようにバインドしている。

```xaml
<!-- 数字を表示する行 -->
<TextBox Grid.Row="1" Grid.Column="0" Text="{Binding MyDoubleValue1, ElementName=root}" />
```

これにより、＋＋した値は画面に表示される。
※特にInotifyPropertyChangedを継承して実装したりしなくても、プロパティの値が変化したら、それが画面に反映される。

### 画面側(ユーザーコントロールを使う側)

画面側では、UserControlではない普通のコントロール(例えばTextBlock)にプロパティをバインドする要領で、バインドして使えばいい。

コードビハインド(やビューモデル)でプロパティを作成して、

```cs
public double DoubleValue1
{
    get { return _doubleValue1; }
    set { Console.WriteLine("* MainWindow DoubleValue1 = {0} ", value); _doubleValue1 = value; OnPropertyChanged(nameof(DoubleValue1)); }
}
private double _doubleValue1 = 0.0;
```

画面(xaml)でそれをバインドする。

```xaml
<local:SimpleUserControl Grid.Column="2" Grid.Row="1" MyDoubleValue1="{Binding DoubleValue1, ElementName=root, Mode=TwoWay}"/>
```

# クセが強い

ユーザーコントロール、触ってみた感じ、扱い方のクセが強い気がする。
ひな型のように、基本的なことしかしないとそれほどでもないが、「プロパティの値が変わった時に〇〇したい」とか「プロパティの値が100を超えたら100に丸めたい」とかをやろうと思うと、かなりハマった。
丸めるとか、変化検出とかは、それ用の仕組みがあるのだが、そいつらがまたよくわからない動きをする。(個人の感覚)


# コード置き場
https://github.com/tera1707/WPF-/tree/master/037_BindingWithUserControlBasic