オルタナティブ・ブログ > IT's my business >

IT業界のコメントマニアが始めるブログ。いつまで続くのか?

(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 カスタムコントロールライブラリ」などを作成すれば、こうしたカスタムコントロールを持つ独自のライブラリを作ることもできます。

Xaml14

Comment(0)