Modelo do botão personalizado no WPF
quero criar um modelo de botão simples com uma imagem e texto dentro dela. Mas quero manter o visual e a sensação do botão do sistema.
Como posso criá-lo, passo a passo?P. S.: eu já tentei com CustomControl
em WPF e BasedOn
propriedade.
5 answers
Você pode fazer isso facilmente com um estilo e propriedades anexadas:
<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>
E
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));
}
Depois Em marcação:
<Button Style="{StaticResource ImageButton}" ap:ButtonProperties.Image="{StaticResource MyImage}" Content="Test">
</Button>
Este exemplo parece bastante hediondo, mas você pode facilmente mudar o StackPanel
para um Grid
ou algo semelhante para restringir a proporção da imagem. A utilização do ContentPresenter
permite-lhe preservar o comportamento de um botão que lhe permite colocar qualquer UIElement
lá dentro, mantendo o Suporte de comandos, etc.
Finalmente criei um botão com imagem + texto dentro dele:
Abaixo está o código Completo:
Passo 1: Criar um novo controlo de utilizador chamado: 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>
Passo 2: Editar O 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);
}
}
Passo 3: no seu xaml pode usá-lo desta forma: Adicionar o espaço de nomes como
xmlns:Local="clr-namespace:WpfApp"
E usá-lo como:
<Local:ImageButtonUC x:Name="buttonImg" Width="100" Margin="10,0,10,0" Image="/WpfApp;component/Resources/Img.bmp" Text="Browse..." Click="buttonImg_Click"/>
Nota: a minha imagem é captada na pasta de recursos toma.
Referência:
Se você não quer escrever qualquer código-atrás, há outra maneira de fazer isso (inspirado pela resposta de jeffora). Pode usar o campo de conteúdo do controlo para colocar o URI na imagem que deseja ver no seu botão:
<Button Content="https://www.google.com/images/srpr/logo3w.png" Height="100" Width="200" Style="{DynamicResource ButtonStyle1}"/>
E depois poderá editar o estilo do botão por omissão para se parecer com isto:
<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>
A magia está na fonte=(ligando ...}' parte. Tem funcionado bem para mim ter a dica lá para depurar as imagens em falta/alteradas -- mas pode ser facilmente removida como bem.
<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>
O resultado está abaixo:
<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. Isto funciona num controlo do utilizador fora da Assembleia principal.
- O estilo é o de um botão regular