Información general sobre la creación de controlesControl Authoring Overview

La extensibilidad del modelo de control Windows Presentation Foundation (WPF)Windows Presentation Foundation (WPF) reduce enormemente la necesidad de crear un nuevo control.The extensibility of the Windows Presentation Foundation (WPF)Windows Presentation Foundation (WPF) control model greatly reduces the need to create a new control. Sin embargo, en ciertos casos, puede que necesite crear un control personalizado.However, in certain cases you may still need to create a custom control. En este tema se describen las características que reducen la necesidad de crear un control personalizado y los diferentes modelos de creación de controles en 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). En este tema también se muestra cómo crear un nuevo control.This topic also demonstrates how to create a new control.

Alternativas a la escritura de un nuevo controlAlternatives to Writing a New Control

Históricamente, si quería obtener una experiencia personalizada con un control existente, estaba limitado a cambiar las propiedades estándar del control, como el color de fondo, el ancho del borde y el tamaño de la fuente.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. Si se deseara extender la apariencia o el comportamiento de un control más allá de estos parámetros predefinidos, necesitaría crear un nuevo control; para ello, lo que haría es heredarlo de un control existente e invalidar el método responsable de dibujar el control.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. Aunque esto sigue siendo una opción, WPFWPF permite personalizar los controles existentes mediante su modelo de contenido enriquecido, sus estilos, plantillas y desencadenadores.Although that is still an option, WPFWPF enables to you customize existing controls by using its rich content model, styles, templates, and triggers. En la lista siguiente se proporcionan ejemplos de cómo se pueden usar estas características para crear experiencias personalizadas y coherentes sin tener que crear un nuevo control.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.

  • Contenido enriquecido.Rich Content. Muchos de los controles WPFWPF estándar admiten contenido enriquecido.Many of the standard WPFWPF controls support rich content. Por ejemplo, la propiedad de contenido de un Button es de tipo Object, de modo que, en teoría, se puede mostrar cualquier cosa en un Button.For example, the content property of a Button is of type Object, so theoretically anything can be displayed on a Button. Para que un botón muestre una imagen y texto, puede Agregar una imagen y un TextBlock a un StackPanel y asignar el StackPanel a la propiedad Content.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. Dado que los controles pueden mostrar elementos visuales WPFWPF y datos arbitrarios, no es tan necesario crear un nuevo control o modificar un control existente para admitir una visualización compleja.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. Para obtener más información sobre el modelo de contenido de Button y otros modelos de contenido en WPFWPF, vea modelo de contenido de WPF.For more information about the content model for Button and other content models in WPFWPF, see WPF Content Model.

  • Estilos.Styles. Un Style es una colección de valores que representan las propiedades de un control.A Style is a collection of values that represent properties for a control. Mediante el uso de estilos, puede crear una representación reutilizable de la apariencia y el comportamiento deseados de un control sin necesidad de escribir un nuevo control.By using styles, you can create a reusable representation of a desired control appearance and behavior without writing a new control. Por ejemplo, supongamos que desea que todos los controles de TextBlock tengan una fuente Arial roja con un tamaño de fuente de 14.For example, assume that you want all of your TextBlock controls to have red, Arial font with a font size of 14. Puede crear un estilo como un recurso y establecer las propiedades adecuadas según corresponda.You can create a style as a resource and set the appropriate properties accordingly. Cada TextBlock que agregue a la aplicación tendrá la misma apariencia.Then every TextBlock that you add to your application will have the same appearance.

  • Plantillas de datos.Data Templates. Un DataTemplate permite personalizar el modo en que se muestran los datos en un control.A DataTemplate enables you to customize how data is displayed on a control. Por ejemplo, un DataTemplate se puede usar para especificar cómo se muestran los datos en un ListBox.For example, a DataTemplate can be used to specify how data is displayed in a ListBox. Para ver un ejemplo de esto, consulte Data Templating Overview (Introducción a las plantillas de datos).For an example of this, see Data Templating Overview. Además de personalizar la apariencia de los datos, una DataTemplate puede incluir elementos de interfaz de usuario, lo que proporciona una gran flexibilidad en las interfaces de usuario personalizadas.In addition to customizing the appearance of data, a DataTemplate can include UI elements, which gives you a lot of flexibility in custom UIs. Por ejemplo, mediante el uso de un DataTemplate, puede crear una ComboBox en la que cada elemento contiene una casilla.For example, by using a DataTemplate, you can create a ComboBox in which each item contains a check box.

  • Plantillas de control.Control Templates. Muchos controles de WPFWPF utilizan un ControlTemplate para definir la estructura y la apariencia del control, lo que separa la apariencia de un control de la funcionalidad del control.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. Puede cambiar drásticamente la apariencia de un control redefiniendo su ControlTemplate.You can drastically change the appearance of a control by redefining its ControlTemplate. Por ejemplo, supongamos que desea un control que se parezca a un semáforo.For example, suppose you want a control that looks like a stoplight. Este control tiene una interfaz de usuario y una funcionalidad sencillas.This control has a simple user interface and functionality. El control son tres círculos, y solo uno se puede iluminar cada vez.The control is three circles, only one of which can be lit up at a time. Después de la reflexión, es posible que se dé cuenta de que una RadioButton ofrece la funcionalidad de solo una que se selecciona a la vez, pero la apariencia predeterminada del RadioButton no tiene ningún aspecto como las luces de una luz.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. Dado que el RadioButton usa una plantilla de control para definir su apariencia, es fácil volver a definir el ControlTemplate para ajustarse a los requisitos del control y usar botones de radio para crear el semáforo.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.

    Nota

    Aunque un RadioButton puede utilizar un DataTemplate, un DataTemplate no es suficiente en este ejemplo.Although a RadioButton can use a DataTemplate, a DataTemplate is not sufficient in this example. El DataTemplate define la apariencia del contenido de un control.The DataTemplate defines the appearance of the content of a control. En el caso de una RadioButton, el contenido es lo que aparece a la derecha del círculo que indica si está seleccionado el 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. En el ejemplo del semáforo, el botón de selección debe ser un círculo que pueda "encenderse".In the example of the stoplight, the radio button needs just be a circle that can "light up." Dado que el requisito de apariencia del semáforo es tan distinto al de la apariencia predeterminada del RadioButton, es necesario volver a definir el 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. En general, un DataTemplate se usa para definir el contenido (o los datos) de un control, y se utiliza un ControlTemplate para definir cómo se estructura un control.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.

  • Desencadenadores.Triggers. Un Trigger permite cambiar dinámicamente la apariencia y el comportamiento de un control sin necesidad de crear un nuevo control.A Trigger allows you to dynamically change the appearance and behavior of a control without creating a new control. Por ejemplo, supongamos que tiene varios controles ListBox en la aplicación y desea que los elementos de cada ListBox estén en negrita y en rojo cuando se seleccionan.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. La primera instinto podría ser crear una clase que herede de ListBox e invalidar el método OnSelectionChanged para cambiar la apariencia del elemento seleccionado, pero un enfoque mejor es agregar un desencadenador a un estilo de un ListBoxItem que cambie la apariencia del elemento seleccionado.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. Un desencadenador permite cambiar los valores de propiedad o realizar acciones según el valor de una propiedad.A trigger enables you to change property values or take actions based on the value of a property. Una EventTrigger permite realizar acciones cuando se produce un evento.An EventTrigger enables you to take actions when an event occurs.

Para más información sobre los estilos, las plantillas y los desencadenadores, consulte Aplicar estilos y plantillas.For more information about styles, templates, and triggers, see Styling and Templating.

En general, si el control refleja la funcionalidad de un control existente, pero quiere que el control tenga un aspecto diferente, primero debe considerar si puede usar cualquiera de los métodos descritos en esta sección para cambiar la apariencia del control existente.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.

Modelos para crear controlesModels for Control Authoring

El modelo de contenido enriquecido, los estilos, las plantillas y los desencadenadores reducen la necesidad de crear un nuevo control.The rich content model, styles, templates, and triggers minimize the need for you to create a new control. Sin embargo, si necesita crear un nuevo control, es importante comprender los diferentes modelos de creación de controles de WPFWPF.However, if you do need to create a new control, it is important to understand the different control authoring models in WPFWPF. WPFWPF proporciona tres modelos generales para la creación de un control, cada uno de los cuales proporciona un conjunto de características y un nivel de flexibilidad diferentes.provides three general models for creating a control, each of which provides a different set of features and level of flexibility. Las clases base para los tres modelos son UserControl, Controly FrameworkElement.The base classes for the three models are UserControl, Control, and FrameworkElement.

Derivación de UserControlDeriving from UserControl

La manera más sencilla de crear un control en WPFWPF es derivar de UserControl.The simplest way to create a control in WPFWPF is to derive from UserControl. Al compilar un control que hereda de UserControl, se agregan los componentes existentes al UserControl, se denominan los componentes y se hace referencia a los controladores de eventos en Lenguaje XAML (Extensible Application Markup Language)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 Lenguaje XAML (Extensible Application Markup Language)Extensible Application Markup Language (XAML). Luego puede hacer referencia a los elementos con nombre y definir los controladores de eventos en el código.You can then reference the named elements and define the event handlers in code. Este modelo de desarrollo es muy similar al modelo utilizado para el desarrollo de aplicaciones en WPFWPF.This development model is very similar to the model used for application development in WPFWPF.

Si se compila correctamente, un UserControl puede aprovechar las ventajas de los desencadenadores, los estilos y el contenido enriquecido.If built correctly, a UserControl can take advantage of the benefits of rich content, styles, and triggers. Sin embargo, si el control hereda de UserControl, las personas que usen el control no podrán usar un DataTemplate o ControlTemplate para personalizar su apariencia.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. Es necesario derivar de la clase Control o de una de sus clases derivadas (que no sea UserControl) para crear un control personalizado que admita plantillas.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.

Ventajas de derivar de UserControlBenefits of Deriving from UserControl

Considere la posibilidad de derivar de UserControl si se aplica lo siguiente:Consider deriving from UserControl if all of the following apply:

  • Desea crear el control de forma similar a cómo crea una aplicación.You want to build your control similarly to how you build an application.

  • El control solo consta de componentes existentes.Your control consists only of existing components.

  • No se necesita personalización compleja.You don't need to support complex customization.

Derivación de ControlDeriving from Control

La derivación de la clase Control es el modelo usado por la mayoría de los controles de WPFWPF existentes.Deriving from the Control class is the model used by most of the existing WPFWPF controls. Cuando se crea un control que hereda de la clase Control, se define su apariencia mediante el uso de plantillas.When you create a control that inherits from the Control class, you define its appearance by using templates. Al hacerlo, se separa la lógica de funcionamiento de la representación visual.By doing so, you separate the operational logic from the visual representation. También puede garantizar el desacoplamiento de la interfaz de usuario y la lógica usando comandos y enlaces en lugar de eventos y evitando que se haga referencia a los elementos de la ControlTemplate siempre que sea posible.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. Si la interfaz de usuario y la lógica del control están desacopladas correctamente, un usuario del control puede volver a definir el ControlTemplate del control para personalizar su apariencia.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. Aunque compilar un Control personalizado no es tan sencillo como generar un UserControl, un Control personalizado proporciona la máxima flexibilidad.Although building a custom Control is not as simple as building a UserControl, a custom Control provides the most flexibility.

Ventajas de derivar de ControlBenefits of Deriving from Control

Considere la posibilidad de derivar de Control en lugar de usar la clase UserControl si se aplica alguna de las siguientes condiciones:Consider deriving from Control instead of using the UserControl class if any of the following apply:

  • Quiere que la apariencia del control se pueda personalizar mediante el ControlTemplate.You want the appearance of your control to be customizable via the ControlTemplate.

  • Desea que el control admita distintos temas.You want your control to support different themes.

Derivación de FrameworkElementDeriving from FrameworkElement

Los controles que derivan de UserControl o Control confían en la composición de elementos existentes.Controls that derive from UserControl or Control rely upon composing existing elements. En muchos escenarios, se trata de una solución aceptable, ya que cualquier objeto que herede de FrameworkElement puede estar en un ControlTemplate.For many scenarios, this is an acceptable solution, because any object that inherits from FrameworkElement can be in a ControlTemplate. Sin embargo, en ocasiones la apariencia de un control requiere una funcionalidad que va más allá de la simple composición de elementos.However, there are times when a control's appearance requires more than the functionality of simple element composition. En estos casos, basar un componente en FrameworkElement es la opción correcta.For these scenarios, basing a component on FrameworkElement is the right choice.

Existen dos métodos estándar para compilar componentes basados en FrameworkElement: representación directa y composición de elementos personalizada.There are two standard methods for building FrameworkElement-based components: direct rendering and custom element composition. La representación directa implica invalidar el método de OnRender de FrameworkElement y proporcionar operaciones de DrawingContext que definen explícitamente los objetos visuales del componente.Direct rendering involves overriding the OnRender method of FrameworkElement and providing DrawingContext operations that explicitly define the component visuals. Este es el método utilizado por Image y Border.This is the method used by Image and Border. La composición de elementos personalizados implica el uso de objetos de tipo Visual para crear la apariencia del componente.Custom element composition involves using objects of type Visual to compose the appearance of your component. Para ver un ejemplo, consulte Using DrawingVisual Objects (Uso de objetos DrawingVisual).For an example, see Using DrawingVisual Objects. Track es un ejemplo de un control en WPFWPF que usa la composición de elementos personalizada.Track is an example of a control in WPFWPF that uses custom element composition. También es posible mezclar la representación directa y la composición de elementos personalizada en el mismo control.It is also possible to mix direct rendering and custom element composition in the same control.

Ventajas de derivar de FrameworkElementBenefits of Deriving from FrameworkElement

Considere la posibilidad de derivar de FrameworkElement si se aplica alguna de las siguientes condiciones:Consider deriving from FrameworkElement if any of the following apply:

  • Desea tener un control preciso sobre la apariencia del control más allá de lo que proporciona la simple composición de elementos.You want to have precise control over the appearance of your control beyond what is provided by simple element composition.

  • Desea definir el aspecto del control definiendo una lógica de representación propia.You want to define the appearance of your control by defining your own render logic.

  • Quiere crear elementos existentes de maneras novedosas que van más allá de lo que es posible con UserControl y Control.You want to compose existing elements in novel ways that go beyond what is possible with UserControl and Control.

Conceptos básicos de creación de controlesControl Authoring Basics

Como se comentó anteriormente, una de las características más eficaces de WPFWPF es la posibilidad de no tener que limitarse a establecer las propiedades básicas de un control para modificar su apariencia y comportamiento, sin estar obligado a crear un control personalizado.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. Las características de estilo, enlace de datos y desencadenadores son posibles gracias al sistema de propiedades de WPFWPF y el sistema de eventos WPFWPF.The styling, data binding, and trigger features are made possible by the WPFWPF property system and the WPFWPF event system. En las próximas secciones se describen algunos procedimientos que debe seguir, independientemente del modelo que emplee para crear el control personalizado, de modo que los usuarios de su control personalizado puedan usar estas características como lo harían para un control incluido con 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.

Uso de propiedades de dependenciaUse Dependency Properties

Cuando una propiedad es de dependencia, es posible realizar las acciones siguientes:When a property is a dependency property, it is possible to do the following:

  • Establecer la propiedad en un estilo.Set the property in a style.

  • Enlazar la propiedad a un origen de datos.Bind the property to a data source.

  • Utilizar un recurso dinámico como valor de la propiedad.Use a dynamic resource as the property's value.

  • Animar la propiedad.Animate the property.

Si desea que una propiedad del control admita esta funcionalidad, debe implementarla como una propiedad de dependencia.If you want a property of your control to support any of this functionality, you should implement it as a dependency property. En el ejemplo siguiente se define una propiedad de dependencia denominada Value mediante este procedimiento:The following example defines a dependency property named Value by doing the following:

  • Defina un identificador de DependencyProperty denominado ValueProperty como un public static campo readonly.Define a DependencyProperty identifier named ValueProperty as a public static readonly field.

  • Registre el nombre de la propiedad con el sistema de propiedades, llamando DependencyProperty.Register, para especificar lo siguiente:Register the property name with the property system, by calling DependencyProperty.Register, to specify the following:

    • Nombre de la propiedad.The name of the property.

    • Tipo de la propiedad.The type of the property.

    • El tipo al que pertenece la propiedad.The type that owns the property.

    • Los metadatos de la propiedad.The metadata for the property. Los metadatos contienen el valor predeterminado de la propiedad, un CoerceValueCallback y un PropertyChangedCallback.The metadata contains the property's default value, a CoerceValueCallback and a PropertyChangedCallback.

  • Defina una propiedad de contenedor CLR denominada Value, que es el mismo nombre que se usa para registrar la propiedad de dependencia, implementando los descriptores de acceso get y set de la propiedad.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. Tenga en cuenta que los descriptores de acceso get y set solo llaman a GetValue y SetValue respectivamente.Note that the get and set accessors only call GetValue and SetValue respectively. Se recomienda que los descriptores de acceso de las propiedades de dependencia no contengan lógica adicional, ya que los clientes y WPFWPF pueden omitir los descriptores de acceso y llamar a GetValue y SetValue directamente.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. Por ejemplo, cuando una propiedad está enlazada a un origen de datos, no se llama al descriptor de acceso set.For example, when a property is bound to a data source, the property's set accessor is not called. En lugar de agregar lógica adicional a los descriptores de acceso get y Set, utilice los delegados ValidateValueCallback, CoerceValueCallbacky PropertyChangedCallback para responder al valor o comprobarlo cuando cambie.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. Para más información sobre estas devoluciones de llamada, consulte Devoluciones de llamada y validación de las propiedades de dependencia.For more information on these callbacks, see Dependency Property Callbacks and Validation.

  • Defina un método para el CoerceValueCallback denominado CoerceValue.Define a method for the CoerceValueCallback named CoerceValue. CoerceValue garantiza que Value es mayor o igual que MinValue y menor o igual que MaxValue.CoerceValue ensures that Value is greater or equal to MinValue and less than or equal to MaxValue.

  • Defina un método para el PropertyChangedCallback, denominado OnValueChanged.Define a method for the PropertyChangedCallback, named OnValueChanged. OnValueChanged crea un objeto RoutedPropertyChangedEventArgs<T> y se prepara para generar el evento enrutado ValueChanged.OnValueChanged creates a RoutedPropertyChangedEventArgs<T> object and prepares to raise the ValueChanged routed event. Los eventos enrutados se abordan en la sección siguiente.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

Para obtener más información, vea Propiedades de dependencia personalizadas.For more information, see Custom Dependency Properties.

Uso de eventos enrutadosUse Routed Events

Del mismo modo que las propiedades de dependencia extienden la noción de propiedades CLR con funcionalidad adicional, los eventos enrutados amplían la noción de eventos CLR estándar.Just as dependency properties extend the notion of CLR properties with additional functionality, routed events extend the notion of standard CLR events. Cuando se crea un nuevo control WPFWPF, también es conveniente implementar el evento como enrutado, porque un evento enrutado admite el comportamiento siguiente: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:

  • Los eventos se pueden controlar en un elemento primario de varios controles.Events can be handled on a parent of multiple controls. Si un evento es de propagación, puede suscribirse a él un elemento primario único del árbol de elementos.If an event is a bubbling event, a single parent in the element tree can subscribe to the event. A continuación, los autores de la aplicación pueden utilizar un mismo controlador para responder al evento de varios controles.Then application authors can use one handler to respond to the event of multiple controls. Por ejemplo, si el control forma parte de cada elemento de una ListBox (porque está incluido en un DataTemplate), el desarrollador de la aplicación puede definir el controlador de eventos para el evento del control en el 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. Cada vez que se produzca el evento en cualquiera de los controles, se llamará al controlador de eventos.Whenever the event occurs on any of the controls, the event handler is called.

  • Los eventos enrutados se pueden utilizar en un EventSetter, lo que permite a los desarrolladores de aplicaciones especificar el controlador de un evento dentro de un estilo.Routed events can be used in an EventSetter, which enables application developers to specify the handler of an event within a style.

  • Los eventos enrutados se pueden utilizar en una EventTrigger, lo que resulta útil para animar propiedades mediante XAMLXAML.Routed events can be used in an EventTrigger, which is useful for animating properties by using XAMLXAML. Para obtener más información, consulte Información general sobre animaciones.For more information, see Animation Overview.

En el ejemplo siguiente se define un evento enrutado mediante este procedimiento:The following example defines a routed event by doing the following:

  • Defina un identificador de RoutedEvent denominado ValueChangedEvent como un public static campo readonly.Define a RoutedEvent identifier named ValueChangedEvent as a public static readonly field.

  • Registre el evento enrutado llamando al método EventManager.RegisterRoutedEvent.Register the routed event by calling the EventManager.RegisterRoutedEvent method. En el ejemplo se especifica la siguiente información cuando llama a RegisterRoutedEvent:The example specifies the following information when it calls RegisterRoutedEvent:

    • El nombre del evento es ValueChanged.The name of the event is ValueChanged.

    • La estrategia de enrutamiento es Bubble, lo que significa que primero se llama a un controlador de eventos en el origen (el objeto que genera el evento) y, a continuación, se llama a los controladores de eventos en los elementos primarios del origen sucesivamente, empezando por el controlador de eventos en el elemento primario.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.

    • El tipo del controlador de eventos es RoutedPropertyChangedEventHandler<T>, construido con un tipo de Decimal.The type of the event handler is RoutedPropertyChangedEventHandler<T>, constructed with a Decimal type.

    • El tipo de propiedad del evento es NumericUpDown.The owning type of the event is NumericUpDown.

  • Declare un evento público denominado ValueChanged e incluya declaraciones de descriptores de acceso del evento.Declare a public event named ValueChanged and includes event-accessor declarations. En el ejemplo se llama a AddHandler en la declaración del descriptor de acceso add y RemoveHandler en la declaración del descriptor de acceso remove para usar los servicios de WPFWPF Event.The example calls AddHandler in the add accessor declaration and RemoveHandler in the remove accessor declaration to use the WPFWPF event services.

  • Cree un método virtual protegido denominado OnValueChanged que genere el evento ValueChanged.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

Para más información, consulte Routed Events Overview (Introducción a los eventos enrutados) y Create a Custom Routed Event (Creación de un evento enrutado personalizado).For more information, see Routed Events Overview and Create a Custom Routed Event.

Uso del enlaceUse Binding

Para desacoplar la interfaz de usuario del control de su lógica, puede ser conveniente utilizar el enlace de datos.To decouple the UI of your control from its logic, consider using data binding. Esto es especialmente importante si define la apariencia del control mediante una ControlTemplate.This is particularly important if you define the appearance of your control by using a ControlTemplate. Al utilizar el enlace de datos, puede que consiga eliminar la necesidad de hacer referencia a partes concretas de la interfaz de usuario desde el código.When you use data binding, you might be able to eliminate the need to reference specific parts of the UI from the code. Es una buena idea evitar hacer referencia a los elementos que se encuentran en el ControlTemplate porque cuando el código hace referencia a elementos que se encuentran en el ControlTemplate y se cambia la ControlTemplate, el elemento al que se hace referencia debe incluirse en el nuevo 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.

En el ejemplo siguiente se actualiza el TextBlock del control NumericUpDown, se le asigna un nombre y se hace referencia al cuadro de texto por su nombre en el código.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

En el ejemplo siguiente se usa el enlace para lograr lo mismo.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>

Para más información sobre el enlace de datos, consulte Información general sobre el enlace de datos.For more information about data binding, see Data Binding Overview.

Diseño para diseñadoresDesign for Designers

Para recibir compatibilidad con los controles personalizados de WPF en WPF Designer para Visual Studio (por ejemplo, la edición de propiedades con el ventana Propiedades), siga estas instrucciones.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. Para obtener más información sobre el desarrollo de WPF Designer, consulte diseño de XAML en Visual Studio.For more information on developing for the WPF Designer, see Design XAML in Visual Studio.

Propiedades de dependenciaDependency Properties

Asegúrese de implementar los descriptores de acceso get y set de CLR tal y como se describió anteriormente, en "usar propiedades de dependencia".Be sure to implement CLR get and set accessors as described earlier, in "Use Dependency Properties." Los diseñadores pueden utilizar el contenedor para detectar la presencia de una propiedad de dependencia, pero no se les exige, al igual que a WPFWPF y a los clientes del control, llamar a los descriptores de acceso al obtener o establecer la propiedad.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.

Propiedades asociadasAttached Properties

Para implementar propiedades adjuntas en controles personalizados, es recomendable que utilice las siguientes instrucciones:You should implement attached properties on custom controls using the following guidelines:

  • Tenga un public static readonly DependencyProperty del formulario PropertyNameProperty que se creó con el método RegisterAttached.Have a public static readonly DependencyProperty of the form PropertyNameProperty that was creating using the RegisterAttached method. El nombre de propiedad que se pasa a RegisterAttached debe coincidir con PropertyName.The property name that is passed to RegisterAttached must match PropertyName.

  • Implemente un par de métodos CLR public static denominados SetNombreDePropiedad y GetNombreDePropiedad.Implement a pair of public static CLR methods named SetPropertyName and GetPropertyName. Ambos métodos deben aceptar una clase derivada de DependencyProperty como primer argumento.Both methods should accept a class derived from DependencyProperty as their first argument. El método SetNombreDePropiedad también acepta un argumento cuyo tipo coincida con el tipo de datos registrado para la propiedad.The SetPropertyName method also accepts an argument whose type matches the registered data type for the property. El método Get NombreDePropiedad método debe devolver un valor del mismo tipo.The GetPropertyName method should return a value of the same type. Si falta el método SetNombreDePropiedad, la propiedad se marca como de solo lectura.If the SetPropertyName method is missing, the property is marked read-only.

  • Set PropertyName y GetPropertyName deben enrutar directamente a los métodos GetValue y SetValue en el objeto de dependencia de destino, respectivamente.Set PropertyName and GetPropertyName must route directly to the GetValue and SetValue methods on the target dependency object, respectively. Los diseñadores pueden tener acceso a la propiedad adjunta mediante una llamada a través del contenedor de método o una llamada directa al objeto de dependencia de destino.Designers may access the attached property by calling through the method wrapper or making a direct call to the target dependency object.

Para más información sobre las propiedades adjuntas, consulte Attached Properties Overview (Introducción a las propiedades adjuntas).For more information on attached properties, see Attached Properties Overview.

Definición y uso de recursos compartidosDefine and Use Shared Resources

Puede incluir el control en el mismo ensamblado que la aplicación o bien empaquetarlo en un ensamblado independiente que se pueda utilizar en varias aplicaciones.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. En general, la información analizada en este tema es aplicable independientemente del método que se utilice.For the most part, the information discussed in this topic applies regardless of the method you use. Sin embargo, hay una diferencia que vale la pena tener en cuenta.There is one difference worth noting, however. Al incluir un control en el mismo ensamblado que una aplicación, puede agregar recursos globales al archivo 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. Pero un ensamblado que solo contiene controles no tiene un objeto Application asociado, por lo que no hay disponible ningún archivo 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.

Cuando una aplicación busca un recurso, la búsqueda se realiza en tres niveles en el orden que se indica a continuación:When an application looks for a resource, it looks at three levels in the following order:

  1. El nivel de elemento.The element level.

    El sistema empieza por el elemento que hace referencia al recurso y, a continuación, busca en los recursos del elemento primario lógico y así sucesivamente hasta que se alcanza el elemento raíz.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. El nivel de aplicación.The application level.

    Recursos definidos por el objeto Application.Resources defined by the Application object.

  3. El nivel de tema.The theme level.

    Los diccionarios del nivel de tema se almacenan en una subcarpeta denominada Temas.Theme-level dictionaries are stored in a subfolder named Themes. Los archivos de la carpeta Temas corresponden a los temas.The files in the Themes folder correspond to themes. Por ejemplo, podría tener Aero.NormalColor.xaml, Luna.NormalColor.xaml, Royale.NormalColor.xaml, etc.For example, you might have Aero.NormalColor.xaml, Luna.NormalColor.xaml, Royale.NormalColor.xaml, and so on. También puede tener un archivo denominado generic.xaml.You can also have a file named generic.xaml. Cuando el sistema busca un recurso en el nivel de temas, primero lo busca en el archivo específico del tema y, a continuación, lo busca en 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.

Cuando el control está en un ensamblado independiente de la aplicación, debe colocar los recursos globales en el nivel de elemento o en el nivel de tema.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. Ambos métodos tienen sus ventajas.Both methods have their advantages.

Definición de los recursos en el nivel de elementoDefining Resources at the Element Level

Puede definir los recursos compartidos en el nivel de elemento mediante la creación de un diccionario de recursos personalizado y combinarlo con el diccionario de recursos del control.You can define shared resources at the element level by creating a custom resource dictionary and merging it with your control’s resource dictionary. Cuando utiliza este método, puede nombrar el archivo de recursos que desee y este puede estar en la misma carpeta que los controles.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. Los recursos en el nivel de elemento también pueden utilizar cadenas simples como claves.Resources at the element level can also use simple strings as keys. En el ejemplo siguiente se crea una LinearGradientBrush archivo de recursos denominado Dictionary1. Xaml.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>

Una vez definido el diccionario, debe combinarlo con el diccionario de recursos del control.Once you have defined your dictionary, you need to merge it with your control's resource dictionary. Para ello, utilice XAMLXAML o código.You can do this by using XAMLXAML or code.

En el ejemplo siguiente se combina un diccionario de recursos mediante 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>

El inconveniente de este enfoque es que se crea un objeto ResourceDictionary cada vez que se hace referencia a él.The disadvantage to this approach is that a ResourceDictionary object is created each time you reference it. Por ejemplo, si tiene 10 controles personalizados en la biblioteca y combina los diccionarios de recursos compartidos de cada control mediante XAML, creará 10 objetos ResourceDictionary idénticos.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. Para evitar esto, cree una clase estática que combine los recursos en el código y devuelva el ResourceDictionaryresultante.You can avoid this by creating a static class that merges the resources in code and returns the resulting ResourceDictionary.

En el ejemplo siguiente se crea una clase que devuelve un ResourceDictionarycompartido.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;
}

En el ejemplo siguiente se combina el recurso compartido con los recursos de un control personalizado en el constructor del control antes de llamar a InitializeComponent.The following example merges the shared resource with the resources of a custom control in the control's constructor before it calls InitializeComponent. Dado que el SharedDictionaryManager.SharedDictionary es una propiedad estática, el ResourceDictionary se crea solo una vez.Because the SharedDictionaryManager.SharedDictionary is a static property, the ResourceDictionary is created only once. Como el diccionario de recursos se combinó antes de llamar a InitializeComponent, los recursos están disponibles para el control en su archivo 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();

}

Definición de recursos en el nivel de temaDefining Resources at the Theme Level

WPFWPF le permite crear recursos para distintos temas de Windows.enables you to create resources for different Windows themes. Como autor del control, puede definir un recurso para un tema concreto con el fin de cambiar la apariencia del control en función del tema que se emplee.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. Por ejemplo, la apariencia de una Button en el tema clásico de Windows (el tema predeterminado para Windows 2000) difiere de una Button en el tema luna de Windows (el tema predeterminado para Windows XP) porque el Button usa un ControlTemplate diferente para cada tema.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.

Los recursos específicos de un tema se mantienen en un diccionario de recursos con un nombre de archivo concreto.Resources that are specific to a theme are kept in a resource dictionary with a specific file name. Estos archivos deben estar en una carpeta denominada Themes que es una subcarpeta de la carpeta que contiene el control.These files must be in a folder named Themes that is a subfolder of the folder that contains the control. En la tabla siguiente se enumeran los archivos de diccionario de recursos y el tema que está asociado a cada archivo:The following table lists the resource dictionary files and the theme that is associated with each file:

Nombre de archivo de diccionario de recursosResource dictionary file name Tema de WindowsWindows theme
Classic.xaml Apariencia clásica de Windows 9x/2000 en Windows XPClassic Windows 9x/2000 look on Windows XP
Luna.NormalColor.xaml Tema azul predeterminado en Windows XPDefault blue theme on Windows XP
Luna.Homestead.xaml Tema verde olivo en Windows XPOlive theme on Windows XP
Luna.Metallic.xaml Tema plateado en Windows XPSilver theme on Windows XP
Royale.NormalColor.xaml Tema predeterminado en Windows XP Media Center EditionDefault theme on Windows XP Media Center Edition
Aero.NormalColor.xaml Tema predeterminado en Windows VistaDefault theme on Windows Vista

No es necesario definir un recurso para cada tema.You do not need to define a resource for every theme. Si no se ha definido un recurso para un tema concreto, el control comprueba Classic.xaml para el recurso.If a resource is not defined for a specific theme, then the control checks Classic.xaml for the resource. Si no se ha definido el recurso en el archivo correspondiente al tema actual o en Classic.xaml, el control utiliza el recurso genérico, que está en un archivo de diccionario de recursos denominado 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. El archivo generic.xaml se encuentra en la misma carpeta que los archivos de diccionario de recursos específicos del tema.The generic.xaml file is located in the same folder as the theme-specific resource dictionary files. Aunque generic.xaml no corresponde a un tema específico de Windows, sigue siendo un diccionario de nivel de tema.Although generic.xaml does not correspond to a specific Windows theme, it is still a theme-level dictionary.

El C# ejemplo o Visual Basic control personalizado NumericUpDown con el tema y la compatibilidad con la automatización de la interfaz de usuario contiene dos diccionarios de recursos para el control NumericUpDown: uno está en Generic. XAML y el otro en luna. NormalColor. Xaml.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.

Al colocar un ControlTemplate en cualquiera de los archivos de Diccionario de recursos específicos del tema, debe crear un constructor estático para el control y llamar al método OverrideMetadata(Type, PropertyMetadata) en el DefaultStyleKey, como se muestra en el ejemplo siguiente.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
Definición de los recursos de tema y referencia a ellosDefining and Referencing Keys for Theme Resources

Al definir un recurso en el nivel de elemento, puede asignar una cadena como su clave y obtener acceso al recurso a través de la cadena.When you define a resource at the element level, you can assign a string as its key and access the resource via the string. Al definir un recurso en el nivel de tema, debe utilizar un ComponentResourceKey como clave.When you define a resource at the theme level, you must use a ComponentResourceKey as the key. En el ejemplo siguiente se define un recurso en 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>

En el ejemplo siguiente se hace referencia al recurso especificando el ComponentResourceKey como la clave.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>
Especificación de la ubicación de los recursos de temaSpecifying the Location of Theme Resources

Para buscar los recursos de un control, la aplicación host debe saber que el ensamblado contiene recursos específicos del control.To find the resources for a control, the hosting application needs to know that the assembly contains control-specific resources. Puede hacerlo agregando el ThemeInfoAttribute al ensamblado que contiene el control.You can accomplish that by adding the ThemeInfoAttribute to the assembly that contains the control. El ThemeInfoAttribute tiene una propiedad GenericDictionaryLocation que especifica la ubicación de los recursos genéricos y una propiedad ThemeDictionaryLocation que especifica la ubicación de los recursos específicos del tema.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.

En el ejemplo siguiente se establecen las propiedades GenericDictionaryLocation y ThemeDictionaryLocation en SourceAssembly, para especificar que los recursos genéricos y específicos del tema se encuentran en el mismo ensamblado que el control.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)>

Vea tambiénSee also