在WPF UserControl中附加ICommand

时间:2009-03-13 17:05:05

标签: c# wpf mvvm user-controls

我实现了一个带有图像的简单按钮:

    <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 个答案:

答案 0 :(得分:7)

您可以使用样式和一些附加属性以更清晰的方式实现此目的。

附加属性将存储您的特定信息。 该样式将使用这些属性并构建您想要的外观。

元素仍然是一个按钮,所以命令和其他一切都可以工作。

 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="..." />

注意:

我很确定通过“模板”属性并使用ControlTemplate和TemplateBindings会更干净,但这意味着要重新创建内容周围的边框和其他内容,所以如果你只是想定义一个默认的“内容”,我想我的例子就是这样。

答案 1 :(得分:4)

如果您想要为按钮添加的唯一功能是在其上添加图像,那么我认为您正在从错误的方向接近此功能。 WPF非常棒,因为UI控件无外观。这意味着Control只是一个功能定义+一些模板来定义它的外观。这意味着可以随时换出模板以更改外观。此外,几乎任何内容都可以放在几乎任何控件内部

例如,要在xaml中定义一个具有您所需要的外观的按钮,请执行以下操作:

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

请记住,使用WPF时,每次要更改某些内容的外观时,都不必定义新的CustomControl或UserControl。您应该需要CustomControl的唯一时间是您要为现有Control添加功能或创建任何其他Control中不存在的功能。

编辑由于评论:

如果你想每次都不想定义按钮的内容,另一个选择是只有一个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>

我只是不知道你尝试这样做的方式是你可以做到的最WPF方式。

编辑根据第二条评论更新 对不起,我忘了包含ContentControl。现在我记得那个,我意识到,这比你手动指定内容的原始文件要简单得多。我会发布一个新的答案来帮助你解决原始问题。

答案 2 :(得分:1)

重新回答原来的问题:

我认为你想要做的是创建一个名为 ImageButton 的新CustomControl。然后将其更改为从Button而不是Control扩展。您不需要Command属性,因为Button已经有一个。您只需要添加一个Image属性,并且可以从按钮重用Content属性而不是Text属性。

创建CustomControl后,它会在Generic.xaml中添加一个条目,用于ImageButton的默认样式。在Setter for 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>

答案 3 :(得分:0)

内置WPF按钮包含触发附加命令以响应单击的代码。您的“ImageButton”派生自UserControl,因此您不会获得该行为。获得所需内容的最短路径可能是您的ImageButton类实际派生自WPF Button类。要完成此操作,请从

更改ImageButton的标记
<UserControl
...
>
...
</UserControl>

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

然后将ImageButton的基类从UserControl更改为Button

在一切正常之前,你可能需要做一些其他的小改动。