Есть много кнопок с одинаковым стилем, в которых изменяется только иконка и название и toolTip. Как создать универсальный стиль для них?
Вот стиль:
<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>
И вот, как я его использую:
<Button Content="Создать конфигурацию"
Command="{Binding CreateConfig}">
Как ПРАВИЛЬНО реализовать такой стиль, с возможностью передачи в Rectagle(внутри стиля) OpacityMask, в которой будет иконка.
Пока решением вижу создание отдельного контрола, в котором создать свойство для иконки, но думаю есть более верное решение.
UPD 1:
Использование проверти:
<Button local:MyButtonExtension.Text="Создать конфигурацию"
local:MyButtonExtension.Icon="{StaticResource appbar_newspaper_create}"
Command="{Binding CreateConfig}">
</Button>
Иконка:
<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>
Допустим, я хочу чтобы все мои кнопки имели выглядели одинаково – содержали в качестве контента картинку и подпись под ней.
Пишу первое приближение:
<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>
Всё просто и выполнено дословно то что требуется по условию.
Теперь идем дальше, мы хотим спрятать всё это поддерево Grid/Image/TextBlock в стиль и параметризировать их через свойства. Штатных таких свойств в WPF, естественно, нет, но есть средства для создания дополнительных свойств: Attached Property. Смотрим какого типа нужны свойства, Text
– понятно, string
, а тип Image.Source
можно узнать поставив курсор на него в разметке и нажав F12, как выясняется, это ImageSource
. Отлично, пишем класс с двумя AP:
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));
}
Компилируем проект, чтобы дизайнер XAML узнал об этом классе. Теперь можно кнопке задать эти свойства:
c:MyButtonExtension.Text="Play"
c:MyButtonExtension.ImageSource="play.png"
Ну и нужно чтобы значения элементов в контенте кнопки брались именно из этих свойств, переписываем с использованием привязок:
<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>
Убеждаемся что это работает, запускаем приложение:
Теперь можно вынести это всё в шаблон/стиль (я показываю лишь кусочек):
...
<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>
...
Отлично, ну и теперь, так как мы находимся внутри ControlTemplate
, мы можем переписать эти привязки проще и удобнее, с использованием 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>
...
Готово!
<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>
Айфон мало держит заряд, разбираемся с проблемой вместе с AppLab
Перевод документов на английский язык: Важность и ключевые аспекты
Пробовал вернуть HBITMAP из unmanaged dll на c++, но его C# не хочет переваривать, описал его как возвращаемый IntPtr, пишет о невозможности преобразования...
Как в C# WPF Prism занять один регион другим? Пример на Word'e: