C# 在WPF UserControl中附加ICommand

C# 在WPF UserControl中附加ICommand,c#,wpf,mvvm,user-controls,C#,Wpf,Mvvm,User Controls,我实现了一个带有图像的简单按钮: <Button Command="{Binding ButtonCommand, ElementName=ImageButtonControl}"> <StackPanel Orientation="Horizontal"> <Image Source="{Binding ButtonImage, ElementName=ImageButtonControl}"/>

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

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


如您所见,为了能够将ICommand附加到此UserControl,我公开了ButtonCommand属性:

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); }
    }
}
公共部分类ImageButton:UserControl
{
/// 
///获取或设置要渲染的图像源的依赖项属性。
/// 
公共静态从属属性ImageSourceProperty=
DependencyProperty.Register(“ButtonImage”、typeof(ImageSource)、typeof(ImageButton));
公共静态从属属性TextProperty=
DependencyProperty.Register(“ButtonText”、typeof(string)、typeof(ImageButton));
公共静态从属属性按钮commandproperty=
DependencyProperty.Register(“ButtonCommand”、typeof(ICommand)、typeof(ImageButton));
公共图像按钮()
{
this.DataContext=this;
初始化组件();
}
/// 
///获取或设置按钮命令。
/// 
公用ICommand按钮命令
{
get{return(ICommand)GetValue(ImageButton.ButtonCommandProperty);}
设置{SetValue(ImageButton.ButtonCommandProperty,value);}
}
/// 
///获取或设置按钮图像。
/// 
公共图像源按钮图像
{
get{return(ImageSource)GetValue(ImageButton.ImageSourceProperty);}
设置{SetValue(ImageButton.ImageSourceProperty,value);}
}
/// 
///获取或设置按钮文本。
/// 
公共字符串按钮文本
{
get{return(string)GetValue(ImageButton.TextProperty);}
设置{SetValue(ImageButton.TextProperty,value);}
}
}
然后,当我声明我的按钮时,它给出:

巴达博姆,当我点击图片按钮时,什么都不会发生。 当我用一个简单的按钮替换ImageButton时,ICommand被调用

我甚至试图简单地扩展Button类并绑定一个ICommand,但再一次,它没有起作用

谢谢你的帮助


Thx.

内置的WPF按钮包含在被单击时触发附加命令的代码。您的“ImageButton”派生自UserControl,因此您不会得到这种行为。获得所需内容的最短路径可能是ImageButton类实际派生自WPF Button类。为此,请将ImageButton的标记从

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

...


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


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

如果您希望为按钮添加的唯一功能是在其上有一个图像,那么我认为您的做法是错误的。WPF和它一样棒,因为UI控件的外观要比它的外观小。这意味着控件仅仅是功能的定义+一些模板来定义它的外观。这意味着可以随时调出模板以更改外观。此外,几乎任何内容都可以放在几乎任何控件内

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

<Window ...>
    ...
    <Button Command="{Binding AttachContextCommand}">
        <StackPanel Orientation="Horizontal">
            <Image Source="{StaticResource AssociateImage}"/>
            <TextBlock Text="Associer"/>
        </StackPanel>
    </Button>
    ...
</Window>
在某处定义了数据模板(App.xaml、Window.Resources等):


然后像这样使用xaml中的家伙:

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

...
...
我只是不知道你尝试的方式是你能做到的最WPF的方式

根据第二条评论更新编辑
抱歉,我忘了包含与此相关的ContentControl。现在我记住了这一点,我意识到这并不比手动指定内容的原始版本更详细。我将发布一个新的答案来帮助您解决原始问题。

您可以使用样式和几个附加属性以更简洁的方式实现这一点

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

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

 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>
公共类图像按钮
{
公共静态ImageSource GetImage(DependencyObject obj)
{
返回(ImageSource)对象获取值(ImageProperty);
}
公共静态void SetImage(DependencyObject obj,ImageSource值)
{
对象设置值(ImageProperty,值);
}
公共静态只读DependencyProperty ImageProperty=
DependencyProperty.RegisterAttached(“图像”、类型化(ImageSource)、类型化(ImageButton)、新UIPropertyMetadata(null));
公共静态字符串GetCaption(DependencyObject obj)
{
返回(字符串)obj.GetValue(CaptionProperty);
}
公共静态void SetCaption(DependencyObject对象,字符串值)
{
对象设置值(CaptionProperty,value);
}
公共静态只读DependencyProperty CaptionProperty=
DependencyProperty.RegisterAttached(“标题”、类型(字符串)、类型(ImageButton)、新UIPropertyMetadata(null));
}
然后,您可以使用它来声明按钮:

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

注:


我敢肯定,通过“Template”属性并使用ControlTemplate和TemplateBindings会更简洁,但这意味着重新创建内容周围的边框和其他内容,因此,如果您只想定义默认的“内容”,我想我的示例就是一种方法。

重新回答原始问题
<Window ...>
    ...
    <ContentControl>
        <ContentControl.Content>
            <l:ToolBarItem Image="..." Command="..." Text="..."/>
        </ContentControl.Content>
    </ContentControl>
    ...
</Window>
 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 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>