2009-03-13 22 views
7

he implementado un simple botón con una imagen en ella:Adjuntar ICommand en WPF control de usuario

<Button Command="{Binding ButtonCommand, ElementName=ImageButtonControl}"> 
     <StackPanel Orientation="Horizontal"> 
      <Image Source="{Binding ButtonImage, ElementName=ImageButtonControl}"/> 
      <TextBlock Text="{Binding ButtonText, ElementName=ImageButtonControl}" Margin="2,0,0,0"/> 
     </StackPanel> 
    </Button> 

Como se puede ver, expongo una propiedad ButtonCommand con el fin de poder adjunte un ICommand a este UserControl:

public partial class ImageButton : UserControl 
{ 
    /// <summary> 
    /// The dependency property that gets or sets the source of the image to render. 
    /// </summary> 
    public static DependencyProperty ImageSourceProperty = 
     DependencyProperty.Register("ButtonImage", typeof(ImageSource), typeof(ImageButton)); 

    public static DependencyProperty TextProperty = 
     DependencyProperty.Register("ButtonText", typeof(string), typeof(ImageButton)); 

    public static DependencyProperty ButtonCommandProperty = 
     DependencyProperty.Register("ButtonCommand", typeof(ICommand), typeof(ImageButton)); 

    public ImageButton() 
    { 
     this.DataContext = this; 
     InitializeComponent(); 
    } 

    /// <summary> 
    /// Gets or sets the button command. 
    /// </summary> 
    public ICommand ButtonCommand 
    { 
     get { return (ICommand)GetValue(ImageButton.ButtonCommandProperty); } 
     set { SetValue(ImageButton.ButtonCommandProperty, value); } 
    } 

    /// <summary> 
    /// Gets or sets the button image. 
    /// </summary> 
    public ImageSource ButtonImage 
    { 
     get { return (ImageSource)GetValue(ImageButton.ImageSourceProperty); } 
     set { SetValue(ImageButton.ImageSourceProperty, value); } 
    } 

    /// <summary> 
    /// Gets or sets the button text. 
    /// </summary> 
    public string ButtonText 
    { 
     get { return (string)GetValue(ImageButton.TextProperty); } 
     set { SetValue(ImageButton.TextProperty, value); } 
    } 
} 

Luego, cuando declaro mi botón, le doy esto :

<uc:ImageButton Grid.Row="1" Grid.Column="0" ButtonCommand="{Binding AttachContextCommand}" ButtonImage="{StaticResource AssociateImage}" ButtonText="Associer"/>

Y badaboom, nada nunca suceden cuando hago clic en mi ImageButton. Cuando reemplazo ImageButton con un botón simple, se llama a ICommand.

Incluso intenté simplemente extiende la clase Button y enlazar un ICommand, pero una vez más, no funcionó ...

Ayuda apreciada!

Thx.

+0

es lo que el botón está desactivado, o que haga clic en él y no pasa nada? –

+0

hago clic en él y el comando no se llama nunca ... – Roubachof

Respuesta

6

Puede lograr esto de una manera mucho más limpia utilizando un estilo y un par de propiedades adjuntas.

Las propiedades adjuntas almacenan su información específica. El estilo utilizará estas propiedades y creará el aspecto que desee.

El elemento seguirá siendo un botón para que el comando y todo lo demás funcione.

public class ImageButton 
    { 

     public static ImageSource GetImage(DependencyObject obj) 
     { 
      return (ImageSource)obj.GetValue(ImageProperty); 
     } 

     public static void SetImage(DependencyObject obj, ImageSource value) 
     { 
      obj.SetValue(ImageProperty, value); 
     } 

     public static readonly DependencyProperty ImageProperty = 
      DependencyProperty.RegisterAttached("Image", typeof(ImageSource), typeof(ImageButton), new UIPropertyMetadata(null)); 

     public static String GetCaption(DependencyObject obj) 
     { 
      return (String)obj.GetValue(CaptionProperty); 
     } 

     public static void SetCaption(DependencyObject obj, String value) 
     { 
      obj.SetValue(CaptionProperty, value); 
     } 

     public static readonly DependencyProperty CaptionProperty = 
      DependencyProperty.RegisterAttached("Caption", typeof(String), typeof(ImageButton), new UIPropertyMetadata(null)); 

    } 

<Style TargetType="{x:Type Button}" 
       x:Key="ImageButton"> 
      <Setter Property="ContentTemplate"> 
       <Setter.Value> 
        <DataTemplate> 
         <StackPanel Orientation="Horizontal"> 
          <Image Source="{Binding Path=(local:ImageButton.Image), RelativeSource={RelativeSource AncestorType={x:Type Button}}}" /> 
          <TextBlock Text="{Binding Path=(local:ImageButton.Caption), RelativeSource={RelativeSource AncestorType={x:Type Button}}}" 
             Margin="2,0,0,0" /> 
         </StackPanel> 
        </DataTemplate> 
       </Setter.Value> 
      </Setter> 
     </Style> 

entonces usted puede utilizar esto para declarar botones:

<Button Style="{DynamicResource ImageButton}" 
         local:ImageButton.Caption="Foo" 
         local:ImageButton.Image="..." /> 

Nota:

Estoy bastante seguro de que sería más limpio que pasar por la propiedad "plantilla" y utilizar un ControlTemplate y TemplateBindings, pero eso significaría volver a crear el borde y otras cosas en torno a su contenido, por lo que si está buscando simplemente definir un "Contenido" predeterminado, mi ejemplo sería el camino a seguir, creo.

+0

Me gusta mucho esta respuesta, también. Normalmente no pienso hacer propiedades adjuntas, pero son una característica increíble de WPF. – dustyburwell

+0

De hecho lo son. El hecho de que pueda adjuntar comportamiento a los elementos a través de propiedades adjuntas (como el manejo declarativo de eventos para cosas como Arrastrar y soltar) es incluso más asombroso cuando lo hace. –

+0

+1 Muy simple, flexible y reutilizable. Gracias – surfen

0

El botón WPF incorporado contiene un código que dispara el comando adjunto en respuesta a un clic. Su "ImageButton" se deriva de UserControl, por lo que no obtiene ese comportamiento. Probablemente, la ruta más corta para obtener lo que desea es que su clase ImageButton realmente se derive de la clase de botón WPF. Para lograr eso, cambiar el margen de beneficio para ImageButton de

<UserControl 
... 
> 
... 
</UserControl> 

a

<Button 
... 
> 
... 
</Button> 

A continuación, cambiar la clase base de ImageButton de UserControl a Button.

Es probable que deba hacer algunos cambios menores antes de que todo funcione.

+0

Hola Daniel, Yo a la verdad intentado extiende la clase de botón, pero el comando no se llama nunca bien ... – Roubachof

4

Si la única funcionalidad añadida que desea para su botón es tener una imagen en ella, entonces creo que se está acercando a esto desde la dirección incorrecta. WPF es tan impresionante como lo es porque los controles de la interfaz de usuario son sin fallas. Esto significa que un Control es simplemente una definición de funcionalidad + alguna plantilla para definir cómo se ve. Esto significa que la plantilla se puede intercambiar en cualquier momento para cambiar el aspecto. Además, casi cualquier contenido puede ser colocado dentro de casi cualquier control de

Por ejemplo, para definir un botón en su xaml que tenga la mirada su ir por todo lo que necesita es la siguiente:

<Window ...> 
    ... 
    <Button Command="{Binding AttachContextCommand}"> 
     <StackPanel Orientation="Horizontal"> 
      <Image Source="{StaticResource AssociateImage}"/> 
      <TextBlock Text="Associer"/> 
     </StackPanel> 
    </Button> 
    ... 
</Window> 

Hemos de tener en tenga en cuenta que con WPF no tiene que definir un nuevo CustomControl o UserControl cada vez que desee cambiar el aspecto de un objeto. La única vez que debe necesitar un CustomControl es si desea agregar funcionalidad a un Control existente o crear una funcionalidad que no existe en ningún otro Control.

edición debido a comentar:

Si tienes intención de evitar que se define el contenido del botón cada vez, la otra opción es tener sólo un poco (normal objeto CLR edad) clase que lo haría definir todo su interés en (voy a escribir mi ejemplo como si lo haces por una barra de herramientas, porque tiene sentido para mí):

public class ToolBarItem : INotifyPropertyChanged 
{ 
    public string Text { get ... set ... } 
    public ICommand Command { get ... set ... } 
    public ImageSource Image { get ... set ... } 
} 

que tiene una plantilla de datos definido en alguna parte (App.xaml , Window.Resources, etc):

<DataTemplate DataType="{x:Type l:ToolBarItem}"> 
    <Button Command="{Binding Command}"> 
     <StackPanel Orientation="Horizontal"> 
      <Image Source="{Binding Image}"/> 
      <TextBlock Text="{Binding Text}"/> 
     </StackPanel> 
    </Button> 
</DataTemplate> 

y luego usar el individuo en su xaml así:

<Window ...> 
    ... 
    <ContentControl> 
     <ContentControl.Content> 
      <l:ToolBarItem Image="..." Command="..." Text="..."/> 
     </ContentControl.Content> 
    </ContentControl> 
    ... 
</Window> 

Es que no saben que la forma en que estamos tratando de hacer es el más WPF manera que podía hacerlo.

EDITAR Actualizado basado en el segundo comentario Lo siento, olvidé incluir el ContentControl que lo rodea. Ahora que lo recuerdo, me doy cuenta de que no es mucho menos detallado que el original en el que se especifica el contenido manualmente. Publicaré una nueva respuesta para ayudar con su pregunta original.

+0

Hola, Sé que esto. En realidad, el objetivo de crear un control de usuario era evitar escribir esto para todos los botones. Al hacer esto, solo tuve que escribir: Es solo una refactorización si lo desea. – Roubachof

+0

Lo siento pero esto no funciona ... ToolBarItem no es un UIElement y cuando intento iniciar la aplicación, recibo una excepción que me lo dice. – Roubachof

1

Para volver a contestar a la pregunta original:

Lo que creo que quieres hacer es crear un nuevo CustomControl llamados ImageButton. Luego cámbialo para extender desde Button en lugar de Control. No necesitarás una propiedad Command ya que Button ya tiene una. Solo necesitará agregar una propiedad de Imagen y podrá reutilizar el botón de propiedad de Contenido en lugar de tener una propiedad de Texto.

Cuando se crea su CustomControl, agregará una entrada en su Generic.xaml para el estilo predeterminado de su ImageButton.En el definidor de la propiedad plantilla se puede cambiar el ControlTemplate a esto:

<ControlTemplate TargetType="{x:Type local:ImageButton}"> 
    <StackPanel Orientation="Horizontal"> 
     <Image Source="{TemplateBinding Image}"/> 
     <ContentPresenter/> 
    </StackPanel> 
</ControlTemplate> 

Entonces, de nuevo, cuando se desea utilizarlo:

<Window ... > 
... 
    <l:ImageButton Image="{StaticResource ...}" Command="..."> 
     Associer 
    </l:ImageButton> 
... 
</Window> 
Cuestiones relacionadas