Custom button template in WPF
I want to create a simple button template with an image and text inside it. But I want to keep the System button's look and feel.
开发者_运维问答How do I create it, step by step?
P.S.: I have already tried it with CustomControl
in WPF and BasedOn
property.
You can do this easily with a style and attached property:
<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>
and
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));
}
Then in markup:
<Button Style="{StaticResource ImageButton}" ap:ButtonProperties.Image="{StaticResource MyImage}" Content="Test">
</Button>
This example looks pretty hideous, but you can easily change the StackPanel
to a Grid
or something similar to constrain the image proportion. Using the ContentPresenter
allows you to preserve the behaviour of a button allowing you to put any UIElement
inside, and retaining Command support etc.
I have finally created a Button with image + text inside it:
Below is the Full Code:
Step 1 : Create a new User Control called: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>
Step 2: Edit 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);
}
}
Step 3: In your xaml you can use it this way: Add the namespace as
xmlns:Local="clr-namespace:WpfApp"
And use it as:
<Local:ImageButtonUC x:Name="buttonImg" Width="100" Margin="10,0,10,0" Image="/WpfApp;component/Resources/Img.bmp" Text="Browse..." Click="buttonImg_Click"/>
Note: My Image is loacted in the Resources folder here
Reference:
http://blogs.msdn.com/knom/archive/2007/10/31/wpf-control-development-3-ways-to-build-an-imagebutton.aspx
If you don't want to write any code-behind, there is another way of doing this (inspired by jeffora's answer). You can use the control's Content field to put the URI to the image you want to see in your button:
<Button Content="https://www.google.com/images/srpr/logo3w.png" Height="100" Width="200" Style="{DynamicResource ButtonStyle1}"/>
And then you can edit the default Button Style to look like this:
<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>
The magic is in the 'Source=(Binding ...}' part. It has worked well for me to have the ToolTip there for debugging missing/changed images -- but can easily be removed as well.
Here is my Solution!
<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>
Result is below:
Another answer - improving on u/dogracer's and 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 >
- Content is the bound to 'object' with 'ImageUrl' and 'Text' Properties.
- This works in a usercontrol outside the main assembly.
- Style is that of a regular button
精彩评论