【问题标题】:Change WPF button background image programmatically以编程方式更改 WPF 按钮背景图像
【发布时间】:2013-04-04 19:14:19
【问题描述】:

我正在尝试创建一个<Button/>,其背景为 png 图像和背景颜色。

注意: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 更改为其他内容?

【问题讨论】:

  • p.s.手动编辑 .xaml 文件是不是编程的,这里似乎有几个答案似乎是这样认为的。 :-/
  • 你想要它完全一样(你的点击处理程序)吗?你能改变 XAML 吗?仅当您无法“触摸”该样式时,才将其“全部编程”才有意义。
  • 当然,我可以编辑样式,但是按钮太多,不现实。但是,所有按钮共享相同的背景颜色。
  • 不,不是完全那样,它只是一个例子。我想要做的是获取一个button 对象并在某些事件(单击、鼠标输入、窗口调整大小等)之后更改其背景图像。有意义吗?
  • 为按钮添加点击触发事件并在模板&lt;ControlTemplate.Triggers&gt; &lt;Trigger Property="Button.IsPressed" Value="True"&gt; &lt;Setter Property="Image.Source" Value=",,,"&gt;&lt;/Setter&gt; &lt;/Trigger&gt; &lt;/ControlTemplate.Triggers&gt;中更改背景

标签: c# wpf xaml


【解决方案1】:

叹息,在 Windows 窗体中它实际上是一行。在 WPF 中,它看起来会是几百个。

那是因为它是您可以在 winforms 中的按钮中放置的唯一内容。 WPF 是一个真正的 UI 框架,而不是一些随机的、半弃用的恐龙,它只允许做默认的东西(顺便说一下,这看起来很可怕)。

选项 1:将图像放置为 Button 的 Content:

如果您只想在按钮中放置一个图像,而不是其他,为什么不这样做呢?

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

结果:

选项 2(不是很优雅):使用 Tag 属性:

如果除了 Image 之外,您还想在 Button 中添加其他内容,您可以采用一种有点老套的方法,将 ImageSource 放入 Button 的 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>

结果:

选项 3:使用Attached Property

与选项 2 相同,但使用在其他地方声明的属性,例如:

 <Button ButtonImage.Source="./ChessPieces/BlackBishop.png"
         <!-- etc -->

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

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

选项 5:MVVM

创建一个ButtonViewModel,或者实际使用ViewModel中声明的DelegateCommand来绑定Button的属性。

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

这不是你想做的事。这根本不是一个好方法。

我可以永远继续下去,但我必须去睡觉。如果您希望我详细说明这些方法中的任何一种,请告诉我。

【讨论】:

  • 我选择了类似于“选项 1:将图像作为按钮的内容”的内容。谢谢!
  • 我没有使用按钮;我不知道如何设置按钮的悬停和按下状态。
【解决方案2】:

我想到了几种方法:

  1. 在运行时访问样式并遍历查找图像
  2. 为图像命名并在运行时引用它并更改源

我认为最好的解决方案是创建一个用户控件并将图像源作为依赖属性,请参阅:http://www.codeproject.com/Articles/224230/Exploring-the-use-of-Dependency-Properties-in-User

这样您将拥有一个即用型“自定义”控件,该控件支持您所追求的内容,并允许您直接引用它并使用绑定,这样您就可以避免使用样式的混乱解决方案。


经过对需求的讨论和扩展,请参见:

How can I access ResourceDictionary in wpf from C# code?

【讨论】:

  • Access the style at runtime and traverse it to find the image 这基本上是我想做的,但我不知道怎么做。
  • @DavidMurdoch 样式在哪里定义?这将完全影响您在运行时访问它的方式,遍历会比简单地创建用户控件更棘手。
  • 它在一个名为“Templates.xaml”(ResourceDictionary)的文件中,在我的App.xaml中引用。
  • 我可能误解了你,我不认为我想访问Style 本身,因为它将应用于许多按钮。我只想在特定按钮上覆盖样式的“属性”(或在 xaml 中调用的任何内容)。
  • @DavidMurdoch 这是不可能的 afaik,拥有按钮特定覆盖的唯一方法是使用 UserControl。
猜你喜欢
  • 2016-07-12
  • 2011-03-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-05-15
  • 1970-01-01
  • 2012-07-11
  • 1970-01-01
相关资源
最近更新 更多