コントロールの作成の概要Control Authoring Overview

Windows Presentation Foundation (WPF)Windows Presentation Foundation (WPF) コントロール モデルの機能拡張により、新しいコントロールを作成する必要性が大幅に削減されます。The extensibility of the Windows Presentation Foundation (WPF)Windows Presentation Foundation (WPF) control model greatly reduces the need to create a new control. ただし、場合によっては、カスタム コントロールを作成する必要があります。However, in certain cases you may still need to create a custom control. このトピックでは、カスタム コントロールを作成する必要性を最小限に抑える機能と、Windows Presentation Foundation (WPF)Windows Presentation Foundation (WPF) のさまざまなコントロール作成モデルについて説明します。This topic discusses the features that minimize your need to create a custom control and the different control authoring models in Windows Presentation Foundation (WPF)Windows Presentation Foundation (WPF). また、新しいコントロールを作成する方法も示します。This topic also demonstrates how to create a new control.

新しいコントロールの作成に代わる方法Alternatives to Writing a New Control

従来は、既存のコントロールをカスタマイズする場合、背景色、境界線の幅、フォントのサイズなど、コントロールの標準プロパティを変更するなどの範囲に制限されていました。Historically, if you wanted to get a customized experience from an existing control, you were limited to changing the standard properties of the control, such as background color, border width, and font size. これらの定義済みのパラメーター以外に、コントロールの外観や動作にまでカスタマイズを拡張しようとすると、通常、既存のコントロールを継承し、コントロールを描画するメソッドをオーバーライドして、新しいコントロールを作成する必要がありました。If you wished to extend the appearance or behavior of a control beyond these predefined parameters, you would need to create a new control, usually by inheriting from an existing control and overriding the method responsible for drawing the control. その方法は今でも選択できますが、WPFWPF の場合、リッチ コンテンツ モデル、スタイル、テンプレート、トリガーを使用して、既存のコントロールをカスタマイズできます。Although that is still an option, WPFWPF enables to you customize existing controls by using its rich content model, styles, templates, and triggers. 新しいコントロールを作成しなくても、これらの機能を使用して、カスタマイズされた一貫性のあるエクスペリエンスを得られる方法としては、次のような例が挙げられます。The following list gives examples of how these features can be used to create custom and consistent experiences without having to create a new control.

  • リッチ コンテンツ。Rich Content. 標準の WPFWPF コントロールの多くがリッチ コンテンツをサポートしています。Many of the standard WPFWPF controls support rich content. たとえば、Button の content プロパティは Object型であるため、理論的には Buttonに何でも表示できます。For example, the content property of a Button is of type Object, so theoretically anything can be displayed on a Button. ボタンに画像とテキストを表示させるには、画像と TextBlockStackPanel に追加し、StackPanelContent プロパティに割り当てます。To have a button display an image and text, you can add an image and a TextBlock to a StackPanel and assign the StackPanel to the Content property. コントロールには、WPFWPF の視覚的要素と任意のデータを表示できるため、複雑な視覚化をサポートするために、新しいコントロールを作成したり、既存のコントロールを変更したりする必要性が少なくなります。Because the controls can display WPFWPF visual elements and arbitrary data, there is less need to create a new control or to modify an existing control to support a complex visualization. WPFWPFにおける Button およびその他のコンテンツモデルのコンテンツモデルの詳細については、「 WPF コンテンツモデル」を参照してください。For more information about the content model for Button and other content models in WPFWPF, see WPF Content Model.

  • スタイルStyles. Style は、コントロールのプロパティを表す値のコレクションです。A Style is a collection of values that represent properties for a control. スタイルを使用すると、新しいコントロールを作成しなくても、必要なコントロールの外観と動作を備えた再利用可能な表現を作成できます。By using styles, you can create a reusable representation of a desired control appearance and behavior without writing a new control. たとえば、すべての TextBlock コントロールに対して、フォントサイズが14の赤い Arial フォントを使用するとします。For example, assume that you want all of your TextBlock controls to have red, Arial font with a font size of 14. そこで、リソースとしてスタイルを作成し、それに応じて、適切なプロパティを設定します。You can create a style as a resource and set the appropriate properties accordingly. アプリケーションに追加したすべての TextBlock の外観は同じになります。Then every TextBlock that you add to your application will have the same appearance.

  • データ テンプレート。Data Templates. DataTemplate を使用すると、コントロールにデータを表示する方法をカスタマイズできます。A DataTemplate enables you to customize how data is displayed on a control. たとえば、DataTemplate を使用すると、ListBoxでのデータの表示方法を指定できます。For example, a DataTemplate can be used to specify how data is displayed in a ListBox. この例については、「データ テンプレートの概要」を参照してください。For an example of this, see Data Templating Overview. データの外観をカスタマイズすることに加えて、DataTemplate には UI 要素を含めることができます。これにより、カスタム Ui の柔軟性が大幅に向上します。In addition to customizing the appearance of data, a DataTemplate can include UI elements, which gives you a lot of flexibility in custom UIs. たとえば、DataTemplateを使用すると、各項目にチェックボックスが含まれている ComboBox を作成できます。For example, by using a DataTemplate, you can create a ComboBox in which each item contains a check box.

  • コントロール テンプレート。Control Templates. WPFWPF の多くのコントロールでは、コントロールの構造と外観を定義するために ControlTemplate を使用します。これにより、コントロールの外観とコントロールの機能が分離されます。Many controls in WPFWPF use a ControlTemplate to define the control's structure and appearance, which separates the appearance of a control from the functionality of the control. ControlTemplateを再定義することで、コントロールの外観を大幅に変更できます。You can drastically change the appearance of a control by redefining its ControlTemplate. たとえば、信号機のような外観のコントロールが必要だとします。For example, suppose you want a control that looks like a stoplight. このコントロールのユーザー インターフェイスと機能は単純です。This control has a simple user interface and functionality. コントロールは 3 つの円で構成され、一度に点灯するのはそのうちの 1 つだけです。The control is three circles, only one of which can be lit up at a time. 一部のリフレクションでは、一度に選択できるのは1つの機能だけであることがわかっている場合がありますが、RadioButton の既定の外観は、ライト信号のようなものでは RadioButton ないように見えます。After some reflection, you might realize that a RadioButton offers the functionality of only one being selected at a time, but the default appearance of the RadioButton looks nothing like the lights on a stoplight. RadioButton はコントロールテンプレートを使用してその外観を定義するため、コントロールの要件に合わせて ControlTemplate を再定義し、オプションボタンを使用して信号を行うことが簡単です。Because the RadioButton uses a control template to define its appearance, it is easy to redefine the ControlTemplate to fit the requirements of the control, and use radio buttons to make your stoplight.


    RadioButtonDataTemplateを使用できますが、この例では DataTemplate では不十分です。Although a RadioButton can use a DataTemplate, a DataTemplate is not sufficient in this example. DataTemplate は、コントロールのコンテンツの外観を定義します。The DataTemplate defines the appearance of the content of a control. RadioButtonの場合、コンテンツは、RadioButton が選択されているかどうかを示す円の右側に表示されます。In the case of a RadioButton, the content is whatever appears to the right of the circle that indicates whether the RadioButton is selected. 信号機の例では、オプション ボタンに必要なのは "点灯" する円だけです。In the example of the stoplight, the radio button needs just be a circle that can "light up." 信号の外観要件は RadioButtonの既定の外観とは異なるため、ControlTemplateを再定義する必要があります。Because the appearance requirement for the stoplight is so different than the default appearance of the RadioButton, it is necessary to redefine the ControlTemplate. 一般に、コントロールのコンテンツ (またはデータ) を定義するために DataTemplate を使用し、ControlTemplate を使用してコントロールを構造化する方法を定義します。In general a DataTemplate is used for defining the content (or data) of a control, and a ControlTemplate is used for defining how a control is structured.

  • トリガー。Triggers. Trigger を使用すると、新しいコントロールを作成せずに、コントロールの外観と動作を動的に変更できます。A Trigger allows you to dynamically change the appearance and behavior of a control without creating a new control. たとえば、アプリケーションに複数の ListBox コントロールがあり、各 ListBox の項目を選択したときに太字と赤にする必要があるとします。For example, suppose you have multiple ListBox controls in your application and want the items in each ListBox to be bold and red when they are selected. 最初の性質は、ListBox から継承するクラスを作成し、選択した項目の外観を変更するために OnSelectionChanged メソッドをオーバーライドすることですが、より適切な方法は、選択した項目の外観を変更する ListBoxItem のスタイルにトリガーを追加する方法です。Your first instinct might be to create a class that inherits from ListBox and override the OnSelectionChanged method to change the appearance of the selected item, but a better approach is to add a trigger to a style of a ListBoxItem that changes the appearance of the selected item. トリガーを使用すると、プロパティ値を変更したり、プロパティ値に基づいた処理を実行したりできます。A trigger enables you to change property values or take actions based on the value of a property. EventTrigger を使用すると、イベントが発生したときにアクションを実行できます。An EventTrigger enables you to take actions when an event occurs.

スタイル、テンプレート、トリガーの詳細については、「スタイルとテンプレート」を参照してください。For more information about styles, templates, and triggers, see Styling and Templating.

一般に、既存のコントロールと同じ機能を持ち、外観が異なるコントロールが必要な場合は、このセクションで説明した方法のいずれかを使用して、既存のコントロールの外観を変更できないかどうかをまず検討することをお勧めします。In general, if your control mirrors the functionality of an existing control, but you want the control to look different, you should first consider whether you can use any of the methods discussed in this section to change the existing control's appearance.

コントロール作成モデルModels for Control Authoring

リッチ コンテンツ モデル、スタイル、テンプレート、トリガーを使用すると、新しいコントロールを作成する必要性が最小限に抑えられます。The rich content model, styles, templates, and triggers minimize the need for you to create a new control. ただし、新しいコントロールを作成する必要がある場合は、WPFWPF の各種のコントロール作成モデルを理解することが重要です。However, if you do need to create a new control, it is important to understand the different control authoring models in WPFWPF. WPFWPF には、コントロールを作成するための一般的なモデルが 3 つあり、各モデルはそれぞれ異なる機能と柔軟性レベルを備えています。provides three general models for creating a control, each of which provides a different set of features and level of flexibility. 3つのモデルの基本クラスは、UserControlControl、および FrameworkElementです。The base classes for the three models are UserControl, Control, and FrameworkElement.

UserControl からの派生Deriving from UserControl

WPFWPF でコントロールを作成する最も簡単な方法は、UserControlから派生することです。The simplest way to create a control in WPFWPF is to derive from UserControl. UserControlから継承するコントロールを作成する場合は、既存のコンポーネントを UserControlに追加し、コンポーネントに名前を付けると共に、Extensible Application Markup Language (XAML)Extensible Application Markup Language (XAML)でイベントハンドラーを参照します。When you build a control that inherits from UserControl, you add existing components to the UserControl, name the components, and reference event handlers in Extensible Application Markup Language (XAML)Extensible Application Markup Language (XAML). 次に、指定の要素を参照し、コードでイベント ハンドラーを定義します。You can then reference the named elements and define the event handlers in code. この開発モデルは、WPFWPF でのアプリケーション開発に使用されるモデルとよく似ています。This development model is very similar to the model used for application development in WPFWPF.

正しく構築されている場合、UserControl はリッチコンテンツ、スタイル、およびトリガーの利点を活用できます。If built correctly, a UserControl can take advantage of the benefits of rich content, styles, and triggers. ただし、コントロールが UserControlから継承されている場合、コントロールを使用するユーザーは、DataTemplate または ControlTemplate を使用してその外観をカスタマイズすることはできません。However, if your control inherits from UserControl, people who use your control will not be able to use a DataTemplate or ControlTemplate to customize its appearance. テンプレートをサポートするカスタムコントロールを作成するには、Control クラスまたはその派生クラス (UserControl以外) から派生する必要があります。It is necessary to derive from the Control class or one of its derived classes (other than UserControl) to create a custom control that supports templates.

UserControl からの派生の利点Benefits of Deriving from UserControl

次のすべてに該当する場合は、UserControl から派生することを検討してください。Consider deriving from UserControl if all of the following apply:

  • アプリケーションの構築と同じ方法でコントロールをビルドする必要がある場合。You want to build your control similarly to how you build an application.

  • コントロールが既存のコンポーネントのみで構成されている場合。Your control consists only of existing components.

  • 複雑なカスタマイズをサポートする必要がない場合。You don't need to support complex customization.

Control からの派生Deriving from Control

Control クラスからの派生は、既存の WPFWPF コントロールの大部分で使用されるモデルです。Deriving from the Control class is the model used by most of the existing WPFWPF controls. Control クラスから継承するコントロールを作成する場合は、テンプレートを使用してそのコントロールの外観を定義します。When you create a control that inherits from the Control class, you define its appearance by using templates. これにより、操作ロジックと視覚的表現とが分離されます。By doing so, you separate the operational logic from the visual representation. また、イベントではなくコマンドとバインドを使用して UI とロジックを分離し、可能な限り ControlTemplate 内の要素を参照しないようにすることもできます。You can also ensure the decoupling of the UI and logic by using commands and bindings instead of events and avoiding referencing elements in the ControlTemplate whenever possible. コントロールの UI とロジックが適切に分離されている場合は、コントロールのユーザーがコントロールの ControlTemplate を再定義して、外観をカスタマイズできます。If the UI and logic of your control are properly decoupled, a user of your control can redefine the control's ControlTemplate to customize its appearance. カスタム Control の構築は、UserControlを構築するほど簡単ではありませんが、カスタム Control は最も柔軟性があります。Although building a custom Control is not as simple as building a UserControl, a custom Control provides the most flexibility.

Control からの派生の利点Benefits of Deriving from Control

次のいずれかに該当する場合は、UserControl クラスを使用するのではなく、Control から派生することを検討してください。Consider deriving from Control instead of using the UserControl class if any of the following apply:

  • コントロールの外観をカスタマイズできるようにするには、ControlTemplateを使用します。You want the appearance of your control to be customizable via the ControlTemplate.

  • コントロールがさまざまなテーマをサポートする必要がある場合。You want your control to support different themes.

FrameworkElement からの派生Deriving from FrameworkElement

UserControl または Control から派生するコントロールは、既存の要素の作成に依存します。Controls that derive from UserControl or Control rely upon composing existing elements. 多くのシナリオでは、これは許容できるソリューションです。 FrameworkElement から継承されたオブジェクトは ControlTemplate内に存在する可能性があるためです。For many scenarios, this is an acceptable solution, because any object that inherits from FrameworkElement can be in a ControlTemplate. しかし、場合によっては、単純な要素コンポジションでは、コントロールの外観に必要な機能を実現できないことがあります。However, there are times when a control's appearance requires more than the functionality of simple element composition. これらのシナリオでは、FrameworkElement 上のコンポーネントに基づいて適切な選択を行います。For these scenarios, basing a component on FrameworkElement is the right choice.

FrameworkElementベースのコンポーネントを構築するには、直接レンダリングとカスタム要素構成という2つの標準的な方法があります。There are two standard methods for building FrameworkElement-based components: direct rendering and custom element composition. 直接レンダリングでは、FrameworkElementOnRender メソッドをオーバーライドし、コンポーネントのビジュアルを明示的に定義する DrawingContext 操作を提供します。Direct rendering involves overriding the OnRender method of FrameworkElement and providing DrawingContext operations that explicitly define the component visuals. これは、ImageBorderによって使用されるメソッドです。This is the method used by Image and Border. カスタム要素の構成では、Visual 型のオブジェクトを使用して、コンポーネントの外観を構成します。Custom element composition involves using objects of type Visual to compose the appearance of your component. 例については、「DrawingVisual オブジェクトの使用」を参照してください。For an example, see Using DrawingVisual Objects. Track は、カスタム要素構成を使用する WPFWPF 内のコントロールの例です。Track is an example of a control in WPFWPF that uses custom element composition. 同じコントロールでダイレクト レンダリングとカスタム要素コンポジションを混在させることもできます。It is also possible to mix direct rendering and custom element composition in the same control.

FrameworkElement からの派生の利点Benefits of Deriving from FrameworkElement

次のいずれかに該当する場合は、FrameworkElement から派生することを検討してください。Consider deriving from FrameworkElement if any of the following apply:

  • コントロールの外観について、単純な要素コンポジションが提供する以上の厳密な制御を必要とする場合。You want to have precise control over the appearance of your control beyond what is provided by simple element composition.

  • 独自のレンダリング ロジックを定義して、コントロールの外観を定義する必要がある場合。You want to define the appearance of your control by defining your own render logic.

  • 既存の要素を新しい方法で作成し、UserControlControlで可能なものを超えています。You want to compose existing elements in novel ways that go beyond what is possible with UserControl and Control.

コントロール作成の基本Control Authoring Basics

既に説明したように、WPFWPF の最も強力な機能の 1 つは、コントロールの基本的なプロパティ設定だけでは不可能な外観や動作の変更を実現し、しかもカスタム コントロールを作成する必要がないということです。As discussed earlier, one of the most powerful features of WPFWPF is the ability to go beyond setting basic properties of a control to change its appearance and behavior, yet still not needing to create a custom control. スタイル設定、データ バインディング、トリガーの各機能は、WPFWPF プロパティ システムおよび WPFWPF イベント システムによって実現されています。The styling, data binding, and trigger features are made possible by the WPFWPF property system and the WPFWPF event system. 以降のセクションでは、カスタム コントロールのユーザーが、WPFWPF に付属のコントロールと同じように、これらの機能を使用できるようにするために、カスタム コントロールの作成に使用するモデルに関係なく、従う必要があるプラクティスについて説明します。The following sections describe some practices that you should follow, regardless of the model you use to create the custom control, so that users of your custom control can use these features just as they would for a control that is included with WPFWPF.

依存関係プロパティの使用Use Dependency Properties

プロパティが依存関係プロパティである場合、以下の操作が可能です。When a property is a dependency property, it is possible to do the following:

  • スタイルのプロパティを設定する。Set the property in a style.

  • プロパティをデータ ソースにバインドする。Bind the property to a data source.

  • プロパティの値として、動的リソースを使用する。Use a dynamic resource as the property's value.

  • プロパティ名をアニメーション化する。Animate the property.

コントロールのプロパティがこれらの機能のいずれかをサポートす必要がある場合、それを依存関係プロパティとして実装する必要があります。If you want a property of your control to support any of this functionality, you should implement it as a dependency property. 次の例では、以下の処理を実行して、Value という名前の依存関係プロパティを定義します。The following example defines a dependency property named Value by doing the following:

  • ValueProperty という名前の DependencyProperty 識別子を readonly フィールド static public として定義します。Define a DependencyProperty identifier named ValueProperty as a public static readonly field.

  • DependencyProperty.Registerを呼び出して、プロパティ名をプロパティシステムに登録し、次の値を指定します。Register the property name with the property system, by calling DependencyProperty.Register, to specify the following:

    • プロパティの名前。The name of the property.

    • プロパティの型。The type of the property.

    • プロパティを所有する型。The type that owns the property.

    • プロパティのメタデータ。The metadata for the property. メタデータには、プロパティの既定値、CoerceValueCallback および PropertyChangedCallbackが含まれています。The metadata contains the property's default value, a CoerceValueCallback and a PropertyChangedCallback.

  • Valueという名前の CLR ラッパープロパティを定義します。これは、プロパティの getset アクセサーを実装することによって、依存関係プロパティの登録に使用される名前と同じです。Define a CLR wrapper property named Value, which is the same name that is used to register the dependency property, by implementing the property's get and set accessors. get アクセサーと set アクセサーは、それぞれ GetValue および SetValue を呼び出すだけであることに注意してください。Note that the get and set accessors only call GetValue and SetValue respectively. クライアントと WPFWPF はアクセサーをバイパスし、GetValueSetValue を直接呼び出すことができるため、依存関係プロパティのアクセサーに追加のロジックが含まれないようにすることをお勧めします。It is recommended that the accessors of dependency properties not contain additional logic because clients and WPFWPF can bypass the accessors and call GetValue and SetValue directly. たとえば、プロパティがデータ ソースにバインドされている場合、プロパティの set アクセサーは呼び出されません。For example, when a property is bound to a data source, the property's set accessor is not called. Get アクセサーと set アクセサーにロジックを追加するのではなく、ValidateValueCallbackCoerceValueCallback、および PropertyChangedCallback デリゲートを使用して応答するか、変更時に値を確認します。Instead of adding additional logic to the get and set accessors, use the ValidateValueCallback, CoerceValueCallback, and PropertyChangedCallback delegates to respond to or check the value when it changes. これらのコールバックの詳細については、「依存関係プロパティのコールバックと検証」を参照してください。For more information on these callbacks, see Dependency Property Callbacks and Validation.

  • CoerceValueという名前の CoerceValueCallback のメソッドを定義します。Define a method for the CoerceValueCallback named CoerceValue. CoerceValue によって、ValueMinValue 以上で MaxValue 以下になります。CoerceValue ensures that Value is greater or equal to MinValue and less than or equal to MaxValue.

  • OnValueChangedという名前の PropertyChangedCallbackのメソッドを定義します。Define a method for the PropertyChangedCallback, named OnValueChanged. OnValueChangedRoutedPropertyChangedEventArgs<T> オブジェクトを作成し、ValueChanged ルーティングイベントの発生を準備します。OnValueChanged creates a RoutedPropertyChangedEventArgs<T> object and prepares to raise the ValueChanged routed event. ルーティング イベントについては、次のセクションで説明します。Routed events are discussed in the next section.

       /// <summary>
       /// Identifies the Value dependency property.
       /// </summary>
       public static readonly DependencyProperty ValueProperty =
               "Value", typeof(decimal), typeof(NumericUpDown),
               new FrameworkPropertyMetadata(MinValue, new PropertyChangedCallback(OnValueChanged),
                                             new CoerceValueCallback(CoerceValue)));

       /// <summary>
       /// Gets or sets the value assigned to the control.
       /// </summary>
       public decimal Value
           get { return (decimal)GetValue(ValueProperty); }
           set { SetValue(ValueProperty, value); }

       private static object CoerceValue(DependencyObject element, object value)
           decimal newValue = (decimal)value;
           NumericUpDown control = (NumericUpDown)element;

           newValue = Math.Max(MinValue, Math.Min(MaxValue, newValue));
           return newValue;

       private static void OnValueChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
           NumericUpDown control = (NumericUpDown)obj;			

           RoutedPropertyChangedEventArgs<decimal> e = new RoutedPropertyChangedEventArgs<decimal>(
               (decimal)args.OldValue, (decimal)args.NewValue, ValueChangedEvent);
''' <summary>
''' Identifies the Value dependency property.
''' </summary>
Public Shared ReadOnly ValueProperty As DependencyProperty = DependencyProperty.Register("Value", GetType(Decimal), GetType(NumericUpDown), New FrameworkPropertyMetadata(MinValue, New PropertyChangedCallback(AddressOf OnValueChanged), New CoerceValueCallback(AddressOf CoerceValue)))

''' <summary>
''' Gets or sets the value assigned to the control.
''' </summary>
Public Property Value() As Decimal
        Return CDec(GetValue(ValueProperty))
    End Get
    Set(ByVal value As Decimal)
        SetValue(ValueProperty, value)
    End Set
End Property

Private Shared Overloads Function CoerceValue(ByVal element As DependencyObject, ByVal value As Object) As Object
    Dim newValue As Decimal = CDec(value)
    Dim control As NumericUpDown = CType(element, NumericUpDown)

    newValue = Math.Max(MinValue, Math.Min(MaxValue, newValue))

    Return newValue
End Function

Private Shared Sub OnValueChanged(ByVal obj As DependencyObject, ByVal args As DependencyPropertyChangedEventArgs)
    Dim control As NumericUpDown = CType(obj, NumericUpDown)

    Dim e As New RoutedPropertyChangedEventArgs(Of Decimal)(CDec(args.OldValue), CDec(args.NewValue), ValueChangedEvent)
End Sub

詳細については、「カスタム依存関係プロパティ」を参照してください。For more information, see Custom Dependency Properties.

ルーティング イベントの使用Use Routed Events

依存関係プロパティが CLR プロパティの概念を拡張し、機能が追加されるのと同様に、ルーティングイベントは標準の CLR イベントの概念を拡張します。Just as dependency properties extend the notion of CLR properties with additional functionality, routed events extend the notion of standard CLR events. 新しい WPFWPF コントロール作成する場合、イベントをルーティング イベントとして実装することをお勧めします。ルーティング イベントは以下の機能をサポートしているためです。When you create a new WPFWPF control, it is also good practice to implement your event as a routed event because a routed event supports the following behavior:

  • 複数のコントロールの親でイベントを処理できます。Events can be handled on a parent of multiple controls. イベントがバブル イベントの場合、要素ツリー内の単一の親はイベントをサブスクライブできます。If an event is a bubbling event, a single parent in the element tree can subscribe to the event. これにより、アプリケーション開発者は、複数のコントロールのイベントに 1 つのハンドラーで対応できます。Then application authors can use one handler to respond to the event of multiple controls. たとえば、コントロールが ListBox 内の各項目の一部である場合 (DataTemplateに含まれているため)、アプリケーション開発者は ListBoxに対するコントロールのイベントのイベントハンドラーを定義できます。For example, if your control is a part of each item in a ListBox (because it is included in a DataTemplate), the application developer can define the event handler for your control's event on the ListBox. いずれかのコントロールでイベントが発生するたびに、そのイベント ハンドラーが呼び出されます。Whenever the event occurs on any of the controls, the event handler is called.

  • ルーティングイベントは、EventSetterで使用できます。これにより、アプリケーション開発者は、スタイル内でイベントのハンドラーを指定できます。Routed events can be used in an EventSetter, which enables application developers to specify the handler of an event within a style.

  • ルーティングイベントは、EventTriggerで使用できます。これは、XAMLXAMLを使用してプロパティをアニメーション化する場合に便利です。Routed events can be used in an EventTrigger, which is useful for animating properties by using XAMLXAML. 詳しくは、「 アニメーションの概要」をご覧ください。For more information, see Animation Overview.

次に示す例では、以下の処理を実行して、ルーティング イベントを定義します。The following example defines a routed event by doing the following:

  • ValueChangedEvent という名前の RoutedEvent 識別子を readonly フィールド static public として定義します。Define a RoutedEvent identifier named ValueChangedEvent as a public static readonly field.

  • EventManager.RegisterRoutedEvent メソッドを呼び出して、ルーティングイベントを登録します。Register the routed event by calling the EventManager.RegisterRoutedEvent method. この例では、RegisterRoutedEventを呼び出すときに、次の情報を指定します。The example specifies the following information when it calls RegisterRoutedEvent:

    • イベントの名前が ValueChanged であること。The name of the event is ValueChanged.

    • ルーティング方法は Bubbleです。つまり、ソース (イベントを発生させたオブジェクト) のイベントハンドラーが最初に呼び出され、次に、ソースの親要素のイベントハンドラーが連続して呼び出されます。その後、最も近い親要素。The routing strategy is Bubble, which means that an event handler on the source (the object that raises the event) is called first, and then event handlers on the source's parent elements are called in succession, starting with the event handler on the closest parent element.

    • イベントハンドラーの種類は、Decimal の種類を使用して構築された RoutedPropertyChangedEventHandler<T>です。The type of the event handler is RoutedPropertyChangedEventHandler<T>, constructed with a Decimal type.

    • イベントを所有する型が NumericUpDown であること。The owning type of the event is NumericUpDown.

  • ValueChanged という名前のパブリック イベントを宣言し、イベント アクセサー宣言を含めます。Declare a public event named ValueChanged and includes event-accessor declarations. この例では、add アクセサー宣言で AddHandler を呼び出し、WPFWPF イベントサービスを使用するために remove アクセサー宣言で RemoveHandler します。The example calls AddHandler in the add accessor declaration and RemoveHandler in the remove accessor declaration to use the WPFWPF event services.

  • ValueChangedイベントを発生させる、保護された仮想メソッド OnValueChanged を作成します。Create a protected, virtual method named OnValueChanged that raises the ValueChanged event.

/// <summary>
/// Identifies the ValueChanged routed event.
/// </summary>
public static readonly RoutedEvent ValueChangedEvent = EventManager.RegisterRoutedEvent(
    "ValueChanged", RoutingStrategy.Bubble, 
    typeof(RoutedPropertyChangedEventHandler<decimal>), typeof(NumericUpDown));

/// <summary>
/// Occurs when the Value property changes.
/// </summary>
public event RoutedPropertyChangedEventHandler<decimal> ValueChanged
    add { AddHandler(ValueChangedEvent, value); }
    remove { RemoveHandler(ValueChangedEvent, value); }

/// <summary>
/// Raises the ValueChanged event.
/// </summary>
/// <param name="args">Arguments associated with the ValueChanged event.</param>
protected virtual void OnValueChanged(RoutedPropertyChangedEventArgs<decimal> args)
''' <summary>
''' Identifies the ValueChanged routed event.
''' </summary>
Public Shared ReadOnly ValueChangedEvent As RoutedEvent = EventManager.RegisterRoutedEvent("ValueChanged", RoutingStrategy.Bubble, GetType(RoutedPropertyChangedEventHandler(Of Decimal)), GetType(NumericUpDown))

''' <summary>
''' Occurs when the Value property changes.
''' </summary>
Public Custom Event ValueChanged As RoutedPropertyChangedEventHandler(Of Decimal)
    AddHandler(ByVal value As RoutedPropertyChangedEventHandler(Of Decimal))
        MyBase.AddHandler(ValueChangedEvent, value)
    End AddHandler
    RemoveHandler(ByVal value As RoutedPropertyChangedEventHandler(Of Decimal))
        MyBase.RemoveHandler(ValueChangedEvent, value)
    End RemoveHandler
    RaiseEvent(ByVal sender As System.Object, ByVal e As RoutedPropertyChangedEventArgs(Of Decimal))
    End RaiseEvent
End Event

''' <summary>
''' Raises the ValueChanged event.
''' </summary>
''' <param name="args">Arguments associated with the ValueChanged event.</param>
Protected Overridable Sub OnValueChanged(ByVal args As RoutedPropertyChangedEventArgs(Of Decimal))
End Sub

詳細については、「ルーティング イベントの概要」および「カスタム ルーティング イベントを作成する」を参照してください。For more information, see Routed Events Overview and Create a Custom Routed Event.

バインディングの使用Use Binding

コントロールの UI とロジックを分離するには、データ バインディングを使用する方法もあります。To decouple the UI of your control from its logic, consider using data binding. これは、ControlTemplateを使用してコントロールの外観を定義する場合に特に重要です。This is particularly important if you define the appearance of your control by using a ControlTemplate. データ バインディングを使用すると、コードから UI の特定の部分を参照する必要性がなくなる場合があります。When you use data binding, you might be able to eliminate the need to reference specific parts of the UI from the code. ControlTemplate 内の要素を参照しないようにすることをお勧めします。コードが ControlTemplate 内の要素を参照し、ControlTemplate が変更された場合、参照される要素を新しい ControlTemplateに含める必要があるためです。It's a good idea to avoid referencing elements that are in the ControlTemplate because when the code references elements that are in the ControlTemplate and the ControlTemplate is changed, the referenced element needs to be included in the new ControlTemplate.

次の例では、NumericUpDown コントロールの TextBlock を更新し、それに名前を割り当て、コードでテキストボックスを名前で参照しています。The following example updates the TextBlock of the NumericUpDown control, assigning a name to it and referencing the textbox by name in code.

<Border BorderThickness="1" BorderBrush="Gray" Margin="2" 
        Grid.RowSpan="2" VerticalAlignment="Center" HorizontalAlignment="Stretch">
  <TextBlock Name="valueText" Width="60" TextAlignment="Right" Padding="5"/>
private void UpdateTextBlock()
    valueText.Text = Value.ToString();
Private Sub UpdateTextBlock()
    valueText.Text = Value.ToString()
End Sub

次の例では、バインディングを使用して同じことを実現しています。The following example uses binding to accomplish the same thing.

<Border BorderThickness="1" BorderBrush="Gray" Margin="2" 
        Grid.RowSpan="2" VerticalAlignment="Center" HorizontalAlignment="Stretch">

    <!--Bind the TextBlock to the Value property-->
        Width="60" TextAlignment="Right" Padding="5"
        Text="{Binding RelativeSource={RelativeSource FindAncestor, 
                       AncestorType={x:Type local:NumericUpDown}}, 


データ バインディングの詳細については、「データ バインディングの概要」を参照してください。For more information about data binding, see Data Binding Overview.

デザイナーに対応したデザインDesign for Designers

Visual Studio の WPF デザイナー (たとえば、プロパティウィンドウを使用したプロパティ編集) でカスタム WPF コントロールのサポートを受けるには、次のガイドラインに従います。To receive support for custom WPF controls in the WPF Designer for Visual Studio (for example, property editing with the Properties window), follow these guidelines. WPF デザイナーの開発の詳細については、「 Visual Studio での XAML のデザイン」を参照してください。For more information on developing for the WPF Designer, see Design XAML in Visual Studio.

依存関係プロパティDependency Properties

「依存関係プロパティの使用」で前述したように、CLR getset アクセサーを実装してください。Be sure to implement CLR get and set accessors as described earlier, in "Use Dependency Properties." デザイナーは、ラッパーを使用して依存関係プロパティの存在を検出する場合がありますが、WPFWPF およびコントロールのクライアントと同様、プロパティを取得または設定するときにアクセサーを呼び出す必要はありません。Designers may use the wrapper to detect the presence of a dependency property, but they, like WPFWPF and clients of the control, are not required to call the accessors when getting or setting the property.

アタッチされるプロパティAttached Properties

以下のガイドラインに従って、カスタム コントロールに添付プロパティを実装する必要があります。You should implement attached properties on custom controls using the following guidelines:

  • Property メソッドを使用して作成されたPropertyName RegisterAttached フォームの DependencyProperty readonly public static を持っている。Have a public static readonly DependencyProperty of the form PropertyNameProperty that was creating using the RegisterAttached method. RegisterAttached に渡されるプロパティ名はPropertyNameと一致している必要があります。The property name that is passed to RegisterAttached must match PropertyName.

  • Set PropertyName および Get PropertyName という名前の public``static CLR メソッドのペアを実装します。Implement a pair of public static CLR methods named SetPropertyName and GetPropertyName. どちらのメソッドも、最初の引数として DependencyProperty から派生したクラスを受け入れる必要があります。Both methods should accept a class derived from DependencyProperty as their first argument. また、Set PropertyName メソッドでは、プロパティの登録データ型と同じ型の引数も受け取ります。The SetPropertyName method also accepts an argument whose type matches the registered data type for the property. Get PropertyNameメソッドでは、同じ型の値を返す必要があります。The GetPropertyName method should return a value of the same type. Set PropertyNameメソッドがない場合、プロパティは読み取り専用としてマークされます。If the SetPropertyName method is missing, the property is marked read-only.

  • Set propertynameおよび Getpropertynameは、ターゲットの依存関係オブジェクトの GetValue および SetValue メソッドにそれぞれ直接ルーティングする必要があります。Set PropertyName and GetPropertyName must route directly to the GetValue and SetValue methods on the target dependency object, respectively. デザイナーが添付プロパティにアクセスするには、メソッド ラッパー経由で呼び出す場合もあれば、対象の依存関係オブジェクトを直接呼び出す場合もあります。Designers may access the attached property by calling through the method wrapper or making a direct call to the target dependency object.

添付プロパティの詳細については、「添付プロパティの概要」を参照してください。For more information on attached properties, see Attached Properties Overview.

共有リソースの定義と使用Define and Use Shared Resources

アプリケーションと同じアセンブリにコントロールを含めることも、複数のアプリケーションが使用できる別のアセンブリにコントロールをパッケージ化することもできます。You can include your control in the same assembly as your application, or you can package your control in a separate assembly that can be used in multiple applications. このトピックで説明した情報の大部分は、使用する方法に関係なく適用されます。For the most part, the information discussed in this topic applies regardless of the method you use. ただし、1 つだけ例外があります。There is one difference worth noting, however. アプリケーションと同じアセンブリ内にコントロールを配置する場合、App.xaml ファイルにグローバル リソースを自由に追加できます。When you put a control in the same assembly as an application, you are free to add global resources to the App.xaml file. ただし、コントロールのみを含むアセンブリには Application オブジェクトが関連付けられていないため、app.xaml ファイルは使用できません。But an assembly that contains only controls does not have an Application object associated with it, so an App.xaml file is not available.

アプリケーションがリソースを検索するときは、次に示す順序で 3 つのレベルを検索します。When an application looks for a resource, it looks at three levels in the following order:

  1. 要素レベル。The element level.

    システムは、リソースを参照する要素から検索を開始し、ルート要素に到達するまで、論理上の親のリソースの検索を継続します。The system starts with the element that references the resource and then searches resources of the logical parent and so forth until the root element is reached.

  2. アプリケーション レベル。The application level.

    Application オブジェクトによって定義されたリソース。Resources defined by the Application object.

  3. テーマ レベル。The theme level.

    テーマ レベルのディクショナリは、Themes という名前のサブフォルダーに格納されています。Theme-level dictionaries are stored in a subfolder named Themes. Themes フォルダー内のファイルはテーマに対応しています。The files in the Themes folder correspond to themes. たとえば、Aero.NormalColor.xaml、Luna.NormalColor.xaml、Royale.NormalColor.xaml などのファイルがあります。For example, you might have Aero.NormalColor.xaml, Luna.NormalColor.xaml, Royale.NormalColor.xaml, and so on. generic.xaml という名前のファイルが含まれている場合もあります。You can also have a file named generic.xaml. システムがテーマ レベルでリソースを検索するとき、最初にテーマ固有のファイル内を検索し、次に generic.xaml 内を検索します。When the system looks for a resource at the themes level, it first looks for it in the theme-specific file and then looks for it in generic.xaml.

アプリケーションとは別のアセンブリ内にコントロールを含めるときは、グローバル リソースを要素レベルまたはテーマ レベルに配置する必要があります。When your control is in an assembly that is separate from the application, you must put your global resources at the element level or at the theme level. どちらに配置する場合も、それぞれの利点があります。Both methods have their advantages.

要素レベルでのリソース定義Defining Resources at the Element Level

カスタムのリソース ディクショナリを作成し、それをコントロールのリソース ディクショナリと結合することによって、共有リソースを要素レベルで定義できます。You can define shared resources at the element level by creating a custom resource dictionary and merging it with your control’s resource dictionary. このメソッドで定義する場合は、リソース ファイルに任意の名前を付けて、コントロールと同じフォルダーに配置できます。When you use this method, you can name your resource file anything you want, and it can be in the same folder as your controls. 要素レベルでのリソースでは、単純な文字列をキーとして使用することもできます。Resources at the element level can also use simple strings as keys. 次の例では、Dictionary1 という名前の LinearGradientBrush リソースファイルを作成します。The following example creates a LinearGradientBrush resource file named Dictionary1.xaml.

    StartPoint="0,0" EndPoint="1,1">
    <GradientStop Color="Red" Offset="0.25" />
    <GradientStop Color="Blue" Offset="0.75" />

ディクショナリを定義したら、それをコントロールのリソース ディクショナリにマージする必要があります。Once you have defined your dictionary, you need to merge it with your control's resource dictionary. これには、XAMLXAML またはコードを使用します。You can do this by using XAMLXAML or code.

次の例では、XAMLXAML を使用してリソース ディクショナリを結合します。The following example merges a resource dictionary by using XAMLXAML.

            <ResourceDictionary Source="Dictionary1.xaml"/>

この方法の欠点は、参照するたびに ResourceDictionary オブジェクトが作成されることです。The disadvantage to this approach is that a ResourceDictionary object is created each time you reference it. たとえば、ライブラリに10個のカスタムコントロールがあり、XAML を使用して各コントロールの共有リソースディクショナリをマージする場合は、10個の同一 ResourceDictionary オブジェクトを作成します。For example, if you have 10 custom controls in your library and merge the shared resource dictionaries for each control by using XAML, you create 10 identical ResourceDictionary objects. これを回避するには、コード内のリソースをマージし、結果の ResourceDictionaryを返す静的クラスを作成します。You can avoid this by creating a static class that merges the resources in code and returns the resulting ResourceDictionary.

次の例では、共有 ResourceDictionaryを返すクラスを作成します。The following example creates a class that returns a shared ResourceDictionary.

internal static class SharedDictionaryManager
    internal static ResourceDictionary SharedDictionary
            if (_sharedDictionary == null)
                System.Uri resourceLocater =
                    new System.Uri("/ElementResourcesCustomControlLibrary;component/Dictionary1.xaml", 

                _sharedDictionary = 

            return _sharedDictionary;

    private static ResourceDictionary _sharedDictionary;

次の例では、InitializeComponent を呼び出す前に、共有リソースをコントロールのコンス トラクター内でカスタム コントロールのリソースと結合します。The following example merges the shared resource with the resources of a custom control in the control's constructor before it calls InitializeComponent. SharedDictionaryManager.SharedDictionary は静的プロパティであるため、ResourceDictionary は1回だけ作成されます。Because the SharedDictionaryManager.SharedDictionary is a static property, the ResourceDictionary is created only once. InitializeComponent が呼び出される前に、リソース ディクショナリが結合されため、コントロールは XAMLXAML ファイル内でリソースを使用できます。Because the resource dictionary was merged before InitializeComponent was called, the resources are available to the control in its XAMLXAML file.

public NumericUpDown()

テーマ レベルでのリソース定義Defining Resources at the Theme Level

WPFWPF では、さまざまな Windows テーマ用にリソースを作成できます。enables you to create resources for different Windows themes. コントロールの作成者は、特定のテーマ用のリソースを定義して、使用するテーマに応じてコントロールの外観を変更できます。As a control author, you can define a resource for a specific theme to change your control's appearance depending on what theme is in use. たとえば、windows クラシックテーマでの Button の外観 (Windows 2000 の既定のテーマ) は、Windows Luna テーマの Button (windows XP の既定のテーマ) とは異なります。これは、Button がテーマごとに異なる ControlTemplate を使用するためです。For example, the appearance of a Button in the Windows Classic theme (the default theme for Windows 2000) differs from a Button in the Windows Luna theme (the default theme for Windows XP) because the Button uses a different ControlTemplate for each theme.

テーマ固有のリソースは、固有のファイル名でリソース ディクショナリに保持されます。Resources that are specific to a theme are kept in a resource dictionary with a specific file name. これらのファイルは、コントロールが格納されているフォルダーのサブフォルダーである Themes フォルダー内に配置する必要があります。These files must be in a folder named Themes that is a subfolder of the folder that contains the control. 次の表は、リソース ディクショナリ ファイルと、各ファイルに関連付けられているテーマを示しています。The following table lists the resource dictionary files and the theme that is associated with each file:

リソース ディクショナリ ファイル名Resource dictionary file name Windows テーマWindows theme
Classic.xaml Windows XP のクラシックな Windows 9x/2000 の外観Classic Windows 9x/2000 look on Windows XP
Luna.NormalColor.xaml Windows XP の既定の青のテーマDefault blue theme on Windows XP
Luna.Homestead.xaml Windows XP のオリーブのテーマOlive theme on Windows XP
Luna.Metallic.xaml Windows XP のシルバーのテーマSilver theme on Windows XP
Royale.NormalColor.xaml Windows XP Media Center Edition の既定テーマDefault theme on Windows XP Media Center Edition
Aero.NormalColor.xaml Windows Vista の既定テーマDefault theme on Windows Vista

すべてのテーマのリソースを定義する必要はありません。You do not need to define a resource for every theme. 特定のテーマについてリソースが定義されていない場合、コントロールはリソースの Classic.xaml を確認します。If a resource is not defined for a specific theme, then the control checks Classic.xaml for the resource. 現在のテーマに対応するファイルや Classic.xaml でリソースが定義されていない場合、コントロールは汎用のリソースを使用します。汎用のリソースは、generic.xaml という名前のリソース ディクショナリ ファイルにあります。If the resource is not defined in the file that corresponds to the current theme or in Classic.xaml, the control uses the generic resource, which is in a resource dictionary file named generic.xaml. generic.xaml ファイルは、テーマ固有のリソース ディクショナリ ファイルと同じフォルダーに配置されています。The generic.xaml file is located in the same folder as the theme-specific resource dictionary files. generic.xaml は、特定の Windows テーマには対応していませんが、テーマ レベルのディクショナリであることに変わりありません。Although generic.xaml does not correspond to a specific Windows theme, it is still a theme-level dictionary.

C#またはVisual Basic NumericUpDown カスタムコントロールとテーマおよび UI オートメーションサポートのサンプルには、NumericUpDown コントロール用の2つのリソースディクショナリが含まれています。1つは汎用 .xaml で、もう1つは Luna にあります。The C# or Visual Basic NumericUpDown custom control with theme and UI automation support sample contains two resource dictionaries for the NumericUpDown control: one is in generic.xaml, and the other is in Luna.NormalColor.xaml.

テーマ固有のリソースディクショナリファイルに ControlTemplate を配置する場合は、次の例に示すように、コントロールの静的コンストラクターを作成し、DefaultStyleKeyOverrideMetadata(Type, PropertyMetadata) メソッドを呼び出す必要があります。When you put a ControlTemplate in any of the theme-specific resource dictionary files, you must create a static constructor for your control and call the OverrideMetadata(Type, PropertyMetadata) method on the DefaultStyleKey, as shown in the following example.

static NumericUpDown()
               new FrameworkPropertyMetadata(typeof(NumericUpDown)));
Shared Sub New()
    DefaultStyleKeyProperty.OverrideMetadata(GetType(NumericUpDown), New FrameworkPropertyMetadata(GetType(NumericUpDown)))
End Sub
テーマ リソース用のキーの定義と参照Defining and Referencing Keys for Theme Resources

要素レベルでリソースを定義するときに、文字列をキーとして割り当て、その文字列を使用してリソースにアクセスできます。When you define a resource at the element level, you can assign a string as its key and access the resource via the string. テーマレベルでリソースを定義する場合は、キーとして ComponentResourceKey を使用する必要があります。When you define a resource at the theme level, you must use a ComponentResourceKey as the key. 次の例では、generic.xaml でリソースを定義します。The following example defines a resource in generic.xaml.

     x:Key="{ComponentResourceKey TypeInTargetAssembly={x:Type local:Painter}, 
                                  StartPoint="0,0" EndPoint="1,0">
    <GradientStop Color="Blue" Offset="0" />
    <GradientStop Color="Red" Offset="0.5" />
    <GradientStop Color="Green" Offset="1"/>

次の例では、キーとして ComponentResourceKey を指定することによって、リソースを参照しています。The following example references the resource by specifying the ComponentResourceKey as the key.

    Grid.Column="1" Grid.Row="0"
    Background="{StaticResource {ComponentResourceKey 
                        TypeInTargetAssembly={x:Type local:NumericUpDown}, 
    Grid.Column="1" Grid.Row="1"
    Background="{StaticResource {ComponentResourceKey 
                    TypeInTargetAssembly={x:Type local:NumericUpDown}, 
テーマ リソースの場所の指定Specifying the Location of Theme Resources

コントロールのリソースを見つけるには、アセンブリにコントロール固有のリソースが含まれていることを、ホスト アプリケーションが認識する必要があります。To find the resources for a control, the hosting application needs to know that the assembly contains control-specific resources. これを行うには、コントロールを含むアセンブリに ThemeInfoAttribute を追加します。You can accomplish that by adding the ThemeInfoAttribute to the assembly that contains the control. ThemeInfoAttribute には、汎用リソースの場所を指定する GenericDictionaryLocation プロパティと、テーマ固有のリソースの場所を指定する ThemeDictionaryLocation プロパティがあります。The ThemeInfoAttribute has a GenericDictionaryLocation property that specifies the location of generic resources, and a ThemeDictionaryLocation property that specifies the location of the theme-specific resources.

次の例では、GenericDictionaryLocation プロパティと ThemeDictionaryLocation プロパティを SourceAssemblyに設定して、汎用およびテーマ固有のリソースがコントロールと同じアセンブリ内にあることを指定しています。The following example sets the GenericDictionaryLocation and ThemeDictionaryLocation properties to SourceAssembly, to specify that the generic and theme-specific resources are in the same assembly as the control.

[assembly: ThemeInfo(ResourceDictionaryLocation.SourceAssembly, 
<Assembly: ThemeInfo(ResourceDictionaryLocation.SourceAssembly, ResourceDictionaryLocation.SourceAssembly)>

関連項目See also