2009-03-13 4 views
7

ho implementato un semplice pulsante con un'immagine in esso:Fissare ICommand in WPF UserControl

<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> 

Come potete vedere, ho esporre una proprietà ButtonCommand al fine di essere in grado di associare un ICommand a questo 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); } 
    } 
} 

Quindi quando dichiaro il mio pulsante, restituisce questo :

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

E badaboom, nulla mai accadere quando clicco sul mio ImageButton. Quando sostituisco ImageButton con un semplice pulsante, viene chiamato l'ICommand.

Ho anche provato a estende semplicemente la classe Button e associare un ICommand, ma ancora una volta, non ha funzionato ...

Aiuto apprezzato!

Thx.

+0

è che il pulsante è disattivato, o che cliccalo e non succede niente? –

+0

clicco su di esso e il comando non viene mai chiamato ... – Roubachof

risposta

6

È possibile ottenere questo risultato in un modo molto più pulito utilizzando uno stile e un paio di proprietà associate.

Le proprietà allegate memorizzeranno le vostre informazioni specifiche. Lo stile utilizzerà queste proprietà e creerà l'aspetto desiderato.

L'elemento sarà ancora un pulsante in modo che il comando e tutto il resto funzionino.

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> 

È quindi possibile utilizzare questo ai pulsanti di dichiarare:

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

Nota:

Sono abbastanza sicuro che sarebbe stato più pulito di passare attraverso la proprietà "Template" e utilizzare un ControlTemplate e TemplateBindings, ma ciò significherebbe ricreare il bordo e altre cose intorno ai tuoi contenuti, quindi se stai cercando di definire un "Contenuto" predefinito, il mio esempio sarebbe la strada da percorrere, credo.

+0

Mi piace molto anche questa risposta. Di solito non penso di fare proprietà allegate, ma sono una caratteristica fantastica di WPF. – dustyburwell

+0

Infatti lo sono. Il fatto che sia possibile attaccare il comportamento agli elementi tramite proprietà collegate (come la gestione dichiarativa di eventi per cose come Drag and Drop) è ancora più fantastico quando ci si arriva. –

+0

+1 Molto semplice, flessibile e riutilizzabile. Grazie – surfen

0

Il pulsante WPF incorporato contiene il codice che attiva il comando allegato in risposta all'attivazione del clic. Il tuo "ImageButton" deriva da UserControl, quindi non ottieni questo comportamento. Probabilmente il percorso più breve per ottenere ciò che desideri è che la tua classe ImageButton derivi effettivamente dalla classe Button WPF. Per fare questo, cambiare il markup per ImageButton da

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

a

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

Quindi modificare la classe base di ImageButton UserControl-Button.

Probabilmente dovrai apportare altre piccole modifiche prima che tutto funzioni.

+0

Ciao Daniel, ho davvero cercato di estendere la classe pulsante, ma il comando non viene mai chiamato o ... – Roubachof

4

Se l'unica funzionalità aggiunta che si desidera per il pulsante è avere un'immagine su di esso, quindi penso che ti stai avvicinando dalla direzione sbagliata. WPF è fantastico come lo è perché i controlli dell'interfaccia utente sono senza look. Ciò significa che un controllo è semplicemente una definizione di funzionalità + un modello per definire come appare. Ciò significa che il modello può essere sostituito in qualsiasi momento per cambiare l'aspetto. Inoltre, quasi ogni contenuto può essere inserito all'interno di quasi ogni controllo

Ad esempio, per definire un pulsante nel vostro XAML che ha l'aspetto la vostra intenzione per tutto ciò che serve è questo:

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

Basta tenere a sia chiaro che con WPF non è necessario definire un nuovo CustomControl o UserControl ogni volta che si desidera modificare l'aspetto di qualcosa. L'unica volta che è necessario un CustomControl è se si desidera aggiungere funzionalità a un controllo esistente o creare funzionalità che non esistono in nessun altro controllo.

modifica perché a commentare:

Se hai intenzione di evitare di definire il contenuto per il pulsante ogni volta, l'altra opzione è quella di avere solo un poco (semplice oggetto CLR vecchio) classe che avrebbe definire tutto il vostro interessato a (scriverò il mio esempio come se si sta facendo questo per una barra degli strumenti, perché ha senso per me):

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

che ha un modello di dati definito da qualche parte (App.xaml , Window.Resources, ecc.):

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

e quindi utilizzare il ragazzo nella tua XAML come questo:

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

io proprio non lo so che il modo in cui si sta cercando di fare è il modo più WPF si potesse fare.

EDIT Aggiornato in base al secondo commento Spiacente, ho dimenticato di includere ContentControl che lo circonda. Ora che me ne sono ricordato, mi rendo conto che non è molto meno prolisso dell'originale in cui si specifica manualmente il contenuto. Pubblicherò una nuova risposta per aiutare con la tua domanda originale.

+0

Ciao, lo so questo. In realtà l'obiettivo di creare un controllo utente era evitare di scrivere questo per tutti i pulsanti. Facendo questo, ho solo dovuto scrivere: È solo un refactoring, se lo si desidera. – Roubachof

+0

Scusa ma non funziona ... ToolBarItem non è un UIElement e quando provo a lanciare l'app, ottengo un'eccezione che me lo dice. – Roubachof

1

Per ri-rispondere alla domanda iniziale:

Quello che penso che si vuole fare è creare un nuovo CustomControl chiamato ImageButton. Quindi modificalo per estenderlo da Button invece che da Control. Non avrai bisogno di una proprietà Comando poiché Button ne ha già uno. Devi solo aggiungere una proprietà Image e puoi riutilizzare la proprietà Content dal pulsante invece di avere una proprietà Text.

Quando il tuo CustomControl viene creato, aggiungerà una voce in Generic.xaml per lo stile predefinito di ImageButton.Nel Setter per la proprietà Template è possibile modificare il ControlTemplate a questo:

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

Poi, ancora una volta, quando si desidera utilizzarlo:

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