以编程方式更改WPF按钮背景图像

3

我正在尝试创建一个带有PNG图像作为背景和背景颜色的<Button/>

注意:可能尚未确定PNG图像源,因此我无法将它们放入模板中。

我有一个看起来像这样的Style

<Style x:Key="MyButton" TargetType="Button">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="Button">
                <Border CornerRadius="0">
                    <Image Margin="0" Source="/MyApp;component/Images/my-image.png" Stretch="None" />
                    <Border.Style>
                        <Style TargetType="{x:Type Border}">
                            <Setter Property="Background" Value="LimeGreen" />
                        </Style>
                    </Border.Style>
                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

这段代码会在一个实心的LimeGreen背景上放置一个带有png图片的按钮。

我的MainWindow.xaml文件大致如下:

<Button Style="{StaticResource MyButton}" Click="btnMine_Click" /* some other props */ />

它的代码如下:

private void btnMine_Click(object sender, RoutedEventArgs e)
{
    // TODO: change the image src on this button
    var myButton = sender as Button;

    // This won't work:
    // myButton.Border.Image.Source = getNewImageString();
}

如何将图像源从/MyApp2;component/Images/my-image.png更改为其他内容?


你想要完全像那样(你的点击处理程序)吗?你能改变XAML吗?如果你无法“触碰”那个样式,那么使它“全部以编程方式”只有意义。 - NSGaga-mostly-inactive
不,不是“完全”像那样,那只是一个例子。我想做的是获取一个“button”对象,并在某些事件(点击、鼠标进入、窗口调整大小等)后更改其背景图像。明白吗? - David Murdoch
为按钮添加点击触发事件并更改模板中的背景 <ControlTemplate.Triggers> <Trigger Property="Button.IsPressed" Value="True"> <Setter Property="Image.Source" Value=",,,"></Setter> </Trigger> </ControlTemplate.Triggers> - KF2
@irsog,这正是我想做的...但我想不出如何实现。 - David Murdoch
你需要“绑定”而不是“查找”控件。将按钮绑定到某个ButtonInfo模型 - 该模型具有图像。然后在样式中绑定到该属性 - 而不是硬编码路径。 - NSGaga-mostly-inactive
显示剩余3条评论
2个回答

2
在Windows Forms中,这只需要一行代码就可以实现。但在WPF中,可能会需要数百行代码来实现同样的效果。
这是因为在WinForms中,按钮中只能放置一个控件。而WPF是一个真正的UI框架,不仅支持默认控件,还允许自定义控件,使得界面更加美观。
选项1:将图片放置在按钮的内容中:
如果您只想在按钮中放置一张图片,那么直接将图片作为按钮的内容即可。
        <Style TargetType="Button">
            <Setter Property="Background" Value="LimeGreen"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="Button">
                        <Border Background="{TemplateBinding Background}">
                            <ContentPresenter ContentSource="Content"/>
                        </Border>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>

然后:

     <Button>
        <!-- Background not set, defaults to what's set in the Style -->
        <Image Source="./ChessPieces/BlackKnight.png"/>
    </Button>
    <Button Background="Red">
        <Image Source="./ChessPieces/BlackBishop.png"/>
    </Button>
    <Button Background="Blue">
        <Image Source="./ChessPieces/BlackPawn.png"/>
    </Button>

    <!-- WPF's idea of "Dynamic" is not the same as win(hack)forms' -->
    <Button Background="White">
        <Image Source="{Binding SomeStringPropertyDefinedInAViewModel}"/>
    </Button>

结果: enter image description here 选项2(不太优雅):使用Tag属性:
如果除了图像之外,您还想在按钮内放置其他内容,则可以采用有点巧妙的方法,将ImageSource放置在按钮的Tag属性中。
        <Style TargetType="Button">
            <Setter Property="Background" Value="LimeGreen"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="Button">
                        <Border Background="{TemplateBinding Background}">
                            <StackPanel Orientation="Horizontal">
                                <Image Source="{Binding Tag, RelativeSource={RelativeSource TemplatedParent}}"
                                       Height="50" Width="50"/>
                                <ContentPresenter ContentSource="Content"/>
                            </StackPanel>
                        </Border>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>

然后:

    <Button Tag="./ChessPieces/BlackKnight.png"
            Background="Blue"
            Foreground="White"
            Content="Some Text"/>

    <Button Tag="./ChessPieces/BlackBishop.png"
            Background="LightGray">
        <CheckBox Content="A CheckBox!" VerticalAlignment="Center"/>
    </Button>

结果: enter image description here 选项3:使用附加属性 与选项2相同,但是使用在其他地方声明的属性,例如:
 <Button ButtonImage.Source="./ChessPieces/BlackBishop.png"
         <!-- etc -->

选项4:创建自定义控件:

创建一个继承于Button的类(.cs文件没有XAML),并添加一个DependencyProperty来保存图片,然后将模板设置为它,并使用该值。

选项5:MVVM

创建一个ButtonViewModel,或者实际上在ViewModel中声明DelegateCommand来绑定按钮的属性。

不是一个选项:遍历可视树并在代码中更改Image.Source

那是你不想做的事情。这不是一个好的方法。

我可以无休止地继续下去,但我得睡觉了。如果您想让我详细说明其中任何一种方法,请告诉我。


我选择了类似于“选项1:将图像放置为按钮的内容”的方案。谢谢! - David Murdoch
我没有使用按钮,因为我无法弄清楚如何设置按钮的悬停和按下状态的样式。 - David Murdoch

0
有几种方法可以考虑:
  1. 在运行时访问样式并遍历以查找图像
  2. 给图像命名并在运行时引用它,只需更改源
我认为最好的解决方案是创建一个用户控件,并将图像源作为依赖属性,请参见:http://www.codeproject.com/Articles/224230/Exploring-the-use-of-Dependency-Properties-in-User 这样,您将拥有一个可直接引用和使用绑定的“自定义”控件,支持您想要的功能,并避免使用样式时出现混乱的解决方案。
经过讨论和需求扩展,请查看以下内容:

如何从C#代码中访问WPF中的ResourceDictionary?


@DavidMurdoch 样式在哪里定义的?这将完全影响你在运行时如何访问它,遍历会比简单创建用户控件更棘手。 - Clint
@DavidMurdoch 据我所知,这是不可能的,具有按钮特定覆盖的唯一方法是使用 UserControl。 - Clint
叹气,Windows Forms 中只需要一行代码。在 WPF 中似乎需要几百行。 - David Murdoch
我在按钮上找不到“图标”属性? - David Murdoch
@DavidMurdoch 他在谈论使用模型进行绑定,不是你问题的主题。 - Clint
显示剩余5条评论

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