Visual Studio Do-It-Yourself シリーズ
第 9 回 ユーザー コントロール
Visual Studio Do-It-Yourself 第 9 回 ユーザー コントロール 第 6 回のリソースから第 8 回のテンプレートで、さまざまな方法でコントロールをカスタマズできるこ とを学びました。今回のテーマであるユーザー コントロールは、既存の一つのコントロールをカスタマ ズするのではなく、複数のコントロールを組み合わせて一つのコントロールのように使うものです。 ここでは、次のことを学習します。 ユーザー コントロールの作成 ユーザー コントロールの利用
■ユーザー コントロール
第 8 回のテンプレートで解説したとおり、コントロール テンプレートは、その構成要素として複数のコン トロールを組み合わせることができます。つまり、複数のコントロールを組み合わせることだけがユーザ ー コントロールを使う理由ではありません。 コントロール テンプレートを使う場合には「コントロールのプロパテゖやベントなどの機能は、対象と なるコントロールのものをそのまま使う」のに対し、 ユーザー コントロールを作成する場合は、 「UserControl クラスから派生したクラスで必要なプロパテゖや機能を実装する」という違いがあります。 コントロール テンプレートは、Button ならボタンとして、CheckBox ならチェックボックスとしての機 能を変えるわけではありません。ユーザー コントロールは、既存のコントロールでは機能が足りない場合、 あるいは新しい機能を持つコントロールを実装したい場合に使います。 また、コントロール テンプレートが、ウゖンドウ レベルあるいはゕプリケーション レベルでの共通化を 図るものであるのに対し、ユーザー コントロールは、プロジェクトをまたがって利用できる、コントロー ル ラブラリとして実装することができます。 なお、独自の機能を持つコントロールを実装するには、既存のコントロール クラスを継承して作成するカ スタム コントロールという方法もあります。ユーザー コントロールは、カスタム コントロールに比べて WPF デザナーを使って容易に開発できる利点があります。■ユーザー コントロールの作成
まず、新しい WPF ゕプリケーションを作成します。ただし、ここで作成されたゕプリケーションは、後で ユーザー コントロールを使うためのものとなります。また、ユーザー コントロールは、プロジェクトの 一部として作成することもできますが、ここでは独立したラブラリとして作成します。 ソリューション エクスプローラーで、ソリューション名を右クリックし、[追加]-[新しいプロジェクト] で表示される「新しいプロジェクトの追加」ダゕログで、「WPF ユーザー コントロール ラブラリ」 を選びます。名前は「MyControlLibrary」としておきます。 ソリューション エクスプローラーには、UserControl1.xaml というモジュールを含む MyControlLibrary が 追 加 さ れ ま す 。 こ こ で 、 UserControl1.xaml を 右 ク リ ッ ク し て [ 名 前 の 変 更 ] を 選 び 、 「NumericBox.xaml」に変更します。また、コード ビハンド フゔルの UserControl1 というクラス名を右クリックして、[リフゔクター] -[名前の変更] を選び、「NumericBox」への変更を適用します。 なお、ラブラリ プロジェクトから UserControl1.xaml を削除して、新しい項目として「WPF ユーザー コントロール」を追加し、名前を NumericBox にすることもできます。 これで、新しいユーザー コントロールを作成する準備ができました。NumericBox.xaml をダブルクリッ クして、WPF デザナーを開くと次のようになっています。
これまでは、もっとも外側はウゖンドウ(Window)でしたが、ユーザー コントロールの場合には UserControl 型が使われています。このため、タトルがなく、大きさはデザナーのための指定(d:) があるだけです(コントロールの実際の大きさは配置されるときに決まります)。ここでは、少し高さを低 くして、横長にしておきます(XAML エデゖターで d:DesignHeight が 100 になる程度)。 コントロールの中央をクリックすると、配置されている Grid が選択されます。第 2 回のレゕウトで、 Grid のレゕウト方法について学びました。ここでは、上辺のやや右寄りの部分をクリックして分割し、 さらに左辺の中央部分で分割します。 今回は、プロパテゖ ウゖンドウを使って、分割の設定を変更します。まず、プロパテゖ ウゖンドウの ColumnDefinitions の右側で [...] ボタンをクリックしてコレクション エデゖターを開きます。ここで、 最初の ColumnDefinition は Width を「*」に、2 つめの ColumnDefinition は Width を「20」に設定し ます。
これで、右側の列は常に 20 ピクセル分、左側の列は残りの幅を占有するようになります。
続いて、プロパテゖ ウゖンドウの RowDefinitions の右側で [...] ボタンをクリックします。ここでは両 方の RowDefinition の Height を「*」にします。これで行の高さは常に等分割されます。
次に、TextBox を左側の列で上下にわたるようにし(これで 2 つの領域をまたがることが示されます)、さ らに 2 つの Button を右上と右下に配置します(下図)。 [Shift]+クリックを使って、この 3 つのコントロールをまとめて選択し、プロパテゖ ウゖンドウで Height、 HorizontalAlignment、Margin、VerticalAlignment、Width の値をリセットします(それぞれのプロパ テゖ名を右クリックして [値のリセット] を選びます)。 さらに、それぞれのコントロールについて、次のようにプロパテゖの値を設定します。 コントロール プロパティ 値 textBox1 Text 0 VerticalContentAlignment Center button1 Content ▲ Button2 Content ▼ この結果、WPF デザナーは次の図のようになります。 textBox1 の文字列をわかりやすくするためフォントサズ(FontSize プロパテゖ)を大きくすることも できます(ただし、コントロールを配置したときに、配置場所のフォントサズを反映しなくなります)。 続いて、コード ビハンド(NumericBox.xaml.cs)で、このユーザー コントロールの機能を割り当て ます。ここには次のように記述してください。 textBox1(TextBox) button1(Button) Bu button2(Button)
public partial class NumericBox : UserControl
{
public int Value
{
get
{
int n;
if (int.TryParse(textBox1.Text, out n))
return n;
return 0;
}
set
{
textBox1.Text = value.ToString();
}
}
public NumericBox()
{
InitializeComponent();
}
}
ここでは、整数値をあらわす Value プロパテゖを定義しています。Value の値は、設定するときはテキス トボックスの文字列として渡され、取得するときはテキストボックスに入力されている文字列を整数値に 変換して返します。 さらに 2 つのボタンについて、次のように Click ベントのベント ハンドラーを割り当てます。private void button1_Click(object sender, RoutedEventArgs e)
{
Value = Value + 1;
}
private void button2_Click(object sender, RoutedEventArgs e)
{
Value = Value - 1;
}
これで、上下のボタンを使える数値入力用のコントロールが完成しました。[Shift]-[F6] などを押して、 このプロジェクトをビルドしておいてください。
■ユーザー コントロールの利用
最初に作成した WPF ゕプリケーション プロジェクトに戻ります。ソリューション エクスプローラーで プロジェクト名を右クリックし、[参照の追加] を選びます。ここで、「プロジェクト」タブを選ぶと、作 成した MyControlLibrary ラブラリが表示されています(下図)。 [OK] ボタンを押すと、このプロジェクトからラブラリに登録されているコントロールを使えるように なります。ただし、WPF デザナーで編集するときには、まだツールボックスに追加したコントロールは 表示されていません。このため、ツールボックスを右クリックして、[ゕテムの選択] を選びます。する と、ゕテムの選択画面になるので、[WPF コンポーネント] タブを選び、[参照] ボタンを押します。こ こで、さきほどビルドしておいたラブラリ(.dll)を指定します。すると、ツールボックス ゕテムの選択で、NumericBox コントロールが表示されてチェックされるので、 [OK] ボタンを押します。
これで、他のコントロールと同じように、ツールボックスからドラッグ ゕンド ドロップして、コントロ ールを配置できるようになります。テスト用に NumericBox と Button コントロールを一つずつ配置し、 Button の Click ベントに次のようなベント ハンドラーを割り当てます。
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void button1_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show("Value = " + numericBox1.Value.ToString());
}
}
プログラムを実行した様子を次に示します。 なお、ここで作成したコントロールは、プログラムで Value プロパテゖを扱うことはできますが、第 5 回 で学んだデータ バンデゖングの対象として使うことはできません。データ バンデゖングとして使え るプロパテゖは、「依存プロパテゖ」でなければならないためです。●依存プロパティを作成する(参考)
依存プロパテゖの実装は、単純なプロパテゖの実装よりも複雑になります。前述の NumericBox コントロ ールの Value プロパテゖを、依存プロパテゖとして実装する例を以下に示します。
public partial class NumericBox : UserControl
{
public int Value
{
get { return (int)GetValue(ValueProperty); }
set { SetValue(ValueProperty, value); }
}
static void valueChangedCallBack(DependencyObject property,
DependencyPropertyChangedEventArgs args)
{
NumericBox searchTextBox = (NumericBox)property;
searchTextBox.textBox1.Text = args.NewValue.ToString();
}
public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register("Value", typeof(int), typeof(NumericBox),
new FrameworkPropertyMetadata(0,
new PropertyChangedCallback(valueChangedCallBack)));
public NumericBox()
{
InitializeComponent();
textBox1.LostFocus += new RoutedEventHandler(textBox1_LostFocus);
}
void textBox1_LostFocus(object sender, RoutedEventArgs e)
{
int n;
if (int.TryParse(textBox1.Text, out n))
Value = n;
else
Value = 0;
}
……button1_Click と button2_Click
}
ここで ValueProperty という依存プロパテゖを静的に定義していることに注意してください。ここに用意 されたデータに基づいて、コントロールの外部から間接的にプロパテゖを変更できる仕組みが提供されて います。また、ここでは Value プロパテゖと TextBox コントロールの Text プロパテゖが直接連動しない ため、TextBox からフォーカスが失われたときに、Value プロパテゖを更新しています。TextBox にフォ ーカスがある間(値を入力している間)は、Value プロパテゖが変化しない(バンデゖング対象に影響 しない)ことに注意してください。
■まとめ
最初に述べたとおり、既存のコントロールを組み合わせてユーザー コントロールを作成する以外にも、既 存のコントロールを拡張してカスタム コントロールを作成する方法があります。これは、既存のコントロ ールの仕組み(プロパテゖやベント)をあまり変更する必要がないものの、コントロール テンプレート のように外観の変更だけで済まない場合に便利です。全 9 回を通じて学んだとおり、Visual Studio 2010 を活用することで、機能的かつ使いやすい Windows ゕプリケーションを容易に開発できます。ぜひ、皆さんもチャレンジしてみてください。