コントロール作成の概要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. たとえば、 のButtoncontent プロパティは 型Object、 の場合、理論的には何でもButton表示できます。For example, the content property of a Button is of type Object, so theoretically anything can be displayed on a Button. ボタンにTextBlockイメージとテキストを表示するには、イメージと a を にStackPanel追加して、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. のコンテンツ モデルButtonと他のコンテンツ モデルのWPFWPF詳細については、「 WPF コンテンツ モデル」を参照してください。For more information about the content model for Button and other content models in WPFWPF, see WPF Content Model.

  • スタイル。Styles. AStyleは、コントロールのプロパティを表す値のコレクションです。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コントロールに赤、Arial フォント、フォント サイズ 14 を使用する必要があるとします。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. ADataTemplateを使用すると、コントロール上でのデータの表示方法をカスタマイズできます。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. データの外観をカスタマイズするだけでなく、UI 要素をDataTemplate含めることができ、カスタム 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. いくつかの反射の後、一度に選択RadioButtonされている 1 つの機能しか提供していないことに気付くかもしれませんが、既定の外観は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.

    注意

    aRadioButtonは をDataTemplate使用できますがDataTemplate、この例では a は十分ではありません。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. 一般にDataTemplatea はコントロールのコンテンツ (またはデータ) を定義するために使用ControlTemplateされ、a はコントロールの構造を定義するために使用されます。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. ATriggerを使用すると、新しいコントロールを作成せずに、コントロールの外観と動作を動的に変更できます。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. 最初の本能は、選択した項目の外観を変更するListBoxOnSelectionChangedメソッドを継承してオーバーライドするクラスを作成することですが、選択した項目の外観を変更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 つのモデルの基本クラスはUserControlControlFrameworkElementおよび です。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. イベントの代わりにコマンドとバインディングを使用し、可能なControlTemplate限り要素を参照しないようにすることで、UI とロジックの分離を確実に行うことができます。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

次のいずれかが該当するControl場合は、クラスUserControlを使用する代わりに派生することを検討してください。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は . ControlTemplateFor 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. 直接レンダリングでは、メソッドをOnRenderオーバーライドFrameworkElementし、コンポーネントDrawingContextビジュアルを明示的に定義する操作を提供します。Direct rendering involves overriding the OnRender method of FrameworkElement and providing DrawingContext operations that explicitly define the component visuals. これは、 と でImage使用Borderされる方法です。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.

  • 既存の要素を、可能な点を超えた新しい方法でUserControl構成するControl必要があります。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:

  • フィールドとしてDependencyProperty名前をValueProperty付けるpublic``static``readonly識別子を定義します。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. メタデータには、プロパティの既定値 、およびCoerceValueCallbacka が含PropertyChangedCallbackまれています。The metadata contains the property's default value, a CoerceValueCallback and a PropertyChangedCallback.

  • プロパティgetsetアクセサーを実装Valueすることによって、依存関係プロパティの登録に使用される名前の CLR ラッパー プロパティを定義します。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. クライアントと呼び出WPFWPFGetValueしをバイパスしてSetValue直接実行できるため、依存関係プロパティのアクセサーには追加のロジックを含めないことをお勧めします。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.

  • 名前付きCoerceValueCoerceValueCallbackメソッドを定義します。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. OnValueChangedオブジェクトをRoutedPropertyChangedEventArgs<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 =
    DependencyProperty.Register(
        "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);
    control.OnValueChanged(e);
}
''' <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
    Get
        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)
    control.OnValueChanged(e)
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に含まれているため)、アプリケーション開発者は コントロールのイベントのイベント ハンドラを . ListBoxFor 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で使用できます。 XAMLXAMLRouted 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:

  • フィールドとしてRoutedEvent名前をValueChangedEvent付けるpublic``static``readonly識別子を定義します。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.

    • イベント ハンドラの型は、RoutedPropertyChangedEventHandler<T>型で構築されますDecimalThe 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. この例ではAddHandleraddアクセサー宣言とRemoveHandlerアクセサーremove宣言を呼び出WPFWPFして、イベント サービスを使用します。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)
{
    RaiseEvent(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))
    MyBase.RaiseEvent(args)
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参照するコードが 変更されると、参照先の要素ControlTemplateControlTemplateを new 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.

次の例では、TextBlockコントロールのNumericUpDownを更新し、名前を割り当て、コード内でテキスト ボックスを名前で参照します。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"/>
</Border>
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-->
    <TextBlock 
        Width="60" TextAlignment="Right" Padding="5"
        Text="{Binding RelativeSource={RelativeSource FindAncestor, 
                       AncestorType={x:Type local:NumericUpDown}}, 
                       Path=Value}"/>

</Border>

データ バインディングの詳細については、「データ バインディングの概要」を参照してください。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

CLRgetsetアクセサーは、前述の「依存関係プロパティの使用」で説明したように実装してください。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:

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

  • SetPropertyName および GetPropertyName という名前の 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. また、SetPropertyName メソッドでは、プロパティの登録データ型と同じ型の引数も受け取ります。The SetPropertyName method also accepts an argument whose type matches the registered data type for the property. GetPropertyNameメソッドでは、同じ型の値を返す必要があります。The GetPropertyName method should return a value of the same type. SetPropertyNameメソッドがない場合、プロパティは読み取り専用としてマークされます。If the SetPropertyName method is missing, the property is marked read-only.

  • SetPropertyName 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.xaml という名前のLinearGradientBrushリソース ファイルを作成します。The following example creates a LinearGradientBrush resource file named Dictionary1.xaml.

<ResourceDictionary 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
  <LinearGradientBrush 
    x:Key="myBrush"  
    StartPoint="0,0" EndPoint="1,1">
    <GradientStop Color="Red" Offset="0.25" />
    <GradientStop Color="Blue" Offset="0.75" />
  </LinearGradientBrush>
  
</ResourceDictionary>

ディクショナリを定義したら、それをコントロールのリソース ディクショナリにマージする必要があります。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.

<UserControl.Resources>
    <ResourceDictionary>
        <ResourceDictionary.MergedDictionaries>
            <ResourceDictionary Source="Dictionary1.xaml"/>
        </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>
</UserControl.Resources>

この方法の欠点は、オブジェクトを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
    {
        get
        {
            if (_sharedDictionary == null)
            {
                System.Uri resourceLocater =
                    new System.Uri("/ElementResourcesCustomControlLibrary;component/Dictionary1.xaml",
                                    System.UriKind.Relative);

                _sharedDictionary =
                    (ResourceDictionary)Application.LoadComponent(resourceLocater);
            }

            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一度だけ作成されます。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()
{
    this.Resources.MergedDictionaries.Add(SharedDictionaryManager.SharedDictionary);
    InitializeComponent();
}

テーマ レベルでのリソース定義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 の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.

テーマと UI オートメーション のサポート サンプルを含むC#またはVisual Basic NumericUpDownNumericUpDownカスタム コントロールには、コントロール用の 2 つのリソース ディクショナリが含まれています。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ディクショナリ ファイルのいずれかに を配置する場合は、次の例に示すように、コントロールの静的コンストラクターをOverrideMetadata(Type, PropertyMetadata)作成しDefaultStyleKey、 でメソッドを呼び出す必要があります。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()
{
    DefaultStyleKeyProperty.OverrideMetadata(typeof(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.

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

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

<RepeatButton 
    Grid.Column="1" Grid.Row="0"
    Background="{StaticResource {ComponentResourceKey 
                        TypeInTargetAssembly={x:Type local:NumericUpDown}, 
                        ResourceId=ButtonBrush}}">
    Up
</RepeatButton>
<RepeatButton 
    Grid.Column="1" Grid.Row="1"
    Background="{StaticResource {ComponentResourceKey 
                    TypeInTargetAssembly={x:Type local:NumericUpDown}, 
                    ResourceId=ButtonBrush}}">
    Down
 </RepeatButton>
テーマ リソースの場所の指定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.

プロパティGenericDictionaryLocationThemeDictionaryLocationプロパティをに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,
           ResourceDictionaryLocation.SourceAssembly)]
<Assembly: ThemeInfo(ResourceDictionaryLocation.SourceAssembly, ResourceDictionaryLocation.SourceAssembly)>

関連項目See also