Присоедините ICommand в WPF UserControl - PullRequest
7 голосов
/ 13 марта 2009

Я реализовал простую кнопку с изображением:

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

Как вы можете видеть, я предоставляю свойство ButtonCommand, чтобы иметь возможность присоединить ICommand к этому 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); }
    }
}

Затем, когда я объявляю свою кнопку, это дает:

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

И badaboom, ничего не происходит, когда я нажимаю на кнопку ImageButton. Когда я заменяю ImageButton простой кнопкой, вызывается ICommand.

Я даже пытался просто расширить класс Button и связать ICommand, но, опять же, это не сработало ...

Помощь оценена!

Thx.

Ответы [ 4 ]

7 голосов
/ 16 марта 2009

Вы можете достичь этого намного более чистым способом, используя стиль и несколько прикрепленных свойств.

В прикрепленных свойствах будет храниться ваша конкретная информация. Стиль будет использовать эти свойства и создавать желаемый вид.

Элемент все еще будет кнопкой, поэтому команда и все остальное будет работать.

 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>

Затем вы можете использовать это для объявления кнопок:

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

Примечание:

Я почти уверен, что было бы чётче пройти через свойство "Template" и использовать ControlTemplate и TemplateBindings, но это будет означать воссоздание границы и других элементов вокруг вашего контента, так что если вы просто хотите определите «Контент» по умолчанию, я думаю, мой пример - это путь.

4 голосов
/ 14 марта 2009

Если единственная добавленная функциональность, которую вы хотите для своей кнопки, - это иметь изображение на ней, то я думаю, что вы подходите к этому с неправильной стороны. WPF так же великолепен, как и элементы управления пользовательского интерфейса look-less. Это означает, что элемент управления - это просто определение функциональности + некоторый шаблон для определения его внешнего вида. Это означает, что шаблон может быть заменен в любое время, чтобы изменить внешний вид. Кроме того, практически любой контент может быть помещен внутри практически любого элемента управления

Например, для определения кнопки в вашем xaml, которая будет выглядеть так, как вам нужно, все, что вам нужно, это:

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

Просто помните, что с WPF вам не нужно определять новый CustomControl или UserControl каждый раз, когда вы хотите изменить внешний вид чего-либо. Единственный раз, когда вам нужен CustomControl, это если вы хотите добавить функциональность в существующий элемент управления или создать функциональность, которой нет ни в одном другом элементе управления.

Редактировать Из-за комментария:

Если вы хотите не определять содержимое кнопки каждый раз, другой вариант - просто иметь класс poco (обычный старый объект CLR), который определит все, что вас интересует (я напишу мой пример) как будто вы делаете это для панели инструментов, потому что это имеет смысл для меня):

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

Здесь определен шаблон данных (App.xaml, Window.Resources и т. Д.):

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

А затем используйте парня в вашем xaml так:

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

Я просто не знаю, как вы пытаетесь это сделать - это самый лучший способ, которым вы могли бы это сделать.

РЕДАКТИРОВАТЬ Обновлено на основе второго комментария Извините, я забыл включить ContentControl вокруг этого. Теперь, когда я вспомнил это, я понял, что это не намного менее многословно, чем оригинал, в котором вы указываете контент вручную. Я отправлю новый ответ, чтобы помочь с вашим первоначальным вопросом.

1 голос
/ 16 марта 2009

Чтобы ответить на оригинальный вопрос:

Я думаю, вы хотите создать новый CustomControl под названием ImageButton . Затем измените его, чтобы расширить от кнопки вместо управления. Вам не понадобится свойство Command, поскольку у Button оно уже есть. Вам нужно только добавить свойство Image, и вы можете повторно использовать свойство Content из кнопки вместо свойства Text.

Когда ваш CustomControl будет создан, он добавит запись в ваш Generic.xaml для стиля по умолчанию вашего ImageButton. В Setter для свойства Template вы можете изменить ControlTemplate следующим образом:

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

Затем, опять же, когда вы захотите его использовать:

<Window ... >
...
    <l:ImageButton Image="{StaticResource ...}" Command="...">
        Associer
    </l:ImageButton>
...
</Window>
0 голосов
/ 13 марта 2009

Встроенная кнопка WPF содержит код, который запускает прикрепленную команду в ответ на нажатие. Ваше «ImageButton» происходит от UserControl, поэтому вы не получаете такого поведения. Вероятно, самый короткий путь к тому, чтобы получить то, что вы хотите, - это чтобы ваш класс ImageButton на самом деле наследовал класс WPF Button. Для этого измените разметку для ImageButton с

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

до

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

Затем измените базовый класс ImageButton с UserControl на Button.

Вам, вероятно, потребуется внести некоторые другие незначительные изменения, прежде чем все это заработает.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...