【WPF】既存のコントロールをベースにオリジナルのコントロールを作ってみる

# 実はめちゃくちゃ簡単にできる
まずは適当にWPFプロジェクトを作成します。
今回はTextBoxを例にして進めていくので、プロジェクト名は「ExtendTextBox」としました。英語的に正しいかは知りません。

親の顔より見たテンプレートをVS君が生成してくれるので……

```C#:MainWindow.xaml.cs
namespace ExtendTextBox {
    /// <summary>
    /// MainWindow.xaml の相互作用ロジック
    /// </summary>
    public partial class MainWindow : Window {
        public MainWindow() {
            InitializeComponent();
        }
    }
}
```

既存コントロールを継承したクラスを追記してあげます。
```C#:MainWindow.xaml.cs
namespace ExtendTextBox {
    /// <summary>
    /// MainWindow.xaml の相互作用ロジック
    /// </summary>
    public partial class MainWindow : Window {
        public MainWindow() {
            InitializeComponent();
        }
    }
    
    //追記ここから
    public class SuperUltraHyperCoooooooolTextBox : TextBox {

    }
    //追記ここまで
}

```
とってもクールなクラス名です。余談になりますが、[C#のクラス名は512文字が上限らしいです。](https://learn.microsoft.com/ja-jp/dotnet/csharp/misc/cs0645?redirectedfrom=MSDN) 



一旦ビルドしてからXAMLの編集画面に行くと……
![2022-10-15_06h03_13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/164419/79bf1600-2ba0-8438-ebb0-76f6197f10ee.png)

い、いる～～～～～！！！！
![2022-10-15_06h10_31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/164419/0342c1b4-d69d-4c44-f037-73c57ae5312e.png)

ツールボックスにいるので、もちろんD&Dで配置できます。
![2022-10-15_06h13_20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/164419/6fac2c42-3597-3f0f-b180-29bc726fe0aa.png)

TextBoxを継承していますから、当然TextBoxの機能は全て使えます。
あとはお好みでメソッドとか追加していけばスーパーでウルトラでハイパークールなTextBoxの出来上がりです。

# 実装例
こんなTextBoxを作ってみます。
- XAML上でText属性とは別でデフォルト値を設定可能
- 自身が持つpublicなメソッドにより、デフォルト値で表示内容(this.Text)を上書きできる

TextBoxには存在しない、DefaultTextプロパティを追加します。
このあたりの詳細はDependencyPropertyについてググってみると色々出て来ると思いますが、記法だけわかればそれでいいという人はこのまま読み進めてしまって大丈夫です。

**【XAML】**
```XML:MainWindow.xaml
<Window x:Class="ExtendTextBox.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:ExtendTextBox"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <local:SuperUltraHyperCoooooooolTextBox DefaultText="でふぉるとてきすと💪💪💪💪" x:Name="suhc_TextBox" HorizontalAlignment="Left" Margin="69,120,0,0" TextWrapping="Wrap" Text="SuperUltraHyperCoooooooolTextBox" VerticalAlignment="Top" Width="258" Height="43"/>
        <Button x:Name="button" Content="Button" HorizontalAlignment="Left" Margin="380,116,0,0" VerticalAlignment="Top" Height="52" Width="114" Click="button_Click"/>
    </Grid>
</Window>

```

**【C#】**
```C#:MainWindow.xaml.cs
using System.Windows;
using System.Windows.Controls;

namespace ExtendTextBox {
    /// <summary>
    /// MainWindow.xaml の相互作用ロジック
    /// </summary>
    public partial class MainWindow : Window {

        public MainWindow() {
            InitializeComponent();
        }

        //ボタンクリックで、スーパーでウルトラでハイパークールなTextBoxが持つメソッドを呼び出す
        private void button_Click(object sender, RoutedEventArgs e) {
            suhc_TextBox.setText_default();
        }
    }

    public class SuperUltraHyperCoooooooolTextBox : TextBox {

        #region XAMLで使える属性を追加
        //デフォルト値用の属性を追加      
        static DependencyProperty property = DependencyProperty.Register("DefaultText", typeof(string), typeof(SuperUltraHyperCoooooooolTextBox));
        public static readonly DependencyProperty DefaultTextProperty = property;
        #endregion

        #region 追加した属性と対応する公開プロパティ
        public string DefaultText {
            get { return (string)GetValue(DefaultTextProperty); }
            set { SetValue(DefaultTextProperty, value); }
        }
        #endregion

        //表示内容をデフォルト値で上書き
        public void setText_default() {
            this.Text = this.DefaultText; 
        }
    }
}
```

![2022-10-15_07h08_21.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/164419/c06152d1-dc97-519b-eb07-9c3c7d1f96b3.gif)

チェックボックスやラジオボタンの状態に応じて固定値と自由入力を切り替えたい場合とか重宝しそうです。
バックアップ用の変数を持たせておけば復元機能も実装できますね。
![2022-10-15_07h40_39.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/164419/55e6442e-973d-79a1-9460-101e37014a6b.gif)


# おわり
以上です。お疲れ様でした。