(XAML#14)「MaxLength プロパティを持つ ComboBox を作る」
昨日は ComboBox の IsEditable が True の場合に、入力できる文字列長を制限する方法について紹介しました。本来、このような機能はプロパティとして用意しておきたいものです。
「プロパティを追加する」といった、クラスの機能を拡張することはスタイル(Style)のような機能では対応できないため、新しいクラスを作る必要があります。たとえば、次のように ComboBox クラスを継承した新しいクラスを作ることができます。
[コード]
public class ComboBoxEx : ComboBox
{
public int MaxLength { get; set; }
public ComboBoxPlus()
{
// Loaded イベントに MaxLength を設定する匿名メソッドを割り当てる
Loaded += delegate(object sender, RoutedEventArgs e)
{
if (Template != null)
{
var tbox = Template.FindName("PART_EditableTextBox", this) as TextBox;
if (tbox != null)
tbox.MaxLength = MaxLength;
}
};
}
}
こうしておくと、XAML で、このクラスをコントロールとして利用できるようになります。以下は、同一プロジェクトで、このクラスを定義した場合の記述例です。冒頭で local 名前空間を定義していることに注意してください。
[XAML]
<Window x:Class="WpfApp1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApp1"
Title="MainWindow" Height="325" Width="525">
<StackPanel>
<local:ComboBoxEx IsEditable="True" MaxLength="7">
<ComboBoxItem Content="1"/>
<ComboBoxItem Content="2"/>
<ComboBoxItem Content="3"/>
</local:ComboBoxEx>
...
ここまでで十分な場合も少なくありませんが、ここで定義した MaxLength プロパティは、WPF コントロールの他のプロパティと大きな違いがあります。ここで定義したものが単純なプロパティであるのに対し、WPF コントロールのプロパティのほとんどは「依存関係プロパティ」だということです。依存関係プロパティは、データバインディングしたり、スタイルで初期値を設定しておけるプロパティのことです。上記のままではスタイルを使って、Setter で MaxLength の値を設定することはできません。
依存関係プロパティを定義するのは、少し手間がかかります。次のコードを見てください。
[コード]
public class ComboBoxEx : ComboBox
{
[Bindable(true, BindingDirection.OneWay)]
public int MaxLength
{
get {
return (int)GetValue(MaxLengthProperty);
}
set {
SetValue(MaxLengthProperty, value);
}
}
public static readonly DependencyProperty MaxLengthProperty =
DependencyProperty.Register("MaxLength", typeof(int),
typeof(ComboBoxEx),
new FrameworkPropertyMetadata(0, FrameworkPropertyMetadataOptions.None,
new PropertyChangedCallback(OnMaxLengthChanged)));
private static void SetMaxLength(ComboBoxEx cbox)
{
if (cbox != null && cbox.Template != null)
{
var tbox = cbox.Template.FindName("PART_EditableTextBox", cbox) as TextBox;
if (tbox != null)
tbox.MaxLength = cbox.MaxLength;
}
}
private static void OnMaxLengthChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
SetMaxLength(obj as ComboBoxEx);
}
protected override void OnInitialized(EventArgs e)
{
base.OnInitialized(e);
Loaded += delegate(object sender, RoutedEventArgs args)
{
SetMaxLength(this);
};
}
}
クラス定義に DependencyProperty 型の MaxLengthProperty という静的メンバーを定義しているのがポイントです。MaxLength は、GetValue または SetValue メソッドに、このオブジェクトを渡しているだけです。このようなケースでは、あまり使われないかもしれませんが、データバインディングを使って実行中に MaxLength の値が変更されることも考慮して、値が変更されるたびに編集用のテキストボックスの MaxLength を更新しています。Visual Studio で「WPF カスタムコントロールライブラリ」などを作成すれば、こうしたカスタムコントロールを持つ独自のライブラリを作ることもできます。