如何创建一个简单的WPF UserControl按钮,带有自定义启用/禁用图像?

9

我对WPF和XAML还不太熟悉,现在正在学习。

我找到了一位先前开发者编写的快速代码:

<ControlTemplate x:Key="ButtonTemplate" TargetType="{x:Type Button}">
        <Border Name="buttonBorder" Background="{TemplateBinding Background}">
            <Border.Effect>
                <DropShadowEffect Opacity="0.0" />
            </Border.Effect>
        </Border>
        <ControlTemplate.Triggers>
            <Trigger Property="IsMouseOver" Value="true">
                <Setter TargetName="buttonBorder" Property="Effect">
                    <Setter.Value>
                        <DropShadowEffect Opacity="0.8" />
                    </Setter.Value>
                </Setter>
            </Trigger>
            <Trigger Property="IsMouseCaptured" Value="true">
                    <Setter TargetName="buttonBorder" Property="Effect">                   
                         <Setter.Value>
                        <DropShadowEffect Opacity="0.8" Direction="135"  
                             ShadowDepth="3" BlurRadius="1" />
                        </Setter.Value>
                    </Setter>
                </Trigger>
            <Trigger Property="IsEnabled" Value="false">
                   <Setter TargetName="buttonBorder" Property="Background">
                        <Setter.Value>
                        <ImageBrush ImageSource="{Binding Path=Tag, RelativeSource={RelativeSource TemplatedParent}}"  />
                        </Setter.Value>
                    </Setter>            
                   <Setter TargetName="buttonBorder" Property="Effect">
                    <Setter.Value>
                        <DropShadowEffect Opacity="0.0"/>
                    </Setter.Value>
                </Setter>     

            </Trigger>
        </ControlTemplate.Triggers>
        </ControlTemplate>

基本上,它只是一个带有基本鼠标悬停效果和禁用状态图像绑定到标记的按钮模板(似乎是一个丑陋的解决方案)。
我想创建一个自定义按钮,其工作方式与原始按钮类似,但我想公开两个自定义属性:NormalImage 和 DisabledImage。这些属性应为字符串类型而非 Uri。我想要使用图像路径 "apply.png" 而非 "pack://application:,,,/Resources/Apply.png"。
我想,为了拥有这样的自定义属性,我需要一个带有依赖属性的 UserControl?
基本上,我想按以下方式使用按钮:
<MyImageButton NormalImage="apply.png" DisabledImage="apply_disabled.png"/>

也许NormalImage/DisabledImage以后会被绑定到其他内容上,但这种可能性很小。
我找不到任何实现自定义属性基本按钮的示例,只能在网上找到一些花哨的按钮和控件。也许我使用的关键词不正确...
有人可以指向正确的文章或提供一些简单的代码供我尝试吗?
对于初学者来说,WPF非常复杂,有时它并不能像预期的那样工作,例如我仍然不理解为什么我可以将Trigger标签添加到ControlTemplate中,但我不能直接将Trigger标签添加到UserControl中...
3个回答

14

你也可以使用 UserControl,但这会有点混乱,因为你的按钮将被封装起来,代码大致如下:

Xaml:

<UserControl x:Class="Test.ImageButton"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300"
             DataContext="{Binding RelativeSource={RelativeSource Self}}">
    <Button Name="button" Click="button_Click" Width="50" Height="50">
        <Button.Template>
            <ControlTemplate TargetType="{x:Type Button}">
                <Border Name="buttonBorder">
                    <Border.Effect>
                        <DropShadowEffect Opacity="0.0" />
                    </Border.Effect>
                    <Border.Child>
                        <Image Name="img" Source="{Binding NormalImage}"/>
                    </Border.Child>
                </Border>
                <ControlTemplate.Triggers>
                    <Trigger Property="IsMouseOver" Value="true">
                        <Setter TargetName="buttonBorder" Property="Effect">
                            <Setter.Value>
                                <DropShadowEffect Opacity="0.8" />
                            </Setter.Value>
                        </Setter>
                    </Trigger>
                    <Trigger Property="IsMouseCaptured" Value="true">
                        <Setter TargetName="buttonBorder" Property="Effect">
                            <Setter.Value>
                                <DropShadowEffect Opacity="0.8" Direction="135"  
                             ShadowDepth="3" BlurRadius="1" />
                            </Setter.Value>
                        </Setter>
                    </Trigger>
                    <Trigger Property="IsEnabled" Value="false">
                        <Setter TargetName="img" Property="Source" Value="{Binding DisabledImage}"/>
                        <Setter TargetName="buttonBorder" Property="Effect">
                            <Setter.Value>
                                <DropShadowEffect Opacity="0.0"/>
                            </Setter.Value>
                        </Setter>
                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Button.Template>
    </Button>
</UserControl>

代码后台:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace Test
{
    /// <summary>
    /// Interaction logic for ImageButton.xaml
    /// </summary>
    public partial class ImageButton : UserControl
    {
        public ImageSource DisabledImage
        {
            get { return (ImageSource)GetValue(DisabledImageProperty); }
            set { SetValue(DisabledImageProperty, value); }
        }
        public static readonly DependencyProperty DisabledImageProperty =
            DependencyProperty.Register("DisabledImage", typeof(ImageSource), typeof(ImageButton), new UIPropertyMetadata(null));


        public ImageSource NormalImage
        {
            get { return (ImageSource)GetValue(NormalImageProperty); }
            set { SetValue(NormalImageProperty, value); }
        }
        public static readonly DependencyProperty NormalImageProperty =
            DependencyProperty.Register("NormalImage", typeof(ImageSource), typeof(ImageButton), new UIPropertyMetadata(null));

        public event RoutedEventHandler Click;

        public ImageButton()
        {
            InitializeComponent();
        }

        private void button_Click(object sender, RoutedEventArgs e)
        {
            if (Click != null)
            {
                Click(this, e);
            }
        }
    }
}

示例用法:

        <local:ImageButton x:Name="imgbutton"
                           NormalImage="C:/1.png"
                           DisabledImage="C:/2.png"
                           Click="ImgButton_Click"/>

(请注意,当前的命名空间是Test,您可能想要更改它;此外,我设置了内部按钮的固定大小,您可能希望删除它,只需确保在某个地方设置大小,因为我认为如果您不这样做,它将不使用任何空间。)


谢谢,我使用了Style而不是ControlTemplate,并在ImageSource代码中添加了一些OnXImagePropertyChanged处理程序,最终它可以正常工作了。其实我现在可以为按钮图片文件夹添加一些默认路径属性。 - JustAMartin
@JustAMartin 你如何处理新的图像源更改事件,以将新值设置为您的控件? - Dia
1
@Dia 我找出了我们的旧代码并创建了一个Gist。由于我不得不删除一些项目特定的代码片段,所以它可能会有一些错误,但我认为它应该可以作为一个示例使用。https://gist.github.com/progmars/9d51e50fa03042264a83941e4f1e99c5 - JustAMartin

2
这是我的理解:

1)创建你的MyImageButton类,这个类应该继承普通的Button,并包含两个依赖属性 - NormalImageDisabledImage。这些属性应该是ImageSource类型的。看这里如何定义依赖属性:http://msdn.microsoft.com/en-us/library/ms752914.aspx#back_dependency_properties

2)将你的样式改为MyImageButton的样式:

<ControlTemplate x:Key="ButtonTemplate" TargetType="{x:Type MyControls:MyImageButton}">

3) 将启用和禁用模式下样式中的ImageBrush更改为使用TemplateBinding。以下是启用模式的示例:

<ImageBrush ImageSource="{TemplateBinding NormalImage}"  />

谢谢,这很有用。但是我可以将NormalImage/DisabledImage作为普通字符串来简化使用MyImageButton吗? - JustAMartin
我不确定它是否能够工作。此外,仅从代码后端(而非XAML)简化使用,因为在XAML中,字符串将被透明地转换为“ImageSource”。另外,在WPF中使用“ImageSource”似乎是对齐的 - 字符串在WPF中不用于这种属性。 - Snowbear
我想使用字符串的原因是为了避免在每个地方都使用完整路径,这样我只需输入“enabledimage.png”而不是“pack://application:,,,/Resources/enabledimage.png”,WPF不会自动为我添加pack://application前缀。 - JustAMartin

1

看一下greyableimage.codeplex.com

这可以用于替代按钮、菜单、工具栏等上的常规图像控件。当父控件被禁用时,它会自动生成一个“灰色”的版本来显示内容。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接