WPF 中的自定义按钮模板

问题描述 投票:0回答:6

我想创建一个简单的按钮模板,其中包含图像和文本。但我想保留系统按钮的外观和感觉。

如何一步步创建它?

P.S.:我已经在 WPF 中使用

CustomControl
BasedOn
属性进行了尝试。

wpf custom-controls styles
6个回答
40
投票

您可以使用样式和附加属性轻松完成此操作:

<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:ap="clr-namespace:MyProject.Namespace.Path.To.ButtonProperties">
    ...
    <Style x:Key="ImageButton" TargetType="Button">
        <Setter Property="ContentTemplate">
            <Setter.Value>
                <DataTemplate>
                    <StackPanel Orientation="Horizontal">
                        <Image Source="{Binding Path=(ap:ButtonProperties.Image), RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Button}}}"></Image>
                        <ContentPresenter Content="{Binding Path=Content, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Button}}}"></ContentPresenter>
                    </StackPanel>
                </DataTemplate>
            </Setter.Value>
        </Setter>
    </Style>
    ...
</ResourceDictionary>

public class ButtonProperties
{
    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(ButtonProperties), new UIPropertyMetadata((ImageSource)null));
}

然后在标记中:

<Button Style="{StaticResource ImageButton}" ap:ButtonProperties.Image="{StaticResource MyImage}" Content="Test">
</Button>

这个例子看起来相当丑陋,但你可以轻松地将

StackPanel
更改为
Grid
或类似的东西来限制图像比例。使用
ContentPresenter
可以保留按钮的行为,允许您将任何
UIElement
放入其中,并保留命令支持等。


7
投票

我终于创建了一个带有图像+文本的按钮:

以下是完整代码:

第 1 步:创建一个名为:ImageButtonUC 的新用户控件

<UserControl Name="ImageButton" x:Class="WpfApp.ImageButtonUC"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Grid>
        <Button VerticalAlignment="Top" Width="100" Height="25" Click="button_Click"> 
            <Button.Content>
                <StackPanel Orientation="Horizontal">
                    <Image Width="16" Height="16" Margin="5,0,5,0" Source="{Binding ElementName=ImageButton, Path=Image}"/>
                    <TextBlock Text="{Binding ElementName=ImageButton, Path=Text}"/>
                </StackPanel>
            </Button.Content>
        </Button>
    </Grid>
</UserControl>

第2步:编辑ImageButtonUC.xaml.cs

public partial class ImageButtonUC : UserControl
    {
        public event RoutedEventHandler Click;

        public ImageButtonUC()
        {
            InitializeComponent();

        }

        public string Text
        {
            get { return (string)GetValue(TextProperty); }
            set { SetValue(TextProperty, value); }
        }


        public static readonly DependencyProperty TextProperty =
          DependencyProperty.Register("Text", typeof(string), typeof(ImageButtonUC), new UIPropertyMetadata(""));

        public ImageSource Image
        {
            get { return (ImageSource)GetValue(ImageProperty); }
            set { SetValue(ImageProperty, value); }
        }

        public static readonly DependencyProperty ImageProperty =
           DependencyProperty.Register("Image", typeof(ImageSource), typeof(ImageButtonUC), new UIPropertyMetadata(null));


        private void button_Click(object sender, RoutedEventArgs e)
        {

            if (null != Click)

                Click(sender, e);

        }

    }

第3步:在你的xaml中你可以这样使用它: 添加命名空间为

xmlns:Local="clr-namespace:WpfApp"

并将其用作:

<Local:ImageButtonUC x:Name="buttonImg" Width="100" Margin="10,0,10,0" Image="/WpfApp;component/Resources/Img.bmp" Text="Browse..." Click="buttonImg_Click"/>

注意:我的图像位于此处的资源文件夹中

参考:

http://blogs.msdn.com/knom/archive/2007/10/31/wpf-control-development-3-ways-to-build-an-imagebutton.aspx


3
投票

如果您不想编写任何后台代码,还有另一种方法可以做到这一点(受到 jeffora 答案的启发)。您可以使用控件的内容字段将 URI 放入您想要在按钮中看到的图像:

<Button Content="https://www.google.com/images/srpr/logo3w.png" Height="100" Width="200" Style="{DynamicResource ButtonStyle1}"/>

然后您可以编辑默认按钮样式,如下所示:

<Style>
...
<Setter Property="Template">
    <Setter.Value>
        <ControlTemplate TargetType="{x:Type Button}">
            <Microsoft_Windows_Themes:ButtonChrome x:Name="Chrome" BorderBrush="{TemplateBinding BorderBrush}" Background="{TemplateBinding Background}" RenderMouseOver="{TemplateBinding IsMouseOver}" RenderPressed="{TemplateBinding IsPressed}" RenderDefaulted="{TemplateBinding IsDefaulted}" SnapsToDevicePixels="true">
                <Image x:Name="theImage" Source="{Binding Path=Content, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Button}}}" Margin="4,0,0,0">
                    <Image.ToolTip>
                        <ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
                    </Image.ToolTip>
                </Image>
            </Microsoft_Windows_Themes:ButtonChrome>
            <ControlTemplate.Triggers>
                ...
            </ControlTemplate.Triggers>
        </ControlTemplate>
    </Setter.Value>
</Setter>
</Style>

神奇之处在于“Source=(Binding ...}”部分。对于我来说,在其中使用工具提示来调试丢失/更改的图像效果很好——但也可以轻松删除。


0
投票

这是我的解决方案!

<Button Content="Browse" Margin="10" Name="btBrowse">
            <Button.Template>
                <ControlTemplate>
                    <StackPanel Orientation="Vertical" Height="50" Margin="5" VerticalAlignment="Center" HorizontalAlignment="Center">
                        <Image Source="MyIcons\browse.png" Height="30" />
                        <TextBlock Text="{Binding ElementName=btBrowse, Path=Content}" VerticalAlignment="Center" HorizontalAlignment="Center" />
                    </StackPanel>
                </ControlTemplate>
            </Button.Template>
        </Button>

结果如下:

screenshot


0
投票

另一个答案 - 改进 u/dogracer 和 u/Dave NP:


<Button Content = "{Binding object}" >
    <Button.Style >
        <Style TargetType="Button">
            <Setter Property = "ContentTemplate" >
                <Setter.Value >
                    <DataTemplate >
                        <StackPanel >
                            <Image  Source="{Binding Path=Content.ImageUrl, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Button}}}" >
                                <TextBlock Text = "{Binding Path=Content.Text,RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Button}}}" />
                        </ StackPanel >
                    </ DataTemplate >
                </ Setter.Value >
            </ Setter >
        </ Style >
    </ Button.Style >
</ Button >
  1. 内容通过“ImageUrl”和“Text”属性绑定到“object”。
  2. 这适用于主程序集外部的用户控件。
  3. 风格与常规按钮相同

0
投票

我提供了两种方法的代码,同时保持系统按钮的外观和感觉。

  • 方法一:使用DataTemplate和样式通过样式添加Image控件 Button.Image AttachedProperty 提供 ImageSource,如下所述 杰弗拉
  • 方法2A:通过Button.AddImage AttachedProperty添加Image控件
  • 方法2B:通过Button.AddImage AttachedProperty添加Image控件,同时也可以通过style灵活修改ContentTemplate

这是输出:

mainWindow.xaml代码

<Window x:Class="ButtonWithImageAndText.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:ButtonWithImageAndText"
        Title="MainWindow" Height="250" Width="400">
    <Window.Resources>
        <BitmapImage x:Key="MyImage" UriSource="/panic-button.png"/>
        <Style x:Key="ImageButton1" TargetType="Button">
            <Setter Property="ContentTemplate">
                <Setter.Value>
                    <DataTemplate>
                        <StackPanel Orientation="Horizontal" HorizontalAlignment="Left">
                            <Image Source="{Binding Path=(local:ButtonProperties.Image), RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Button}}}"></Image>
                            <ContentPresenter Content="{Binding Path=Content, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Button}}}"></ContentPresenter>
                        </StackPanel>
                    </DataTemplate>
                </Setter.Value>
            </Setter>
        </Style>
        <Style x:Key="ImageButton2" TargetType="Button">
            <Setter Property="ContentTemplate">
                <Setter.Value>
                    <DataTemplate>
                        <StackPanel Orientation="Horizontal" HorizontalAlignment="Left">
                            <CheckBox/>
                            <ContentPresenter Content="{Binding Path=Content, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Button}}}"></ContentPresenter>
                        </StackPanel>
                    </DataTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </Window.Resources>
    <Grid>
        <StackPanel>
            <!--Method-1: Adding Image control through style using DataTemplate and Button.Image AttachedProperty to provide ImageSource. -->
            <Button HorizontalContentAlignment="Center"  local:ButtonProperties.Image="{StaticResource MyImage}" Content="Test1" Height="20" Style="{StaticResource ImageButton1}"/>
            <!--Method-2A: Adding Image control through Button.AddImage AttachedProperty  -->
            <Button HorizontalContentAlignment="Center"   local:ButtonExtensions.AddImage="{StaticResource MyImage}" Content="Test2" Height="20"/>
            <!--Method-2B: Adding Image control through Button.AddImage AttachedProperty, also giving flexibility to modify ContentTemplate through style  -->
            <Button HorizontalContentAlignment="Center"   local:ButtonExtensions.AddImage="{StaticResource MyImage}" Content="Test3" Height="20" Style="{StaticResource ImageButton2}"/>
        </StackPanel>
    </Grid>
</Window>

ButtonExtensions.cs 代码

using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;

namespace ButtonWithImageAndText
{
    public static class ButtonExtensions
    {
        public static ImageSource GetAddImage(DependencyObject obj)
        {
            return (ImageSource)obj.GetValue(ImageProperty);
        }

        public static void SetAddImage(DependencyObject obj, ImageSource value)
        {
            obj.SetValue(ImageProperty, value);
        }

        //Image is added dynamically to control template using OnImageChanged event handler which is raised whenever the AddImage property is assigned some value.
        public static readonly DependencyProperty ImageProperty =
            DependencyProperty.RegisterAttached("AddImage", typeof(ImageSource), typeof(ButtonExtensions), new UIPropertyMetadata((ImageSource)null, OnAddImageChanged));

        
        private static void OnAddImageChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (d is Button btn)
            {
                btn.Loaded += (sender, args) =>
                {
                    // ControlTemplate of Button has Border > ContentPresenter. We will change it to Border > StackPanel(Image+ContentPresenter)
                    ContentPresenter contentPresenter = FindVisualChild<ContentPresenter>(btn);
                    var currentParent = VisualTreeHelper.GetParent(contentPresenter) as Border;

                    if (currentParent != null)
                    {
                        StackPanel newStackPanel = new StackPanel()
                        {
                            Orientation = Orientation.Horizontal,
                            HorizontalAlignment = btn.HorizontalContentAlignment
                        };
                        UIElement existingChild = currentParent.Child;
                        currentParent.Child= newStackPanel;
                        newStackPanel.Children.Add(new Image() { Source = e.NewValue as ImageSource });
                        // Add the ContentPresenter to the new StackPanel
                        newStackPanel.Children.Add(existingChild);
                    }
                };
            }
        }


        // Helper method to find a child of a specific type in the visual tree
        private static T FindVisualChild<T>(DependencyObject visual)
            where T : DependencyObject
        {
            for (int i = 0; i < VisualTreeHelper.GetChildrenCount(visual); i++)
            {
                DependencyObject child = VisualTreeHelper.GetChild(visual, i);

                if (child != null && child is T)
                {
                    return (T)child;
                }

                T childOfChild = FindVisualChild<T>(child);

                if (childOfChild != null)
                {
                    return childOfChild;
                }
            }

            return null;
        }
    }
}

ButtonProperties.cs 代码

using System.Windows;
using System.Windows.Media;

namespace ButtonWithImageAndText;

public class ButtonProperties
{
    public static ImageSource GetImage(DependencyObject obj)
    {
        return (ImageSource)obj.GetValue(ImageProperty);
    }

    public static void SetImage(DependencyObject obj, ImageSource value)
    {
        obj.SetValue(ImageProperty, value);
    }
    //It is assumed that the Image is added to control template using style.
    public static readonly DependencyProperty ImageProperty =
        DependencyProperty.RegisterAttached("Image", typeof(ImageSource), typeof(ButtonProperties), new UIPropertyMetadata((ImageSource)null));
}
© www.soinside.com 2019 - 2024. All rights reserved.