Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Attach ICommand in WPF UserControl

I implemented a simple button with an image in it:

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

As you can see, I expose a ButtonCommand property in order to be able to attach an ICommand to this 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); }
    }
}

Then when I declare my button it gives this:

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

And badaboom, nothing never happen when I click on my ImageButton. When I replace the ImageButton with a simple button, the ICommand is called.

I even tried to simply extends the Button class and bind an ICommand, but once again, it didn't work...

Help appreciated !

Thx.

like image 753
Roubachof Avatar asked Mar 13 '09 17:03

Roubachof


2 Answers

You can achieve this in a much cleaner way using a style and a couple of attached properties.

The attached properties will store your specific information. The style will use these properties and build the look you want.

The element will still be a button so the command and everything else will work.

 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>

You can then use this to declare buttons:

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

Note:

I'm pretty sure it would be cleaner to go through the "Template" property and use a ControlTemplate and TemplateBindings, but that would mean re-creating the border and other stuff around your content, so if you are looking to just define a default "Content", my example would be the way to go, I think.

like image 168
Denis Troller Avatar answered Nov 02 '22 09:11

Denis Troller


To re-answer the original question:

What I think you want to do is create a new CustomControl called ImageButton. Then change it to extend from Button instead of Control. You won't need a Command property since Button already has one. You'll only need to add an Image property and you can reuse the Content property from button instead of having a Text property.

When your CustomControl is created, it'll add an entry in your Generic.xaml for the default style of your ImageButton. In the Setter for the Template property you can change the ControlTemplate to this:

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

Then, again, when you want to use it:

<Window ... >
...
    <l:ImageButton Image="{StaticResource ...}" Command="...">
        Associer
    </l:ImageButton>
...
</Window>
like image 41
dustyburwell Avatar answered Nov 02 '22 07:11

dustyburwell