There are many buttons with the same style, in which only the icon and the name and toolTip change. How to create a universal style for them?

Here is the style:

<Style TargetType="{x:Type Button}"> <Setter Property="SnapsToDevicePixels" Value="True"/> <Setter Property="VerticalAlignment" Value="Center"/> <Setter Property="HorizontalAlignment" Value="Stretch"/> <Setter Property="Width" Value="200"/> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type Button}"> <Border Width="{TemplateBinding Width}"> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="auto"/> <ColumnDefinition Width="*"/> <ColumnDefinition Width="5"/> </Grid.ColumnDefinitions> <Rectangle Name="Rectagle" Grid.Column="0" Margin="2.5" Height="30" Width="30" Fill="White"> <Rectangle.OpacityMask> <VisualBrush Visual="{TemplateBinding local:MyButtonExtension.Icon}" Stretch="Fill"/> </Rectangle.OpacityMask> </Rectangle> <TextBlock Name="Name" Grid.Column="1" Margin="5" VerticalAlignment="Center" HorizontalAlignment="Stretch" Foreground="White" FontSize="12" Text="{TemplateBinding Content}"/> <Rectangle Name="Flag" Grid.Column="2" Fill="Transparent"/> </Grid> </Border> <ControlTemplate.Triggers> <Trigger Property="IsMouseOver" Value="True"> <Setter TargetName="Flag" Property="Fill" Value="{StaticResource MainBrush}"/> <Setter TargetName="Rectagle" Property="Fill" Value="{StaticResource MainBrush}"/> <Setter TargetName="Name" Property="Foreground" Value="{StaticResource MainBrush}"/> </Trigger> </ControlTemplate.Triggers> </ControlTemplate> </Setter.Value> </Setter> </Style> 

And here is how I use it:

 <Button Content="Создать конфигурацию" Command="{Binding CreateConfig}"> 

How to correctly implement such a style, with the ability to transfer to the Rectagle (inside the style) OpacityMask, in which there will be an icon.

While the solution I see is the creation of a separate control in which to create a property for the icon, but I think there is a more correct solution.

UPD 1:

Use check:

 <Button local:MyButtonExtension.Text="Создать конфигурацию" local:MyButtonExtension.Icon="{StaticResource appbar_newspaper_create}" Command="{Binding CreateConfig}"> </Button> 

Icon:

 <Canvas x:Key="appbar_newspaper_create" Width="76" Height="76" Clip="F1 M 0,0L 76,0L 76,76L 0,76L 0,0"> <Path Width="48.0313" Height="43" Canvas.Left="17" Canvas.Top="14" Stretch="Fill" Fill="{DynamicResource BlackBrush}" Data="(удалил, чтобы не засорять)"/> </Canvas> 
  • one
    Use Attached Property - Andrew NOP
  • @AndreyNOP, can you share an example / link / book where you can see about it? Google gives out not the best, in my opinion, options. I have not come across such things yet ... - UporotayaPanda

1 answer 1

Suppose I want all my buttons to have the same look — they contained a picture and a caption as content.

I am writing the first approximation:

 <Button Margin="2.5" VerticalContentAlignment="Stretch"> <Grid> <Grid.RowDefinitions> <RowDefinition/> <RowDefinition Height="Auto"/> </Grid.RowDefinitions> <Image Source="play.png"/> <TextBlock Grid.Row="1" HorizontalAlignment="Center" Text="Play"/> </Grid> </Button> 

Everything is simple and done literally what is required by the condition.

Now we go further, we want to hide all this Grid / Image / TextBlock subtree in style and parameterize them through properties. There are naturally no such properties in WPF, but there are means for creating additional properties: Attached Property. We look at what type of properties are needed, Text is understandable, string , and the Image.Source type can be found by placing the cursor on it in the markup and pressing F12 , as it turns out, this is an ImageSource . Ok, we write a class with two APs:

 public static class MyButtonExtension { public static string GetText(DependencyObject obj) => (string)obj.GetValue(TextProperty); public static void SetText(DependencyObject obj, string value) => obj.SetValue(TextProperty, value); public static readonly DependencyProperty TextProperty = DependencyProperty.RegisterAttached("Text", typeof(string), typeof(MyButtonExtension), new PropertyMetadata("")); public static ImageSource GetImageSource(DependencyObject obj) => (ImageSource)obj.GetValue(ImageSourceProperty); public static void SetImageSource(DependencyObject obj, ImageSource value) => obj.SetValue(ImageSourceProperty, value); public static readonly DependencyProperty ImageSourceProperty = DependencyProperty.RegisterAttached("ImageSource", typeof(ImageSource), typeof(MyButtonExtension), new PropertyMetadata(null)); } 

We compile the project so that the XAML designer will know about this class. Now you can set these properties for the button:

 c:MyButtonExtension.Text="Play" c:MyButtonExtension.ImageSource="play.png" 

Well, it is necessary that the values ​​of elements in the content of the button should be taken from these properties, rewritten using the bindings:

 <Button Margin="2.5" VerticalContentAlignment="Stretch" c:MyButtonExtension.Text="Play" c:MyButtonExtension.ImageSource="play.png"> <Grid> <Grid.RowDefinitions> <RowDefinition/> <RowDefinition Height="Auto"/> </Grid.RowDefinitions> <Image Source="{Binding (c:MyButtonExtension.ImageSource), RelativeSource={RelativeSource AncestorType=Button}}"/> <TextBlock Grid.Row="1" HorizontalAlignment="Center" Text="{Binding (c:MyButtonExtension.Text), RelativeSource={RelativeSource AncestorType=Button}}"/> </Grid> </Button> 

Make sure it works, run the application:

enter image description here

Now you can put it all into a template / style (I only show a piece):

 ... <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type Button}"> <Themes:ButtonChrome x:Name="Chrome" BorderBrush="{TemplateBinding BorderBrush}" Background="{TemplateBinding Background}" RenderMouseOver="{TemplateBinding IsMouseOver}" RenderPressed="{TemplateBinding IsPressed}" RenderDefaulted="{TemplateBinding IsDefaulted}" SnapsToDevicePixels="true"> <Grid> <Grid.RowDefinitions> <RowDefinition/> <RowDefinition Height="Auto"/> </Grid.RowDefinitions> <Image Source="{Binding (c:MyButtonExtension.ImageSource), RelativeSource={RelativeSource AncestorType={x:Type Button}}}"/> <TextBlock Grid.Row="1" HorizontalAlignment="Center" Text="{Binding (c:MyButtonExtension.Text), RelativeSource={RelativeSource AncestorType={x:Type Button}}}"/> </Grid> </Themes:ButtonChrome> ... 

Great, and now, since we're inside the ControlTemplate , we can rewrite these bindings easier and more conveniently using TemplateBinding :

 ... <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type Button}"> <Themes:ButtonChrome x:Name="Chrome" BorderBrush="{TemplateBinding BorderBrush}" Background="{TemplateBinding Background}" RenderMouseOver="{TemplateBinding IsMouseOver}" RenderPressed="{TemplateBinding IsPressed}" RenderDefaulted="{TemplateBinding IsDefaulted}" SnapsToDevicePixels="true"> <Grid> <Grid.RowDefinitions> <RowDefinition/> <RowDefinition Height="Auto"/> </Grid.RowDefinitions> <Image Source="{TemplateBinding c:MyButtonExtension.ImageSource}"/> <TextBlock Grid.Row="1" HorizontalAlignment="Center" Text="{TemplateBinding c:MyButtonExtension.Text}"/> </Grid> </Themes:ButtonChrome> ... 

Done!

 <UniformGrid Margin="5" Rows="3" Columns="4"> <Button c:MyButtonExtension.Text="Play" c:MyButtonExtension.ImageSource="play.png" Style="{DynamicResource MyButtonStyle}"/> <Button c:MyButtonExtension.Text="Pause" c:MyButtonExtension.ImageSource="pause.png" Style="{DynamicResource MyButtonStyle}"/> <Button c:MyButtonExtension.Text="Stop" c:MyButtonExtension.ImageSource="stop.png" Style="{DynamicResource MyButtonStyle}"/> </UniformGrid> 

enter image description here

  • Comments are not intended for extended discussion; conversation moved to chat . - Yuriy SPb