Как изменить стиль кнопки wpf

Would somebody know how to recreate this button style in WPF? As I do not know how to make the different compartments. As well as the 2 different texts and text styles?

To solve your question definitely need to use the Style and Template for the Button. But how exactly does he look like? Decisions may be several. For example, Button are two texts to better define the relevant TextBlocks? Can be directly in the template, but then use the buttons will be limited, because the template can be only one ContentPresenter. I decided to do things differently, to identify one ContentPresenter with an icon in the form of a Path, and the content is set using the buttons on the side.

The style:

<Style TargetType="{x:Type Button}">
    <Setter Property="Background" Value="#373737" />
    <Setter Property="Foreground" Value="White" />
    <Setter Property="FontSize" Value="15" />
    <Setter Property="SnapsToDevicePixels" Value="True" />

    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type Button}">
                <Border CornerRadius="4" Background="{TemplateBinding Background}">
                    <Grid>
                        <Path x:Name="PathIcon" Width="15" Height="25" Stretch="Fill" Fill="#4C87B3" HorizontalAlignment="Left" Margin="17,0,0,0" Data="F1 M 30.0833,22.1667L 50.6665,37.6043L 50.6665,38.7918L 30.0833,53.8333L 30.0833,22.1667 Z "/>
                        <ContentPresenter x:Name="MyContentPresenter" Content="{TemplateBinding Content}" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="0,0,0,0" />                                
                    </Grid>
                </Border>

                <ControlTemplate.Triggers>
                    <Trigger Property="IsMouseOver" Value="True">
                        <Setter Property="Background" Value="#E59400" />
                        <Setter Property="Foreground" Value="White" />
                        <Setter TargetName="PathIcon" Property="Fill" Value="Black" />
                    </Trigger>

                    <Trigger Property="IsPressed" Value="True">
                        <Setter Property="Background" Value="OrangeRed" />
                        <Setter Property="Foreground" Value="White" />
                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

Sample of using:

<Button Width="200" Height="50" VerticalAlignment="Top" Margin="0,20,0,0" />
    <Button.Content>
        <StackPanel>
            <TextBlock Text="Watch Now" FontSize="20" />
            <TextBlock Text="Duration: 50m" FontSize="12" Foreground="Gainsboro" />
        </StackPanel>
    </Button.Content>
</Button>

Output

enter image description here

It is best to StackPanel determine the Resources and set the Button so:

<Window.Resources>
    <StackPanel x:Key="MyStackPanel">
        <TextBlock Name="MainContent" Text="Watch Now" FontSize="20" />
        <TextBlock Name="DurationValue" Text="Duration: 50m" FontSize="12" Foreground="Gainsboro" />
    </StackPanel>
</Window.Resources>

<Button Width="200" Height="50" Content="{StaticResource MyStackPanel}" VerticalAlignment="Top" Margin="0,20,0,0" />

The question remains with setting the value for TextBlock Duration, because this value must be dynamic. I implemented it using attached DependencyProperty. Set it to the window, like that:

<Window Name="MyWindow" local:MyDependencyClass.CurrentDuration="Duration: 50m" ... />

Using in TextBlock:

<TextBlock Name="DurationValue" Text="{Binding ElementName=MyWindow, Path=(local:MyDependencyClass.CurrentDuration)}" FontSize="12" Foreground="Gainsboro" />

In fact, there is no difference for anyone to determine the attached DependencyProperty, because it is the predominant feature.

Example of set value:

private void Button_Click(object sender, RoutedEventArgs e)
{
    MyDependencyClass.SetCurrentDuration(MyWindow, "Duration: 101m");
}

A complete listing of examples:

XAML

<Window x:Class="ButtonHelp.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:ButtonHelp"
    Name="MyWindow"
    Title="MainWindow" Height="350" Width="525"
    WindowStartupLocation="CenterScreen"
    local:MyDependencyClass.CurrentDuration="Duration: 50m">

<Window.Resources>
    <Style TargetType="{x:Type Button}">
        <Setter Property="Background" Value="#373737" />
        <Setter Property="Foreground" Value="White" />
        <Setter Property="FontSize" Value="15" />
        <Setter Property="FontFamily" Value="./#Segoe UI" />
        <Setter Property="SnapsToDevicePixels" Value="True" />

        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type Button}">
                    <Border CornerRadius="4" Background="{TemplateBinding Background}">
                        <Grid>
                            <Path x:Name="PathIcon" Width="15" Height="25" Stretch="Fill" Fill="#4C87B3" HorizontalAlignment="Left" Margin="17,0,0,0" Data="F1 M 30.0833,22.1667L 50.6665,37.6043L 50.6665,38.7918L 30.0833,53.8333L 30.0833,22.1667 Z "/>
                            <ContentPresenter x:Name="MyContentPresenter" Content="{TemplateBinding Content}" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="0,0,0,0" />                                
                        </Grid>
                    </Border>

                    <ControlTemplate.Triggers>
                        <Trigger Property="IsMouseOver" Value="True">
                            <Setter Property="Background" Value="#E59400" />
                            <Setter Property="Foreground" Value="White" />
                            <Setter TargetName="PathIcon" Property="Fill" Value="Black" />
                        </Trigger>

                        <Trigger Property="IsPressed" Value="True">
                            <Setter Property="Background" Value="OrangeRed" />
                            <Setter Property="Foreground" Value="White" />
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

    <StackPanel x:Key="MyStackPanel">
        <TextBlock Name="MainContent" Text="Watch Now" FontSize="20" />
        <TextBlock Name="DurationValue" Text="{Binding ElementName=MyWindow, Path=(local:MyDependencyClass.CurrentDuration)}" FontSize="12" Foreground="Gainsboro" />
    </StackPanel>
</Window.Resources>

<Grid>        
    <Button Width="200" Height="50" Content="{StaticResource MyStackPanel}" VerticalAlignment="Top" Margin="0,20,0,0" />

    <Button Content="Set some duration" Style="{x:Null}" Width="140" Height="30" VerticalAlignment="Top" HorizontalAlignment="Left" Click="Button_Click" />
</Grid>

Code behind

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        MyDependencyClass.SetCurrentDuration(MyWindow, "Duration: 101m");
    }
}

public class MyDependencyClass : DependencyObject
{
    public static readonly DependencyProperty CurrentDurationProperty;        

    public static void SetCurrentDuration(DependencyObject DepObject, string value)
    {
        DepObject.SetValue(CurrentDurationProperty, value);
    }

    public static string GetCurrentDuration(DependencyObject DepObject)
    {
        return (string)DepObject.GetValue(CurrentDurationProperty);
    }

    static MyDependencyClass()
    {
        PropertyMetadata MyPropertyMetadata = new PropertyMetadata("Duration: 0m");

        CurrentDurationProperty = DependencyProperty.RegisterAttached("CurrentDuration",
                                                            typeof(string),
                                                            typeof(MyDependencyClass),
                                                            MyPropertyMetadata);
    }
}

Я думаю что каждый (или почти каждый), из тех, кто приходит в WPF из WinForms, поначалу испытывает растерянность по поводу функционала стандартных контролов.
Казалось бы – вот он — знакомый контрол.
Он очень похож на старого знакомого из WinForms. Даже сигнатура обычных методов либо полностью совпадает, либо претерпела незначительную трансформацию (ну, например, свойство Enabled получило приставку Is). Настроек у контролов много, от параметров визуализации рябит в глазах.
Но при чуть более тесном знакомстве и попытке натянуть привычные способы построения интерфейса на XAML и приходит та самая растерянность.

Как же так? Ну неужели у кнопки нет свойства Image? Вы ведь шутите, правда?

Все дело в том, что у WPF (точнее у XAML) совершенно иная идеология организации интерфейса. Базовые контролы представляют лишь базовый (простите за тавтологию) функционал. Простота базовых контролов компенсируется мощными механизмами шаблонов и стилей.
Существуют и сторонние библиотеки компонентов, но они, чаще всего, либо бесполезны, либо безнадежно устарели, либо сильно платные.

Не так давно я в очередной раз столкнулся с необходимостью решения этой очень простой (казалось бы) задачи. Я истерзал весь гугл запросами типа “XAML button with image” “WPF button image text” и т.д.

Среди десятков просмотренных результатов нашлись очевидные как очевидные (и при этом неудобные) пути решения, так и более изощренные.

Небольшое отступление номер 1
После первых же экспериментов стало очевидно, что XAML и иконки в виде png – вещи несовместимые. Не буду долго растекаться почему так – литературы на эту тему хватает, скажу только что в итоге получается и некрасиво, и неудобно, и нефункционально. Картинки размытые, наложенные эффекты и анимация выглядят удручающе и т.д…
Но не стоит огорчаться – в сети десятки и сотни ресурсов с векторными картинками.
Лучшее из того, что я нашел – SyncFusion Metro Studio 2 (не реклама). Это бесплатный продукт, в котором есть 1700 векторных иконок и средства вывода этих иконок в XAML. Результат получается в виде сложного элемента, из которого достаточно скопировать лишь Path, который описывает саму геометрию иконки.
С этим элементом я поступаю так – в проект добавляю ResourceDictionary с именем Icons.xaml и кладу в него все нужные мне иконки:

<ResourceDictionary
	xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
	xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
	<!-- Resource dictionary entries should be defined here. -->
	
    <Path x:Key="IconTriangle" x:Shared="False" Stretch="Uniform" Data="M50.25,133.5 L49.75,158.25 76,147.25 z" Fill="Black" Stroke="Black"/>
</ResourceDictionary>

Но давайте вернемся к способам реализации кнопки с иконкой.

Первый и самый очевидный способ – описать нужный Content кнопки прямо в коде формы

        <Button 
            HorizontalAlignment="Center" 
            VerticalAlignment="Center">
            <StackPanel Orientation="Horizontal">
                <ContentControl Width="16" Height="16" Margin="4" Content="{StaticResource IconTriangle}"/>
                <TextBlock HorizontalAlignment="Center" VerticalAlignment="Center">Button</TextBlock>
            </StackPanel>
        </Button>


Ну, казалось бы, всё на месте. Есть кнопка, у нее есть иконка и текст. Тех, кто поверит в то, что всё так просто и попытается организовать таким подходом красивый и стильный интерфейс, очень скоро ждёт разочарование. И вот почему: простая настройка стиля приводит к распуханию XAML за счет дублирования описания параметров элементов прямо в коде формы. 10 кнопок – 10 идентичных описаний. Простое изменение типа «а давайте-ка покрасим текст на кнопках в зеленый цвет» превращается в утомительный копипаст и еще большее распухание формы.

Второй очевидный способ — наследование от Button

А давайте напишем «свою кнопку с блекджеком и сами знаете с чем еще»?
Наследуемся от Button и добавляем DependencyProperty для ContentControl, через который из XAML формы можно будет задать содержимое для иконки. Не буду останавливаться на подробностях реализации (внизу будет ссылка на источники, там можно будет почитать), но опишу минусы – содержимое кнопки придется задавать из конструктора наследника, на C#. Отсюда получаем массу очевидных и неочевидных проблем, не говоря уже о том, что это не очень хорошо пахнет.

Третий очевидный способ — создадим UserControl.

Создадим UserControl, на который покладем одну лишь кнопку. В UserControl создадим DependencyProperty, через которое будем задавать иконку для ContentControl, который лежит в кнопке. Этот способ по праву заслуживает медаль за максимальную корявость. Он наследует почти все недостатки предыдущих способов, и добавляет множество собственных. В коде формы мы получаем UserControl, но теряем кнопку. Теряем вместе со всеми свойствами и событиями. Автор идеи предлагает вытащить все, что было потеряно, через те самые DependencyProperty, в общем вы поняли. Становится непонятно за что же мы боролись.

Четвёртый способ — AttachedProperty

Этот способ я отношу к неочевидным и изощренным. В оригинале статьи автор предлагает задавать картинку через AttachedProperty. Забегая вперед скажу, что именно этот способ я и выбрал для использования в своем продукте и именно его я опишу максимально подробно. Он не лишен некоторых недостатков на этапе разработки (опишу ниже), но всё же мне он понравился больше остальных. В оригинале автор использовал иконку в виде картинки png, я же модифицировал способ для использования векторной иконки и добавил плюшек.

Итак, совсем немного теории. Что же такое это самое AttachedProperty

Каждый XAML разработчик сталкивался с Attached свойствами когда, например, задавал контролу свойство Grid.Column.
Если в трёх словах – то это своей идее немного похоже на Extension из Linq. Можно зарегистрировать свойство, значение которого можно задать любому DependencyObject. Выглядит это примерно так (пример из MSDN):

public class AquariumObject
{
    public static readonly DependencyProperty IsBubbleSourceProperty = DependencyProperty.RegisterAttached(
		"IsBubbleSource",
		typeof(Boolean),
		typeof(AquariumObject),
		new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.AffectsRender)
    );
    public static void SetIsBubbleSource(UIElement element, Boolean value)
    {
    	element.SetValue(IsBubbleSourceProperty, value);
    }
    public static Boolean GetIsBubbleSource(UIElement element)
    {
	return (Boolean)element.GetValue(IsBubbleSourceProperty);
    } 
}

В этом коде регистрируется свойство IsBubleSource. В результате любому DependencyObject, например тому же Button, можно задать его значение:

<Button AquariumObject.IsBubbleSource="True">Button</Button>

Общий смысл этого кода – при задании свойства IsBubbleSource для кнопки мы автоматически попадаем в метод SetIsBubbleSource, который устанавливает значение. При получении значения, соответственно, попадаем в метод GetIsBubbleSource. Это все происходит автоматически, достаточно лишь написать методы с именами Set и Get, остальное – дело платформы.

Несмотря на то, что написано не так уж и мало кода, самому Button от такой операции ни жарко ни холодно – он просто становится хранилищем обособленного значения, которое можно задавать и спрашивать. Конечно, можно реализовать в методах SetIsBubbleSource и GetIsBubbleSource хитрую логику, которая будет приводить element к Button, доставать из него содержимое, и производить с содержимым различные операции, но это опять плохо пахнет, делать так не нужно.

Приступаем к практической части

Небольшое отступление 2
В оригинале автор использует имя класса EyeCandy и namespace проекта, но это слишком длинно и я надеюсь, что мне простят сокращение – namespace Ext и имя класса E.

В проект WPF добавляем следующий класс:

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

namespace Ext
{
	public class E
	{
		public static readonly DependencyProperty IconProperty;

		static E()
		{
			var metadata = new FrameworkPropertyMetadata(null);
			IconProperty = DependencyProperty.RegisterAttached("Icon",
	typeof(FrameworkElement),
	typeof(E), metadata);

		}

		public static FrameworkElement GetIcon(DependencyObject obj)
		{
			return (FrameworkElement)obj.GetValue(IconProperty);
		}

		public static void SetIcon(DependencyObject obj, FrameworkElement value)
		{
			obj.SetValue(IconProperty, value);
		}
       }
}

Что же здесь происходит? Мы зарегистрировали Attached свойство Icon типа FrameworkElement со значением по умолчанию равным null.

Теперь создадим шаблон для нашей кнопки. Я не буду останавливаться на объяснения «что такое шаблоны и как они работают» – если вдруг кому-то это неизвестно – информации в сети очень много.
Итак, добавляем в наш проект ResourceDictionary с именем Styles.xaml (если вдруг в проекте еще нет ресурса стилей). В этом ResourceDictionary добавим следующий код:

<ResourceDictionary 
	xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
	xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
	xmlns:Microsoft_Windows_Themes="clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Aero">
	<Style x:Key="ButtonFocusVisual">
		<Setter Property="Control.Template">
			<Setter.Value>
				<ControlTemplate>
					<Rectangle Margin="2" SnapsToDevicePixels="true" Stroke="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" StrokeThickness="1" StrokeDashArray="1 2"/>
				</ControlTemplate>
			</Setter.Value>
		</Setter>
	</Style>
	<!-- Resource dictionary entries should be defined here. -->
	<LinearGradientBrush x:Key="ButtonNormalBackground" EndPoint="0,1" StartPoint="0,0">
		<GradientStop Color="#F3F3F3" Offset="0"/>
		<GradientStop Color="#EBEBEB" Offset="0.5"/>
		<GradientStop Color="#DDDDDD" Offset="0.5"/>
		<GradientStop Color="#CDCDCD" Offset="1"/>
	</LinearGradientBrush>
	<SolidColorBrush x:Key="ButtonNormalBorder" Color="#FF707070"/>
	<Style TargetType="{x:Type Button}">
		<Setter Property="FocusVisualStyle" Value="{StaticResource ButtonFocusVisual}"/>
		<Setter Property="Background" Value="{StaticResource ButtonNormalBackground}"/>
		<Setter Property="BorderBrush" Value="{StaticResource ButtonNormalBorder}"/>
		<Setter Property="BorderThickness" Value="1"/>
		<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
		<Setter Property="HorizontalContentAlignment" Value="Center"/>
		<Setter Property="VerticalContentAlignment" Value="Center"/>
		<Setter Property="Padding" Value="1"/>
		<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">
						<VisualStateManager.VisualStateGroups>
							<VisualStateGroup x:Name="CommonStates">
								<VisualState x:Name="Normal"/>
								<VisualState x:Name="MouseOver"/>
								<VisualState x:Name="Pressed"/>
								<VisualState x:Name="Disabled">
									<Storyboard>
	<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="IconContent">
											<EasingDoubleKeyFrame KeyTime="0" Value="0.5"/>
										</DoubleAnimationUsingKeyFrames>
									</Storyboard>
								</VisualState>
							</VisualStateGroup>
						</VisualStateManager.VisualStateGroups>
						<StackPanel>
							<ContentControl 
								Content="{StaticResource IconTriangle}"
								Width="16"
								Height="16"
								x:Name="IconContent" Margin="4" RenderTransformOrigin="0.5,0.5" IsEnabled="{Binding IsEnabled, ElementName=Chrome}">
								<ContentControl.Effect>
									<DropShadowEffect Opacity="0" BlurRadius="2"/>
								</ContentControl.Effect>
								<ContentControl.RenderTransform>
									<TransformGroup>
										<ScaleTransform/>
										<SkewTransform/>
										<RotateTransform/>
										<TranslateTransform/>
									</TransformGroup>
								</ContentControl.RenderTransform>
							</ContentControl>
							<TextBlock x:Name="textBlock" Margin="4" TextWrapping="Wrap" Text="{TemplateBinding Content}" VerticalAlignment="Center" HorizontalAlignment="Center"/>
						</StackPanel>
					</Microsoft_Windows_Themes:ButtonChrome>
					<ControlTemplate.Triggers>
						<Trigger Property="IsKeyboardFocused" Value="true">
							<Setter Property="RenderDefaulted" TargetName="Chrome" Value="true"/>
						</Trigger>
						<Trigger Property="ToggleButton.IsChecked" Value="true">
							<Setter Property="RenderPressed" TargetName="Chrome" Value="true"/>
						</Trigger>
						<Trigger Property="IsEnabled" Value="false">
							<Setter Property="Foreground" Value="#ADADAD"/>
						</Trigger>
					</ControlTemplate.Triggers>
				</ControlTemplate>
			</Setter.Value>
		</Setter>
	</Style>
</ResourceDictionary>

Эта запись ResourceDictionary описывает шаблон любой кнопки нашего проекта. В этом шаблоне задается обычное оформление для кнопки WPF, но переопределяется ее содержимое. В качестве содержимого выступает StackPanel, в котором лежат ContentControl и TextBlock, т.е. точно так же, как и в самом первом примере. Кроме этого в шаблоне я задал следующее поведение для иконки – если для кнопки задано IsEnabled == False, то иконка получает прозрачность 50%, и становится похожей на неактивную.

Добавим на нашу форму 4 простые кнопки. Назначим каждой кнопке свой текст, например вот так: Content=«Кнопка 1».
Запускаем приложение.

Идентичные иконки на каждой кнопке приложения – это не то, чего мы добиваемся, и именно здесь мы пускаем в дело наше секретное оружие – класс Ext.E и механизм AttachedProperty.

Итак, идем в наш ресурсный файл Styles.xaml и добавляем в него новый namespace:

xmlns:Ext="clr-namespace:Ext"

После этого опускаемся ниже и в шаблоне кнопки находим строчку, в которой создается ContentControl и задается его содержимое:

<ContentControl 
	Content="{StaticResource IconTriangle}" 
        .../>

Заменяем вторую строчку:

<ContentControl 
	Content="{Binding (Ext:E.Icon), RelativeSource={RelativeSource TemplatedParent}}"
        .../>

Эта строчка заставляет ContentControl обратиться к свойству Ext.E.Icon у кнопки и получить из него своё содержимое. После этого остается добавить код, устанавливающий значение свойства Ext.E.Icon в саму кнопку. Делается это в коде формы, на которой создается кнопка.

<Button 
	Ext:E.Icon="{StaticResource IconTriangle}"
	Content="Кнопка 1" />

Примитивный вариант кнопки с иконкой готов. Меняя значение IconTriangle на имена других ресурсов, можно задавать различные иконки на кнопках. При этом, в отличии от первых трех способов, мы сохраняем у кнопки все ее врожденные способности к стилизации (за исключением возможности менять структуру Content, само собой). Содержимое кнопки задается не из C#, и все свойства с событиями остались на своем месте.

Пойдем немного дальше

Если мы попытаемся использовать эту кнопку в реальном проекте, то столкнемся вот с чем:

  • Размер иконки не настраивается.
  • Ориентация кнопки (вертикальная или горизонтальная) не настраивается.

Если точнее, то все настраивается, но только в шаблоне, т.е. для всех кнопок сразу, но клонирование шаблонов – это грабли и сплошное неудобство. Боролись мы не за это.
Расширим класс Ext.E. Добавим туда еще два AttachedProperty

  • IconSize типа double
  • Orientation типа Orientation

Исходный код одним архивом будет в конце статьи, поэтому я не буду дублировать аналогичные методы класса Ext.E в статье.

Опишу лишь изменения, которые нужно сделать в шаблоне Button.
Размеры ContentControl связываем со значением IconSize:

<ContentControl 
	Content="{Binding (Ext:E.Icon), RelativeSource={RelativeSource TemplatedParent}}"
	Width="{Binding (Ext:E.IconSize), RelativeSource={RelativeSource TemplatedParent}}"
	Height="{Binding (Ext:E.IconSize), RelativeSource={RelativeSource TemplatedParent}}"
        .../>

Ориентацию StackPanel связываем со значением Orientation

<StackPanel Orientation="{Binding (Ext:E.Orientation), RelativeSource={RelativeSource TemplatedParent}}">

В результате кнопка получила дополнительные параметры, и мы можем написать вот так:

<Button 
	Ext:E.Icon="{StaticResource IconTriangle}"
	Ext:E.IconSize="32"
	Ext:E.Orientation="Horizontal"
Content="Кнопка 1"/>

В результате нехитрых манипуляций можно получить вот такой зоопарк (первая кнопкаIsEnabled=«False»):
»

Ну и напоследок упомяну об ограничениях

Все они касаются процесса и средств разработки:
— XAML дизайнер VisualStudio 2010 реагирует на подобное описание кнопки как-то так:

— Blend 4 и VisualStudio 2012 ведут себя лучше, но тоже с особенностями:

  • После изменения класса Ext.E лучше перезагрузить среду разработки. Без этого изменения чаще всего не определяются и дизайнеры ругаются на то, что добавленные или измененные свойства не существуют.
  • По неустановленному мною алгоритму значения, заданные кнопке с помощью AttachedProperty, то видны дизайнеру, то нет. Чаще не видны, и форма выглядит как-то так:

Но касается это только Designer, в режиме выполнения приложения всё работает как нужно.

Возможно (и даже скорее всего) я описал велосипед, но тот факт что за два дня поисков я не нашел более приемлемой бесплатной реализации говорит о том, что на этом фронте не всё ладно.
Кроме того я получил возможность немного разобраться с механизмом расширения стандартных контролов нестандартным способом и применений этому механизму можно найти массу.

Благодарю за внимание.

UPD: Статья обновлена в связи с тем, что было найдено решение проблемы с использованием одинаковой иконки на разных кнопках.
UPD2: Благодарю onikiychuka за дельное предложение.

Исходный код

Полезные ссылки:

WPF Control Development — 3 Ways to build an ImageButton
blogs.msdn.com/b/knom/archive/2007/10/31/wpf-control-development-3-ways-to-build-an-imagebutton.aspx
Using Attached Properties to Create a WPF Image Button
www.hardcodet.net/2009/01/create-wpf-image-button-through-attached-properties
Пользовательские свойства зависимостей
msdn.microsoft.com/ru-ru/library/ms753358.aspx
SyncFusion Metro Studio 2
www.syncfusion.com/downloads/metrostudio

I am trying to apply a style to button how can we achieve this?

Below is my sample XAML, but it’s not working

  <Grid>
    <Button Width="150" Height="50">
        <Button.Template>
            <ControlTemplate>
                <Label Content="Helllo"/>
            </ControlTemplate>
        </Button.Template>

        <Button.Style>
            <Style TargetType="Button">
                <Setter Property="Background" Value="Green"/>  
                <Style.Triggers>
                    <Trigger Property="IsMouseOver" Value="True">
                        <Setter Property="Background" Value="AliceBlue"/>
                    </Trigger>
                </Style.Triggers>
            </Style>
        </Button.Style>

    </Button>
</Grid>

asked May 24, 2014 at 3:41

superuser's user avatar

2

Nothing wrong in your style placement.

ControlTemplate is overridden so you need to template bind background property of Label with button’s background property.

This is how you do it:

<ControlTemplate>
    <Label Content="Helllo" Background="{TemplateBinding Background}"/>
</ControlTemplate>

Community's user avatar

answered May 24, 2014 at 8:02

Rohit Vats's user avatar

Rohit VatsRohit Vats

78.7k12 gold badges156 silver badges184 bronze badges

Use ContentTemplate in place of ControlTemplate it will keep your button events stick to its style.And for reference see Difference between ContentTemplate and Template

 <Button Width="150" Height="50">
    <Button.ContentTemplate>
            <DataTemplate>
               <Label Content="Helllo"/>
            </DataTemplate>
        </Button.ContentTemplate>

    <Button.Style>
        <Style TargetType="Button">
            <Setter Property="Background" Value="Green"/>  
            <Style.Triggers>
                <Trigger Property="IsMouseOver" Value="True">
                    <Setter Property="Background" Value="AliceBlue"/>
                </Trigger>
            </Style.Triggers>
        </Style>
    </Button.Style>

</Button>

Community's user avatar

answered May 24, 2014 at 11:16

loop's user avatar

looploop

8,88210 gold badges39 silver badges76 bronze badges

When you are overriding control template,you need to implement the whole default control template for that control.
Here’s link: http://msdn.microsoft.com/en-us/library/ms753328%28v=vs.110%29.aspx

I am providing sample code which is working fine for me:

<Grid>
    <Grid.Resources>
        <SolidColorBrush x:Key="ControlBackground_MouseOver" Color="AliceBlue"/>
    </Grid.Resources>
        <Button Width="150" Height="50" Content="Hello" >
        <Button.Template>

                    <ControlTemplate  TargetType="Button">
                        <Grid>
                            <VisualStateManager.VisualStateGroups>
                                <VisualStateGroup x:Name="CommonStates">
                                    <VisualState x:Name="Normal"/>
                                    <VisualState x:Name="MouseOver">

                                        <Storyboard>

                                            <ObjectAnimationUsingKeyFrames Storyboard.TargetName="Border" Storyboard.TargetProperty="Background" Duration="0:0:0">
                                            <DiscreteObjectKeyFrame KeyTime="0:0:0" Value="{StaticResource ControlBackground_MouseOver}"/>
                                            </ObjectAnimationUsingKeyFrames>
                                        </Storyboard>
                                    </VisualState>

                                </VisualStateGroup>

                            </VisualStateManager.VisualStateGroups>
                            <Border x:Name="Border"
                BorderThickness="{TemplateBinding BorderThickness}"
                BorderBrush="{TemplateBinding BorderBrush}"
                Background="{TemplateBinding Background}"
                />
                            <ContentControl x:Name="ContentElement"
                IsTabStop="False"
                Content="{TemplateBinding Content}"
                ContentTemplate="{TemplateBinding ContentTemplate}"
                Foreground="{TemplateBinding Foreground}"
                HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                Margin="{TemplateBinding Padding}"
                VerticalAlignment="{TemplateBinding VerticalContentAlignment}">

                            </ContentControl>
                            <Border
                BorderThickness="1"

                Opacity="0"
                x:Name="FocusState"
                />
                        </Grid>
                    </ControlTemplate>

        </Button.Template>

        <Button.Style>
            <Style TargetType="Button">
                <Setter Property="Background" Value="Green"/>

            </Style>
        </Button.Style>

    </Button>

</Grid>

If you want to only display text on button,you can just use content property of button.
Please let me know if this was what you were looking for .
Thanks.

answered May 24, 2014 at 13:53

DT sawant's user avatar

DT sawantDT sawant

1,1912 gold badges9 silver badges21 bronze badges

I am trying to apply a style to button how can we achieve this?

Below is my sample XAML, but it’s not working

  <Grid>
    <Button Width="150" Height="50">
        <Button.Template>
            <ControlTemplate>
                <Label Content="Helllo"/>
            </ControlTemplate>
        </Button.Template>

        <Button.Style>
            <Style TargetType="Button">
                <Setter Property="Background" Value="Green"/>  
                <Style.Triggers>
                    <Trigger Property="IsMouseOver" Value="True">
                        <Setter Property="Background" Value="AliceBlue"/>
                    </Trigger>
                </Style.Triggers>
            </Style>
        </Button.Style>

    </Button>
</Grid>

asked May 24, 2014 at 3:41

superuser's user avatar

2

Nothing wrong in your style placement.

ControlTemplate is overridden so you need to template bind background property of Label with button’s background property.

This is how you do it:

<ControlTemplate>
    <Label Content="Helllo" Background="{TemplateBinding Background}"/>
</ControlTemplate>

Community's user avatar

answered May 24, 2014 at 8:02

Rohit Vats's user avatar

Rohit VatsRohit Vats

78.7k12 gold badges156 silver badges184 bronze badges

Use ContentTemplate in place of ControlTemplate it will keep your button events stick to its style.And for reference see Difference between ContentTemplate and Template

 <Button Width="150" Height="50">
    <Button.ContentTemplate>
            <DataTemplate>
               <Label Content="Helllo"/>
            </DataTemplate>
        </Button.ContentTemplate>

    <Button.Style>
        <Style TargetType="Button">
            <Setter Property="Background" Value="Green"/>  
            <Style.Triggers>
                <Trigger Property="IsMouseOver" Value="True">
                    <Setter Property="Background" Value="AliceBlue"/>
                </Trigger>
            </Style.Triggers>
        </Style>
    </Button.Style>

</Button>

Community's user avatar

answered May 24, 2014 at 11:16

loop's user avatar

looploop

8,88210 gold badges39 silver badges76 bronze badges

When you are overriding control template,you need to implement the whole default control template for that control.
Here’s link: http://msdn.microsoft.com/en-us/library/ms753328%28v=vs.110%29.aspx

I am providing sample code which is working fine for me:

<Grid>
    <Grid.Resources>
        <SolidColorBrush x:Key="ControlBackground_MouseOver" Color="AliceBlue"/>
    </Grid.Resources>
        <Button Width="150" Height="50" Content="Hello" >
        <Button.Template>

                    <ControlTemplate  TargetType="Button">
                        <Grid>
                            <VisualStateManager.VisualStateGroups>
                                <VisualStateGroup x:Name="CommonStates">
                                    <VisualState x:Name="Normal"/>
                                    <VisualState x:Name="MouseOver">

                                        <Storyboard>

                                            <ObjectAnimationUsingKeyFrames Storyboard.TargetName="Border" Storyboard.TargetProperty="Background" Duration="0:0:0">
                                            <DiscreteObjectKeyFrame KeyTime="0:0:0" Value="{StaticResource ControlBackground_MouseOver}"/>
                                            </ObjectAnimationUsingKeyFrames>
                                        </Storyboard>
                                    </VisualState>

                                </VisualStateGroup>

                            </VisualStateManager.VisualStateGroups>
                            <Border x:Name="Border"
                BorderThickness="{TemplateBinding BorderThickness}"
                BorderBrush="{TemplateBinding BorderBrush}"
                Background="{TemplateBinding Background}"
                />
                            <ContentControl x:Name="ContentElement"
                IsTabStop="False"
                Content="{TemplateBinding Content}"
                ContentTemplate="{TemplateBinding ContentTemplate}"
                Foreground="{TemplateBinding Foreground}"
                HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                Margin="{TemplateBinding Padding}"
                VerticalAlignment="{TemplateBinding VerticalContentAlignment}">

                            </ContentControl>
                            <Border
                BorderThickness="1"

                Opacity="0"
                x:Name="FocusState"
                />
                        </Grid>
                    </ControlTemplate>

        </Button.Template>

        <Button.Style>
            <Style TargetType="Button">
                <Setter Property="Background" Value="Green"/>

            </Style>
        </Button.Style>

    </Button>

</Grid>

If you want to only display text on button,you can just use content property of button.
Please let me know if this was what you were looking for .
Thanks.

answered May 24, 2014 at 13:53

DT sawant's user avatar

DT sawantDT sawant

1,1912 gold badges9 silver badges21 bronze badges

In this post I will demonstrate how to create a custom template for a WPF button using XAML. In particular we will look at being able to have complete control over all the visual states, including disabled, mouse over, mouse down and even the appearance of the focus rectangle.

Stage 1: Creating a style

The first thing to do is to create a Style which sets the properties of the Button that we want to customize. We will put this in the Page.Resources section of our XAML file.

<Style x:Key="OrangeButton" TargetType="Button">
<Setter Property="OverridesDefaultStyle" Value="True"/>
<Setter Property="Margin" Value="2"/>
<Setter Property="FontFamily" Value="Verdana"/>
<Setter Property="FontSize" Value="11px"/>
<Setter Property="FontWeight" Value="Bold"/>
<Setter Property="FocusVisualStyle" Value="{StaticResource MyFocusVisual}" />
<Setter Property="Background" >
   <Setter.Value>
       <LinearGradientBrush StartPoint="0,0" EndPoint="0,1" >
           <GradientStop Color="#FFFFD190" Offset="0.2"/>
           <GradientStop Color="Orange" Offset="0.85"/>
           <GradientStop Color="#FFFFD190" Offset="1"/>
       </LinearGradientBrush>
   </Setter.Value>
</Setter>

This is all fairly straightforward stuff — I am just setting the font and background gradient. The only complicated bit is that I am overriding the focus rectangle drawing, as I want a smaller rectangle than the one that gets drawn by default. So I need another style in my Page.Resources section:

<Style x:Key="MyFocusVisual">
     <Setter Property="Control.Template">
       <Setter.Value>
         <ControlTemplate TargetType="{x:Type Control}">
             <Grid Margin="3 2">
               <Rectangle Name="r1" StrokeThickness="1" Stroke="Black" StrokeDashArray="2 2"/>
               <Border Name="border" Width="{TemplateBinding ActualWidth}" Height="{TemplateBinding ActualHeight}"  CornerRadius="2" BorderThickness="1" />
             </Grid>
         </ControlTemplate>
       </Setter.Value>
     </Setter>
   </Style>

Stage 2: Creating a template

We would already be finished if I just wanted the normal appearance of my button to be changed. But I want complete control — including the appearance in mouse over, mouse down and disabled situations. So a template allows us to completely control what visual elements constitute our control.

Again this goes in Page.Resources. The first part is shown here:

<Setter Property="Template">
   <Setter.Value>
       <ControlTemplate TargetType="Button">
           <Border Name="border"
               BorderThickness="1"
               Padding="4,2"
               BorderBrush="DarkGray"
               CornerRadius="3"
               Background="{TemplateBinding Background}">
               <Grid >
               <ContentPresenter HorizontalAlignment="Center"
                              VerticalAlignment="Center" Name="contentShadow"
                   Style="{StaticResource ShadowStyle}">
                   <ContentPresenter.RenderTransform>
                       <TranslateTransform X="1.0" Y="1.0" />
                   </ContentPresenter.RenderTransform>
               </ContentPresenter>
               <ContentPresenter HorizontalAlignment="Center"
                           VerticalAlignment="Center" Name="content"/>
               </Grid>
           </Border>

Here you can see I have set up a simple border which gives my button rounded corners, a single pixel gray border, and uses the fill from the control’s Background property. This means that by default it will use the orange gradient specified in my style, but that users can override this for their own Background.

To draw the content (usually text), we use a ContentPresenter control. I am trying to do something clever here, which is create a bevelled effect on the text by drawing it again in gray. This works fine on text, but for some reason doesn’t do anything when the Content is a Shape. I’m not sure why that is. ShadowStyle is defined as follows:

<Style x:Key="ShadowStyle">
   <Setter Property="Control.Foreground" Value="LightGray" />
</Style>

Stage 3: Applying some triggers

Now we need to add some triggers to our Button template so that we can change its appearance on various events.

First is mouse over. When IsMouseOver becomes true, we change the colour of the border and the text colour to blue. Unfortunately, setting the Foreground property does nothing if the content is a shape.

<ControlTemplate.Triggers>
  <Trigger Property="IsMouseOver" Value="True">
     <Setter TargetName="border" Property="BorderBrush" Value="#FF4788c8" />
     <Setter Property="Foreground" Value="#FF4788c8" />
  </Trigger>

Next is mouse down. When IsPressed becomes true, we modify the background gradient so it looks like the button has gone ‘down’. I also move the text down one pixel.

<Trigger Property="IsPressed" Value="True">
   <Setter Property="Background" >
   <Setter.Value>
       <LinearGradientBrush StartPoint="0,0" EndPoint="0,1" >
           <GradientStop Color="#FFFFD190" Offset="0.35"/>
           <GradientStop Color="Orange" Offset="0.95"/>
           <GradientStop Color="#FFFFD190" Offset="1"/>
       </LinearGradientBrush>
   </Setter.Value>
   </Setter>
   <Setter TargetName="content" Property="RenderTransform" >
   <Setter.Value>
       <TranslateTransform Y="1.0" />
   </Setter.Value>
   </Setter>
</Trigger>

Third, we draw a dark gray border when a button is focussed or if it is the default button (the button that will be clicked when you press enter).

<Trigger Property="IsDefaulted" Value="True">
   <Setter TargetName="border" Property="BorderBrush" Value="#FF282828" />
</Trigger>
<Trigger Property="IsFocused" Value="True">
   <Setter TargetName="border" Property="BorderBrush" Value="#FF282828" />
</Trigger>

Finally, the when the button is disabled, we gray out the text and wash out the background a little by reducing its opacity.

<Trigger Property="IsEnabled" Value="False">
       <Setter TargetName="border" Property="Opacity" Value="0.7" />
       <Setter Property="Foreground" Value="Gray" />
   </Trigger>
</ControlTemplate.Triggers>

Stage 4: Using the control

Now we are ready to use our control. All we have to do is set the Style property of our button. We can override any of the settings such as font size if we like. The button will automatically resize itself to fit the content.

<StackPanel HorizontalAlignment="Center">
<Button Style="{StaticResource OrangeButton}">Hello</Button>
<Button Style="{StaticResource OrangeButton}">World</Button>
<Button Style="{StaticResource OrangeButton}" FontSize="20">Big Button</Button>
<Button Style="{StaticResource OrangeButton}" IsDefault="True">Default</Button>
<Button Style="{StaticResource OrangeButton}" IsEnabled="False">Disabled</Button>
<Button Style="{StaticResource OrangeButton}" Width="70" Height="30">70 x 30</Button>
<TextBox />
<Button Style="{StaticResource OrangeButton}" Width="30" Height="30">
<Path Fill="Black" Data="M 3,3 l 9,9 l -9,9 Z" />
</Button>
</StackPanel>

Here’s what it looks like:

image

Download an example XAML file here.

Вступление

Стиль позволяет полностью изменить внешний вид элемента управления WPF. Вот некоторые примеры некоторых базовых стилей и введение в ресурсные словари и анимацию.

Стилизация кнопки

Самый простой способ создать стиль — скопировать существующий и отредактировать его.

Создайте простое окно с двумя кнопками:

<Window x:Class="WPF_Style_Example.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d" ResizeMode="NoResize"
    Title="MainWindow"
    Height="150" Width="200">
<Grid>
    <Grid.RowDefinitions>
        <RowDefinition/>
        <RowDefinition/>
    </Grid.RowDefinitions>
    <Button Margin="5" Content="Button 1"/>
    <Button Margin="5" Grid.Row="1" Content="Button 2"/>
</Grid>

В Visual Studio копирование можно сделать, щелкнув правой кнопкой мыши на первой кнопке в редакторе и выбрав «Редактировать копию …» в меню «Редактировать шаблон».

Определите в «Заявке».

В следующем примере показан модифицированный шаблон для создания кнопки эллипса:

<Style x:Key="ButtonStyle1" TargetType="{x:Type Button}">
        <Setter Property="FocusVisualStyle" Value="{StaticResource FocusVisual}"/>
        <Setter Property="Background" Value="{StaticResource Button.Static.Background}"/>
        <Setter Property="BorderBrush" Value="{StaticResource Button.Static.Border}"/>
        <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
        <Setter Property="BorderThickness" Value="1"/>
        <Setter Property="HorizontalContentAlignment" Value="Center"/>
        <Setter Property="VerticalContentAlignment" Value="Center"/>
        <Setter Property="Padding" Value="1"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type Button}">
                    <Grid>
                        <Ellipse x:Name="ellipse" StrokeThickness="{TemplateBinding BorderThickness}" Stroke="{TemplateBinding BorderBrush}" Fill="{TemplateBinding Background}" SnapsToDevicePixels="true"/>
                        <ContentPresenter x:Name="contentPresenter" Focusable="False" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
                    </Grid>
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsDefaulted" Value="true">
                            <Setter Property="Stroke" TargetName="ellipse" Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}"/>
                        </Trigger>
                        <Trigger Property="IsMouseOver" Value="true">
                            <Setter Property="Fill" TargetName="ellipse" Value="{StaticResource Button.MouseOver.Background}"/>
                            <Setter Property="Stroke" TargetName="ellipse" Value="{StaticResource Button.MouseOver.Border}"/>
                        </Trigger>
                        <Trigger Property="IsPressed" Value="true">
                            <Setter Property="Fill" TargetName="ellipse" Value="{StaticResource Button.Pressed.Background}"/>
                            <Setter Property="Stroke" TargetName="ellipse" Value="{StaticResource Button.Pressed.Border}"/>
                        </Trigger>
                        <Trigger Property="IsEnabled" Value="false">
                            <Setter Property="Fill" TargetName="ellipse" Value="{StaticResource Button.Disabled.Background}"/>
                            <Setter Property="Stroke" TargetName="ellipse" Value="{StaticResource Button.Disabled.Border}"/>
                            <Setter Property="TextElement.Foreground" TargetName="contentPresenter" Value="{StaticResource Button.Disabled.Foreground}"/>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

Результат:

введите описание изображения здесь

Стиль, используемый для всех кнопок

В предыдущем примере удаление элемента x: Key стиля применяет стиль для всех кнопок в области Приложения.

<Style TargetType="{x:Type Button}">
        <Setter Property="FocusVisualStyle" Value="{StaticResource FocusVisual}"/>
        <Setter Property="Background" Value="{StaticResource Button.Static.Background}"/>
        <Setter Property="BorderBrush" Value="{StaticResource Button.Static.Border}"/>
        <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
        <Setter Property="BorderThickness" Value="1"/>
        <Setter Property="HorizontalContentAlignment" Value="Center"/>
        <Setter Property="VerticalContentAlignment" Value="Center"/>
        <Setter Property="Padding" Value="1"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type Button}">
                    <Grid>
                        <Ellipse x:Name="ellipse" StrokeThickness="{TemplateBinding BorderThickness}" Stroke="{TemplateBinding BorderBrush}" Fill="{TemplateBinding Background}" SnapsToDevicePixels="true"/>
                        <ContentPresenter x:Name="contentPresenter" Focusable="False" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
                    </Grid>
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsDefaulted" Value="true">
                            <Setter Property="Stroke" TargetName="ellipse" Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}"/>
                        </Trigger>
                        <Trigger Property="IsMouseOver" Value="true">
                            <Setter Property="Fill" TargetName="ellipse" Value="{StaticResource Button.MouseOver.Background}"/>
                            <Setter Property="Stroke" TargetName="ellipse" Value="{StaticResource Button.MouseOver.Border}"/>
                        </Trigger>
                        <Trigger Property="IsPressed" Value="true">
                            <Setter Property="Fill" TargetName="ellipse" Value="{StaticResource Button.Pressed.Background}"/>
                            <Setter Property="Stroke" TargetName="ellipse" Value="{StaticResource Button.Pressed.Border}"/>
                        </Trigger>
                        <Trigger Property="IsEnabled" Value="false">
                            <Setter Property="Fill" TargetName="ellipse" Value="{StaticResource Button.Disabled.Background}"/>
                            <Setter Property="Stroke" TargetName="ellipse" Value="{StaticResource Button.Disabled.Border}"/>
                            <Setter Property="TextElement.Foreground" TargetName="contentPresenter" Value="{StaticResource Button.Disabled.Foreground}"/>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

Обратите внимание, что стиль больше не нужно указывать для отдельных кнопок:

<Window x:Class="WPF_Style_Example.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d" ResizeMode="NoResize"
    Title="MainWindow"
    Height="150" Width="200">
<Grid>
    <Grid.RowDefinitions>
        <RowDefinition/>
        <RowDefinition/>
    </Grid.RowDefinitions>
    <Button Margin="5" Content="Button 1"/>
    <Button Margin="5" Grid.Row="1" Content="Button 2"/>
</Grid>

Обе кнопки теперь стилизованы.

введите описание изображения здесь

Стилизация ComboBox

Начиная со следующих ComboBox es:

<Window x:Class="WPF_Style_Example.ComboBoxWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d" ResizeMode="NoResize"
    Title="ComboBoxWindow"
    Height="100" Width="150">
<StackPanel>
    <ComboBox Margin="5" SelectedIndex="0">
        <ComboBoxItem Content="Item A"/>
        <ComboBoxItem Content="Item B"/>
        <ComboBoxItem Content="Item C"/>
    </ComboBox>
    <ComboBox IsEditable="True" Margin="5" SelectedIndex="0">
        <ComboBoxItem Content="Item 1"/>
        <ComboBoxItem Content="Item 2"/>
        <ComboBoxItem Content="Item 3"/>
    </ComboBox>
</StackPanel>

Щелкните правой кнопкой мыши на первом ComboBox в дизайнере, выберите «Редактировать шаблон -> Изменить копию». Определите стиль в области приложения.

Создано 3 стиля:

ComboBoxToggleButton
ComboBoxEditableTextBox
ComboBoxStyle1

И 2 шаблона:

ComboBoxTemplate
ComboBoxEditableTemplate

Пример редактирования стиля ComboBoxToggleButton :

<SolidColorBrush x:Key="ComboBox.Static.Border" Color="#FFACACAC"/>
    <SolidColorBrush x:Key="ComboBox.Static.Editable.Background" Color="#FFFFFFFF"/>
    <SolidColorBrush x:Key="ComboBox.Static.Editable.Border" Color="#FFABADB3"/>
    <SolidColorBrush x:Key="ComboBox.Static.Editable.Button.Background" Color="Transparent"/>
    <SolidColorBrush x:Key="ComboBox.Static.Editable.Button.Border" Color="Transparent"/>
    <SolidColorBrush x:Key="ComboBox.MouseOver.Glyph" Color="#FF000000"/>
    <LinearGradientBrush x:Key="ComboBox.MouseOver.Background" EndPoint="0,1" StartPoint="0,0">
        <GradientStop Color="Orange" Offset="0.0"/>
        <GradientStop Color="OrangeRed" Offset="1.0"/>
    </LinearGradientBrush>
    <SolidColorBrush x:Key="ComboBox.MouseOver.Border" Color="Red"/>
    <SolidColorBrush x:Key="ComboBox.MouseOver.Editable.Background" Color="#FFFFFFFF"/>
    <SolidColorBrush x:Key="ComboBox.MouseOver.Editable.Border" Color="#FF7EB4EA"/>
    <LinearGradientBrush x:Key="ComboBox.MouseOver.Editable.Button.Background" EndPoint="0,1" StartPoint="0,0">
        <GradientStop Color="#FFEBF4FC" Offset="0.0"/>
        <GradientStop Color="#FFDCECFC" Offset="1.0"/>
    </LinearGradientBrush>
    <SolidColorBrush x:Key="ComboBox.MouseOver.Editable.Button.Border" Color="#FF7EB4EA"/>
    <SolidColorBrush x:Key="ComboBox.Pressed.Glyph" Color="#FF000000"/>
    <LinearGradientBrush x:Key="ComboBox.Pressed.Background" EndPoint="0,1" StartPoint="0,0">
        <GradientStop Color="OrangeRed" Offset="0.0"/>
        <GradientStop Color="Red" Offset="1.0"/>
    </LinearGradientBrush>
    <SolidColorBrush x:Key="ComboBox.Pressed.Border" Color="DarkRed"/>
    <SolidColorBrush x:Key="ComboBox.Pressed.Editable.Background" Color="#FFFFFFFF"/>
    <SolidColorBrush x:Key="ComboBox.Pressed.Editable.Border" Color="#FF569DE5"/>
    <LinearGradientBrush x:Key="ComboBox.Pressed.Editable.Button.Background" EndPoint="0,1" StartPoint="0,0">
        <GradientStop Color="#FFDAEBFC" Offset="0.0"/>
        <GradientStop Color="#FFC4E0FC" Offset="1.0"/>
    </LinearGradientBrush>
    <SolidColorBrush x:Key="ComboBox.Pressed.Editable.Button.Border" Color="#FF569DE5"/>
    <SolidColorBrush x:Key="ComboBox.Disabled.Glyph" Color="#FFBFBFBF"/>
    <SolidColorBrush x:Key="ComboBox.Disabled.Background" Color="#FFF0F0F0"/>
    <SolidColorBrush x:Key="ComboBox.Disabled.Border" Color="#FFD9D9D9"/>
    <SolidColorBrush x:Key="ComboBox.Disabled.Editable.Background" Color="#FFFFFFFF"/>
    <SolidColorBrush x:Key="ComboBox.Disabled.Editable.Border" Color="#FFBFBFBF"/>
    <SolidColorBrush x:Key="ComboBox.Disabled.Editable.Button.Background" Color="Transparent"/>
    <SolidColorBrush x:Key="ComboBox.Disabled.Editable.Button.Border" Color="Transparent"/>
    <SolidColorBrush x:Key="ComboBox.Static.Glyph" Color="#FF606060"/>
    <Style x:Key="ComboBoxToggleButton" TargetType="{x:Type ToggleButton}">
        <Setter Property="OverridesDefaultStyle" Value="true"/>
        <Setter Property="IsTabStop" Value="false"/>
        <Setter Property="Focusable" Value="false"/>
        <Setter Property="ClickMode" Value="Press"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type ToggleButton}">
                    <Border x:Name="templateRoot" CornerRadius="10" BorderBrush="{StaticResource ComboBox.Static.Border}" BorderThickness="{TemplateBinding BorderThickness}" Background="{StaticResource ComboBox.Static.Background}" SnapsToDevicePixels="true">
                        <Border x:Name="splitBorder" BorderBrush="Transparent" BorderThickness="1" HorizontalAlignment="Right" Margin="0" SnapsToDevicePixels="true" Width="{DynamicResource {x:Static SystemParameters.VerticalScrollBarWidthKey}}">
                            <Path x:Name="arrow" Data="F1 M 0,0 L 2.667,2.66665 L 5.3334,0 L 5.3334,-1.78168 L 2.6667,0.88501 L0,-1.78168 L0,0 Z" Fill="{StaticResource ComboBox.Static.Glyph}" HorizontalAlignment="Center" Margin="0" VerticalAlignment="Center"/>
                        </Border>
                    </Border>
                    <ControlTemplate.Triggers>
                        <MultiDataTrigger>
                            <MultiDataTrigger.Conditions>
                                <Condition Binding="{Binding IsEditable, RelativeSource={RelativeSource AncestorType={x:Type ComboBox}}}" Value="true"/>
                                <Condition Binding="{Binding IsMouseOver, RelativeSource={RelativeSource Self}}" Value="false"/>
                                <Condition Binding="{Binding IsPressed, RelativeSource={RelativeSource Self}}" Value="false"/>
                                <Condition Binding="{Binding IsEnabled, RelativeSource={RelativeSource Self}}" Value="true"/>
                            </MultiDataTrigger.Conditions>
                            <Setter Property="Background" TargetName="templateRoot" Value="{StaticResource ComboBox.Static.Editable.Background}"/>
                            <Setter Property="BorderBrush" TargetName="templateRoot" Value="{StaticResource ComboBox.Static.Editable.Border}"/>
                            <Setter Property="Background" TargetName="splitBorder" Value="{StaticResource ComboBox.Static.Editable.Button.Background}"/>
                            <Setter Property="BorderBrush" TargetName="splitBorder" Value="{StaticResource ComboBox.Static.Editable.Button.Border}"/>
                        </MultiDataTrigger>
                        <Trigger Property="IsMouseOver" Value="true">
                            <Setter Property="BorderThickness" TargetName="templateRoot" Value="2"/>
                        </Trigger>
                        <MultiDataTrigger>
                            <MultiDataTrigger.Conditions>
                                <Condition Binding="{Binding IsMouseOver, RelativeSource={RelativeSource Self}}" Value="true"/>
                                <Condition Binding="{Binding IsEditable, RelativeSource={RelativeSource AncestorType={x:Type ComboBox}}}" Value="false"/>
                            </MultiDataTrigger.Conditions>
                            <Setter Property="Background" TargetName="templateRoot" Value="{StaticResource ComboBox.MouseOver.Background}"/>
                            <Setter Property="BorderBrush" TargetName="templateRoot" Value="{StaticResource ComboBox.MouseOver.Border}"/>
                        </MultiDataTrigger>
                        <MultiDataTrigger>
                            <MultiDataTrigger.Conditions>
                                <Condition Binding="{Binding IsMouseOver, RelativeSource={RelativeSource Self}}" Value="true"/>
                                <Condition Binding="{Binding IsEditable, RelativeSource={RelativeSource AncestorType={x:Type ComboBox}}}" Value="true"/>
                            </MultiDataTrigger.Conditions>
                            <Setter Property="Background" TargetName="templateRoot" Value="{StaticResource ComboBox.MouseOver.Editable.Background}"/>
                            <Setter Property="BorderBrush" TargetName="templateRoot" Value="{StaticResource ComboBox.MouseOver.Editable.Border}"/>
                            <Setter Property="Background" TargetName="splitBorder" Value="{StaticResource ComboBox.MouseOver.Editable.Button.Background}"/>
                            <Setter Property="BorderBrush" TargetName="splitBorder" Value="{StaticResource ComboBox.MouseOver.Editable.Button.Border}"/>
                        </MultiDataTrigger>
                        <Trigger Property="IsPressed" Value="true">
                            <Setter Property="Fill" TargetName="arrow" Value="{StaticResource ComboBox.Pressed.Glyph}"/>
                        </Trigger>
                        <MultiDataTrigger>
                            <MultiDataTrigger.Conditions>
                                <Condition Binding="{Binding IsPressed, RelativeSource={RelativeSource Self}}" Value="true"/>
                                <Condition Binding="{Binding IsEditable, RelativeSource={RelativeSource AncestorType={x:Type ComboBox}}}" Value="false"/>
                            </MultiDataTrigger.Conditions>
                            <Setter Property="Background" TargetName="templateRoot" Value="{StaticResource ComboBox.Pressed.Background}"/>
                            <Setter Property="BorderBrush" TargetName="templateRoot" Value="{StaticResource ComboBox.Pressed.Border}"/>
                        </MultiDataTrigger>
                        <MultiDataTrigger>
                            <MultiDataTrigger.Conditions>
                                <Condition Binding="{Binding IsPressed, RelativeSource={RelativeSource Self}}" Value="true"/>
                                <Condition Binding="{Binding IsEditable, RelativeSource={RelativeSource AncestorType={x:Type ComboBox}}}" Value="true"/>
                            </MultiDataTrigger.Conditions>
                            <Setter Property="Background" TargetName="templateRoot" Value="{StaticResource ComboBox.Pressed.Editable.Background}"/>
                            <Setter Property="BorderBrush" TargetName="templateRoot" Value="{StaticResource ComboBox.Pressed.Editable.Border}"/>
                            <Setter Property="Background" TargetName="splitBorder" Value="{StaticResource ComboBox.Pressed.Editable.Button.Background}"/>
                            <Setter Property="BorderBrush" TargetName="splitBorder" Value="{StaticResource ComboBox.Pressed.Editable.Button.Border}"/>
                        </MultiDataTrigger>
                        <Trigger Property="IsEnabled" Value="false">
                            <Setter Property="Fill" TargetName="arrow" Value="{StaticResource ComboBox.Disabled.Glyph}"/>
                        </Trigger>
                        <MultiDataTrigger>
                            <MultiDataTrigger.Conditions>
                                <Condition Binding="{Binding IsEnabled, RelativeSource={RelativeSource Self}}" Value="false"/>
                                <Condition Binding="{Binding IsEditable, RelativeSource={RelativeSource AncestorType={x:Type ComboBox}}}" Value="false"/>
                            </MultiDataTrigger.Conditions>
                            <Setter Property="Background" TargetName="templateRoot" Value="{StaticResource ComboBox.Disabled.Background}"/>
                            <Setter Property="BorderBrush" TargetName="templateRoot" Value="{StaticResource ComboBox.Disabled.Border}"/>
                        </MultiDataTrigger>
                        <MultiDataTrigger>
                            <MultiDataTrigger.Conditions>
                                <Condition Binding="{Binding IsEnabled, RelativeSource={RelativeSource Self}}" Value="false"/>
                                <Condition Binding="{Binding IsEditable, RelativeSource={RelativeSource AncestorType={x:Type ComboBox}}}" Value="true"/>
                            </MultiDataTrigger.Conditions>
                            <Setter Property="Background" TargetName="templateRoot" Value="{StaticResource ComboBox.Disabled.Editable.Background}"/>
                            <Setter Property="BorderBrush" TargetName="templateRoot" Value="{StaticResource ComboBox.Disabled.Editable.Border}"/>
                            <Setter Property="Background" TargetName="splitBorder" Value="{StaticResource ComboBox.Disabled.Editable.Button.Background}"/>
                            <Setter Property="BorderBrush" TargetName="splitBorder" Value="{StaticResource ComboBox.Disabled.Editable.Button.Border}"/>
                        </MultiDataTrigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

Это создает закругленный ComboBox который выделяет оранжевый цвет над мышью и при нажатии становится красным.

введите описание изображения здесь

Обратите внимание, что это не изменит подредактируемую выпадающую вывеску; который требует изменения стиля ComboBoxEditableTextBox или ComboBoxEditableTemplate .

Создание ресурсного словаря

Наличие большого количества стилей в App.xaml быстро станет сложным, поэтому их можно разместить в отдельных ресурсных словарях.

введите описание изображения здесь

Чтобы использовать словарь, он должен быть объединен с App.xaml. Итак, в App.xaml, после того, как был создан словарь ресурсов:

<Application
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         x:Class="WPF_Style_Example.App"
         StartupUri="MainWindow.xaml">
    <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="ResourceDictionaries/Dictionary1.xaml"/>
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Application.Resources>
</Application>

Теперь в Dictionary1.xaml теперь можно создавать новые стили, и на них можно ссылаться, как если бы они были в App.xaml. После создания проекта опция также появится в Visual Studio при копировании стиля, чтобы найти его в новом словаре ресурсов.

Стиль кнопки DoubleAnimation

Создано следующее Window :

<Window x:Class="WPF_Style_Example.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d" ResizeMode="NoResize"
    Title="MainWindow"
    Height="150" Width="250">
<Grid>
    <Grid.RowDefinitions>
        <RowDefinition/>
        <RowDefinition/>
    </Grid.RowDefinitions>
    <Button Margin="5" Content="Button 1" Width="200"/>
    <Button Margin="5" Grid.Row="1" Content="Button 2" Width="200"/>
</Grid>

Стиль (созданный в App.xaml) был применен к кнопкам, которые оживляют ширину от 200 до 100, когда мышь входит в элемент управления и от 100 до 200, когда она уходит:

<Style TargetType="{x:Type Button}">
    <Setter Property="FocusVisualStyle" Value="{StaticResource FocusVisual}"/>
    <Setter Property="Background" Value="{StaticResource Button.Static.Background}"/>
    <Setter Property="BorderBrush" Value="{StaticResource Button.Static.Border}"/>
    <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
    <Setter Property="BorderThickness" Value="1"/>
    <Setter Property="HorizontalContentAlignment" Value="Center"/>
    <Setter Property="VerticalContentAlignment" Value="Center"/>
    <Setter Property="Padding" Value="1"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type Button}">
                <Grid Background="White">
                    <Border x:Name="border" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" SnapsToDevicePixels="true">
                        <ContentPresenter x:Name="contentPresenter" Focusable="False" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
                    </Border>
                </Grid>
                <ControlTemplate.Triggers>
                    <EventTrigger RoutedEvent="MouseEnter">
                        <BeginStoryboard>
                            <Storyboard>
                                <DoubleAnimation To="100" From="200" Storyboard.TargetProperty="Width" Storyboard.TargetName="border" Duration="0:0:0.25"/>
                            </Storyboard>
                        </BeginStoryboard>
                    </EventTrigger>
                    <EventTrigger RoutedEvent="MouseLeave">
                        <BeginStoryboard>
                            <Storyboard>
                                <DoubleAnimation To="200" From="100" Storyboard.TargetProperty="Width" Storyboard.TargetName="border" Duration="0:0:0.25"/>
                            </Storyboard>
                        </BeginStoryboard>
                    </EventTrigger>
                    <Trigger Property="IsDefaulted" Value="true">
                        <Setter Property="BorderBrush" TargetName="border" Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}"/>
                    </Trigger>
                    <Trigger Property="IsMouseOver" Value="true">
                        <Setter Property="Background" TargetName="border" Value="{StaticResource Button.MouseOver.Background}"/>
                        <Setter Property="BorderBrush" TargetName="border" Value="{StaticResource Button.MouseOver.Border}"/>
                    </Trigger>
                    <Trigger Property="IsPressed" Value="true">
                        <Setter Property="Background" TargetName="border" Value="{StaticResource Button.Pressed.Background}"/>
                        <Setter Property="BorderBrush" TargetName="border" Value="{StaticResource Button.Pressed.Border}"/>
                    </Trigger>
                    <Trigger Property="IsEnabled" Value="false">
                        <Setter Property="Background" TargetName="border" Value="{StaticResource Button.Disabled.Background}"/>
                        <Setter Property="BorderBrush" TargetName="border" Value="{StaticResource Button.Disabled.Border}"/>
                        <Setter Property="TextElement.Foreground" TargetName="contentPresenter" Value="{StaticResource Button.Disabled.Foreground}"/>
                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

Ask0n

1 / 1 / 0

Регистрация: 05.10.2017

Сообщений: 71

1

Изменение стиля кнопок

06.07.2019, 13:36. Показов 7084. Ответов 17

Метки button, style, wpf (Все метки)


Подскажите пожалуйста как изменить стиль у кнопок. Мне именно при наведении надо что бы кнопка другого цвета была а не просто серая. (сразу такой мелкий вопрос можно ли как-то сделать так что бы цвет брался из Bacground кнопки и чуть прозрачнее становился ?)
Мой вариант не очень как-то работает:

XML
1
2
3
4
5
6
7
8
9
10
11
12
<Window.Resources>
        <SolidColorBrush x:Key="Button.MouseOver.Background" Color="#FFF900EE"/>
        <SolidColorBrush x:Key="Button.MouseOver.Border" Color="#FF3C7FB1"/>
        <Style TargetType="{x:Type Button}" BasedOn="{StaticResource MetroButton}">
            <Style.Triggers>
                <Trigger Property="IsMouseOver" Value="true">
                    <Setter Property="Background" Value="{StaticResource Button.MouseOver.Background}"/>
                    <Setter Property="BorderBrush" Value="{StaticResource Button.MouseOver.Border}"/>
                </Trigger>
            </Style.Triggers>
        </Style>
    </Window.Resources>

__________________
Помощь в написании контрольных, курсовых и дипломных работ, диссертаций здесь



0



Элд Хасп

Модератор

Эксперт .NET

13306 / 9589 / 2575

Регистрация: 21.04.2018

Сообщений: 28,310

Записей в блоге: 2

06.07.2019, 15:39

2

Цитата
Сообщение от Ask0n
Посмотреть сообщение

так что бы цвет брался из Bacground кнопки и чуть прозрачнее становился ?)

XML
1
2
3
                <Trigger Property="IsMouseOver" Value="true">
                    <Setter Property="Opacity" Value="0.5"/>
                </Trigger>



0



Элд Хасп

Модератор

Эксперт .NET

13306 / 9589 / 2575

Регистрация: 21.04.2018

Сообщений: 28,310

Записей в блоге: 2

06.07.2019, 16:25

3

Лучший ответ Сообщение было отмечено Ask0n как решение

Решение

Цитата
Сообщение от Ask0n
Посмотреть сообщение

Мне именно при наведении надо что бы кнопка другого цвета была а не просто серая.

Это просто не получится. В шаблоне кнопки цвет выделения при наведении задан константой. Изменить эту константу невозможно.

XML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
        <ControlTemplate x:Key="ButtonBaseControlTemplate1" TargetType="{x:Type ButtonBase}">
            <Border x:Name="border" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" SnapsToDevicePixels="True">
                <ContentPresenter x:Name="contentPresenter" ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" ContentStringFormat="{TemplateBinding ContentStringFormat}" Focusable="False" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
            </Border>
            <ControlTemplate.Triggers>
                <Trigger Property="Button.IsDefaulted" Value="True">
                    <Setter Property="BorderBrush" TargetName="border" Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}"/>
                </Trigger>
                <Trigger Property="IsMouseOver" Value="True">
                    <Setter Property="Background" TargetName="border" Value="#FFBEE6FD"/>
                    <Setter Property="BorderBrush" TargetName="border" Value="#FF3C7FB1"/>
                </Trigger>
                <Trigger Property="IsPressed" Value="True">
                    <Setter Property="Background" TargetName="border" Value="#FFC4E5F6"/>
                    <Setter Property="BorderBrush" TargetName="border" Value="#FF2C628B"/>
                </Trigger>
                <Trigger Property="ToggleButton.IsChecked" Value="True">
                    <Setter Property="Background" TargetName="border" Value="#FFBCDDEE"/>
                    <Setter Property="BorderBrush" TargetName="border" Value="#FF245A83"/>
                </Trigger>
                <Trigger Property="IsEnabled" Value="False">
                    <Setter Property="Background" TargetName="border" Value="#FFF4F4F4"/>
                    <Setter Property="BorderBrush" TargetName="border" Value="#FFADB2B5"/>
                    <Setter Property="Foreground" Value="#FF838383"/>
                </Trigger>
            </ControlTemplate.Triggers>
        </ControlTemplate>



1



Элд Хасп

Модератор

Эксперт .NET

13306 / 9589 / 2575

Регистрация: 21.04.2018

Сообщений: 28,310

Записей в блоге: 2

06.07.2019, 16:29

4

Для нужного вам дизайна необходимо изменить дефолтный шаблон кнопки

XML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
        <ControlTemplate x:Key="ButtonBaseControlTemplate1" TargetType="{x:Type ButtonBase}">
            <Border x:Name="border" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" SnapsToDevicePixels="True">
                <ContentPresenter x:Name="contentPresenter" ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" ContentStringFormat="{TemplateBinding ContentStringFormat}" Focusable="False" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
            </Border>
            <ControlTemplate.Triggers>
                <Trigger Property="Button.IsDefaulted" Value="True">
                    <Setter Property="BorderBrush" TargetName="border" Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}"/>
                </Trigger>
                <Trigger Property="IsMouseOver" Value="True">
                    <Setter Property="Background" TargetName="border" Value="{DynamicResource Button.MouseOver.Background}"/>
                    <Setter Property="BorderBrush" TargetName="border" Value="{DynamicResource Button.MouseOver.Border}"/>
                </Trigger>
                <Trigger Property="IsPressed" Value="True">
                    <Setter Property="Background" TargetName="border" Value="#FFC4E5F6"/>
                    <Setter Property="BorderBrush" TargetName="border" Value="#FF2C628B"/>
                </Trigger>
                <Trigger Property="ToggleButton.IsChecked" Value="True">
                    <Setter Property="Background" TargetName="border" Value="#FFBCDDEE"/>
                    <Setter Property="BorderBrush" TargetName="border" Value="#FF245A83"/>
                </Trigger>
                <Trigger Property="IsEnabled" Value="False">
                    <Setter Property="Background" TargetName="border" Value="#FFF4F4F4"/>
                    <Setter Property="BorderBrush" TargetName="border" Value="#FFADB2B5"/>
                    <Setter Property="Foreground" Value="#FF838383"/>
                </Trigger>
            </ControlTemplate.Triggers>
        </ControlTemplate>

Но как это сочетать с Метро….??? Я не знаю.
Это надо переопределять шаблоны Метро, но если их переопределять зачем тогда он, вообще, нужен?



0



1 / 1 / 0

Регистрация: 05.10.2017

Сообщений: 71

06.07.2019, 19:10

 [ТС]

5

Вообще он не не нужен мне по сути только цвет изменить при наведении и все, а то навел курсор на красную кнопку а она стала белой и как-то не очень



0



Модератор

Эксперт .NET

13306 / 9589 / 2575

Регистрация: 21.04.2018

Сообщений: 28,310

Записей в блоге: 2

06.07.2019, 20:07

6

Цитата
Сообщение от Ask0n
Посмотреть сообщение

Вообще он не не нужен мне по сути только цвет изменить при наведении и все, а то навел курсор на красную кнопку а она стала белой и как-то не очень

К сожалению только через переопределение шаблона кнопки.



0



1 / 1 / 0

Регистрация: 05.10.2017

Сообщений: 71

07.07.2019, 13:33

 [ТС]

7

Жаль



0



Модератор

Эксперт .NET

13306 / 9589 / 2575

Регистрация: 21.04.2018

Сообщений: 28,310

Записей в блоге: 2

07.07.2019, 13:58

8

Цитата
Сообщение от Ask0n
Посмотреть сообщение

Жаль

Но это в дефолных шаблонах.
В Метро все шаблоны переопределены.
Вы ссылаетесь на стиль MetroButton.
Что это за стиль? Может в нём переопределен дефолтный шаблон?
И в нём есть динамические привязки к внешним ресурсам?

Добавлено через 51 секунду
Получите этот шаблон и посмотрите его.
Если сами не разберётесь выставите его в тему — я посмотрю.



0



1 / 1 / 0

Регистрация: 05.10.2017

Сообщений: 71

07.07.2019, 14:04

 [ТС]

9

Это шаблон из Metro.Mahapps https://mahapps.com/
я просто когда делал поиск мне надо было красить строки в гриде и там когда тыкаешь на ечейку после закрашивания текст белым становился и надо было дописать BasedOn=»{StaticResource MetroDataGridRow}» вот я и предположил что может с кнопками то же самое надо, но потом я все больше и больше начал находить что надо переопределять шаблоны.



0



Модератор

Эксперт .NET

13306 / 9589 / 2575

Регистрация: 21.04.2018

Сообщений: 28,310

Записей в блоге: 2

07.07.2019, 14:08

10

Цитата
Сообщение от Ask0n
Посмотреть сообщение

Я знаю откуда этот шаблон.
Но скачивать и устанавливать Метро ради одного шаблона не хочу.

Поставьте курсор на название шаблона и нажмите F12. Перейдёте к определению шаблона. Скопируйте его в тему.



0



1 / 1 / 0

Регистрация: 05.10.2017

Сообщений: 71

07.07.2019, 14:15

 [ТС]

11

Выдал ошибку «Не удается перейти к определению.»



0



Модератор

Эксперт .NET

13306 / 9589 / 2575

Регистрация: 21.04.2018

Сообщений: 28,310

Записей в блоге: 2

07.07.2019, 14:21

12

Цитата
Сообщение от Ask0n
Посмотреть сообщение

Выдал ошибку «Не удается перейти к определению.»

А он точно есть этот стиль?
Или Вы его просто так написали?

Добавлено через 3 минуты
Второй способ.
В конструкторе окна нажимает правой кнопкой мыши на Button в котором определён это стиль.
Выходит контекстное меню.
В нём выбираете Правка шаблона -> Правка копии.
Задаёте имя копии и после её создания копируете сюда в тему.



0



Ask0n

1 / 1 / 0

Регистрация: 05.10.2017

Сообщений: 71

07.07.2019, 14:23

 [ТС]

13

Понял его нет

XML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
 <ControlTemplate x:Key="ButtonBaseControlTemplate1" TargetType="{x:Type ButtonBase}">
            <Grid>
                <Border x:Name="Background" Background="{TemplateBinding Background}" CornerRadius="{Binding (Controls:ControlsHelper.CornerRadius), Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}"/>
                <Border x:Name="Border" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{x:Null}" CornerRadius="{Binding (Controls:ControlsHelper.CornerRadius), Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
                <Border x:Name="DisabledVisualElement" Background="{DynamicResource ControlsDisabledBrush}" CornerRadius="{Binding (Controls:ControlsHelper.CornerRadius), Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}" IsHitTestVisible="False" Opacity="0" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
                <Controls:ContentControlEx x:Name="PART_ContentPresenter" ContentCharacterCasing="{Binding (Controls:ControlsHelper.ContentCharacterCasing), RelativeSource={RelativeSource TemplatedParent}}" ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" ContentStringFormat="{TemplateBinding ContentStringFormat}" HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" Padding="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"/>
            </Grid>
            <ControlTemplate.Triggers>
                <Trigger Property="IsMouseOver" Value="True">
                    <Setter Property="Background" TargetName="Background" Value="{DynamicResource GrayBrush8}"/>
                </Trigger>
                <Trigger Property="IsPressed" Value="True">
                    <Setter Property="Background" TargetName="Background" Value="{DynamicResource GrayBrush7}"/>
                </Trigger>
                <Trigger Property="IsKeyboardFocusWithin" Value="True">
                    <Setter Property="BorderBrush" TargetName="Border" Value="{DynamicResource ButtonMouseOverBorderBrush}"/>
                    <Setter Property="BorderThickness" TargetName="Border" Value="2"/>
                </Trigger>
                <Trigger Property="IsEnabled" Value="False">
                    <Setter Property="Opacity" TargetName="DisabledVisualElement" Value="0.7"/>
                    <Setter Property="Opacity" TargetName="PART_ContentPresenter" Value="0.3"/>
                </Trigger>
            </ControlTemplate.Triggers>
        </ControlTemplate>



0



Элд Хасп

Модератор

Эксперт .NET

13306 / 9589 / 2575

Регистрация: 21.04.2018

Сообщений: 28,310

Записей в блоге: 2

07.07.2019, 14:28

14

Цитата
Сообщение от Ask0n
Посмотреть сообщение

Понял его нет

Вид контрола при наведении курсора мыши задаёт блок

XML
9
10
11
                <Trigger Property="IsMouseOver" Value="True">
                    <Setter Property="Background" TargetName="Background" Value="{DynamicResource GrayBrush8}"/>
                </Trigger>

В нём идёт динамическая привязка к ресурсу GrayBrush8.
Задайте ресурс с этим именем в ресурсах кнопки и будет Вам щастье!.



0



Модератор

Эксперт .NET

13306 / 9589 / 2575

Регистрация: 21.04.2018

Сообщений: 28,310

Записей в блоге: 2

07.07.2019, 14:39

15

Но это если Вам надо целиком изменять кисть для фона.
Если Вам надо менять только прозрачность элемента, то нужно задать триггер как я написал в пост №2



0



Ask0n

1 / 1 / 0

Регистрация: 05.10.2017

Сообщений: 71

07.07.2019, 14:40

 [ТС]

16

А даже так ну ок
спасибо

я же правильно понял ?

XML
1
2
3
<Window.Resources>
        <SolidColorBrush Color="Red" x:Key="GrayBrush8" />
    </Window.Resources>



0



Модератор

Эксперт .NET

13306 / 9589 / 2575

Регистрация: 21.04.2018

Сообщений: 28,310

Записей в блоге: 2

07.07.2019, 14:44

17

Цитата
Сообщение от Ask0n
Посмотреть сообщение

я же правильно понял ?

Да.
Только если у Вас несколько кнопок — это повлияет на все кнопки в окне.
Если нужно задать не для всех, то нужно определять не в ресурсах окна, а в ресурсах контейнера содержащего нужные кнопки.
Если только для одной кнопки, то в её ресурсах.



0



1 / 1 / 0

Регистрация: 05.10.2017

Сообщений: 71

07.07.2019, 14:45

 [ТС]

18

Ок, спасибо большое



0



Стили, триггеры и темы

Стили

Последнее обновление: 07.02.2016

Стили позволяют определить набор некоторых свойств и их значений, которые потом могут применяться к элементам в xaml.
Стили хранятся в ресурсах и отделяют значения свойств элементов от пользовательского интерфейса. Также стили могут задавать некоторые аспекты поведения элементов с помощью триггеров.
Аналогом стилей могут служить каскадные таблицы стилей (CSS), которые применяются в коде html на веб-страницах.

Зачем нужны стили? Стили помогают создать стилевое единообразие для определенных элементов. Допустим, у нас есть следующий код xaml:

<StackPanel x:Name="buttonsStack" Background="Black" >
    <Button x:Name="button1" Margin="10" Content="Кнопка 1" FontFamily="Verdana" Foreground="White" Background="Black" />
    <Button x:Name="button2" Margin="10" Content="Кнопка 2" FontFamily="Verdana" Foreground="White" Background="Black"/>
</StackPanel>

Здесь обе кнопки применяют ряд свойств с одними и теми же значениями:

Стили в WPF

Однако в данном случае мы вынуждены повторяться. Частично, проблему могло бы решить использование ресурсов:

<Window x:Class="StylesApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:StylesApp"
        mc:Ignorable="d"
        Title="Стили" Height="250" Width="300">
    <Window.Resources>
        <FontFamily x:Key="buttonFont">Verdana</FontFamily>
        <SolidColorBrush Color="White" x:Key="buttonFontColor" />
        <SolidColorBrush Color="Black" x:Key="buttonBackColor" />
        <Thickness x:Key="buttonMargin" Bottom="10" Left="10" Top="10" Right="10" />
    </Window.Resources>
    <StackPanel x:Name="buttonsStack" Background="Black" >
        <Button x:Name="button1" Content="Кнопка 1" 
                Margin="{StaticResource buttonMargin}" 
                FontFamily="{StaticResource buttonFont}" 
                Foreground="{StaticResource buttonFontColor}" 
                Background="{StaticResource buttonBackColor}" />
        <Button x:Name="button2" Content="Кнопка 2" 
                Margin="{StaticResource buttonMargin}" 
                FontFamily="{StaticResource buttonFont}" 
                Foreground="{StaticResource buttonFontColor}" 
                Background="{StaticResource buttonBackColor}"/>
    </StackPanel>
</Window>

Однако в реальности код раздувается, опть же приходится писать много повторяющейся информации. И в этом плане стили предлагают более элегантное решение:

<Window x:Class="StylesApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:StylesApp"
        mc:Ignorable="d"
        Title="Стили" Height="250" Width="300">
    <Window.Resources>
        <Style x:Key="BlackAndWhite">
            <Setter Property="Control.FontFamily" Value="Verdana" />
            <Setter Property="Control.Background" Value="Black" />
            <Setter Property="Control.Foreground" Value="White" />
            <Setter Property="Control.Margin" Value="10" />
        </Style>
    </Window.Resources>
    <StackPanel x:Name="buttonsStack" Background="Black" >
        <Button x:Name="button1" Content="Кнопка 1" 
                Style="{StaticResource BlackAndWhite}" />
        <Button x:Name="button2" Content="Кнопка 2" 
                Style="{StaticResource BlackAndWhite}"/>
    </StackPanel>
</Window>

Результат будет тот же, однако теперь мы избегаем ненужного повторения. Более того теперь мы можем управлять всеми нужными нам свойствами как единым целым — одним стилем.

Стиль создается как ресурс с помощью объекта Style, который представляет класс System.Windows.Style.
И как любой другой ресурс, он обязательно должен иметь ключ. С помощью коллекции Setters определяется группа свойств, входящих в
стиль. В нее входят объекты Setter, которые имеют следующие свойства:

  • Property: указывает на свойство, к которому будет применяться данный сеттер. Имеет следующий синтаксис:
    Property="Тип_элемента.Свойство_элемента". Выше в качестве типа элемента использовался Control, как общий для всех элементов.
    Поэтому данный стиль мы могли бы применить и к Button, и к TextBlock, и к другим элементам. Однако мы можем и конкретизировать элемент, например, Button:

    <Setter Property="Button.FontFamily" Value="Arial" />
  • Value: устанавливает значение

Если значение свойства представляет сложный объект, то мы можем его вынести в отдельный элемент:

<Style x:Key="BlackAndWhite">
    <Setter Property="Control.Background">
        <Setter.Value>
            <LinearGradientBrush>
                <LinearGradientBrush.GradientStops>
                    <GradientStop Color="White" Offset="0" />
                    <GradientStop Color="Black" Offset="1" />
                </LinearGradientBrush.GradientStops>
            </LinearGradientBrush>
        </Setter.Value>
    </Setter>
    <Setter Property="Control.FontFamily" Value="Verdana" />
    <Setter Property="Control.Foreground" Value="White" />
    <Setter Property="Control.Margin" Value="10" />
</Style>

TargetType

Hам необязательно прописывать для всех кнопок стиль. Мы можем в самом определении стиля с помощью свойства TargetType
задать тип элементов. В этом случае стиль будет автоматически применяться ко всем кнопкам в окне:

<Window x:Class="StylesApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:StylesApp"
        mc:Ignorable="d"
        Title="Стили" Height="250" Width="300">
    <Window.Resources>
        <Style TargetType="Button">
            <Setter Property="FontFamily" Value="Verdana" />
            <Setter Property="Background" Value="Black" />
            <Setter Property="Foreground" Value="White" />
            <Setter Property="Margin" Value="10" />
        </Style>
    </Window.Resources>
    <StackPanel x:Name="buttonsStack" Background="Black" >
        <Button x:Name="button1" Content="Кнопка 1"  />
        <Button x:Name="button2" Content="Кнопка 2" />
    </StackPanel>
</Window>

Причем в этом случае нам уже не надо указывать у стиля ключ x:Key несмотря на то, что это ресурс.

Также если используем свойство TargetType, то в значении атрибута Property уже необязательно указывать тип, то есть Property="Control.FontFamily".
И в данном случае тип можно просто опустить: Property="FontFamily"

Если же необходимо, чтобы к какой-то кнопке не применялся автоматический стиль, то ее стилю присваивают значение null

<Button x:Name="button2" Content="Кнопка 2" Style="{x:Null}" />

Определение обработчиков событий с помощью стилей

Кроме коллекции Setters стиль может определить другую коллекцию — EventSetters, которая содержит объекты
EventSetter. Эти объекты позволяют связать события элементов с обработчиками. Например, подключим все кнопки к одному
обработчику события Click:

<Style TargetType="Button">
    <Setter Property="Button.Background" Value="Black" />
    <Setter Property="Button.Foreground" Value="White" />
    <Setter Property="Button.FontFamily" Value="Andy" />
    <EventSetter Event="Button.Click" Handler="Button_Click" />
</Style>

Соответственно в файле кода c# у нас должен быть определен обработчик Button_Click:

private void Button_Click(object sender, RoutedEventArgs e)
{
    Button clickedButton = (Button)sender;
    MessageBox.Show(clickedButton.Content.ToString());
}

Наследование стилей и свойство BasedOn

У класса Style еще есть свойство BasedOn, с помощью которого можно наследовать и расширять существующие стили:

<Window.Resources>
    <Style x:Key="ButtonParentStyle">
        <Setter Property="Button.Background" Value="Black" />
        <Setter Property="Button.Foreground" Value="White" />
        <Setter Property="Button.FontFamily" Value="Andy" />
    </Style>
    <Style x:Key="ButtonChildStyle" BasedOn="{StaticResource ButtonParentStyle}">
        <Setter Property="Button.BorderBrush" Value="Red" />
        <Setter Property="Button.FontFamily" Value="Verdana" />
    </Style>
</Window.Resources>

Cвойство BasedOn в качестве значения принимает существующий стиль, определяя его как статический ресурс. В итоге он объединяет весь
функционал родительского стиля со своим собственным.

Если в дочернем стиле есть сеттеры для свойств, которые также используются в родительском стиле, как в данном случае сеттер для свойства Button.FontFamily,
то дочерний стиль переопределяет родительский стиль.

Стили в C#

В C# стили представляют объект System.Windows.Style. Используя его, мы можем добавлять сеттеры и устанавливать стиль для нужных элементов:

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

namespace StylesApp
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            Style buttonStyle = new Style();
            buttonStyle.Setters.Add(new Setter { Property = Control.FontFamilyProperty, Value = new FontFamily("Verdana") });
            buttonStyle.Setters.Add(new Setter { Property = Control.MarginProperty, Value = new Thickness(10) });
            buttonStyle.Setters.Add(new Setter { Property = Control.BackgroundProperty, Value = new SolidColorBrush(Colors.Black) });
            buttonStyle.Setters.Add(new Setter { Property = Control.ForegroundProperty, Value = new SolidColorBrush(Colors.White) });
            buttonStyle.Setters.Add(new EventSetter { Event= Button.ClickEvent, Handler= new RoutedEventHandler( Button_Click) });

            button1.Style = buttonStyle;
            button2.Style = buttonStyle;
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            Button clickedButton = (Button)sender;
            MessageBox.Show(clickedButton.Content.ToString());
        }
    }
}

При создании сеттера нам надо использовать свойство зависимостей, например, Property = Control.FontFamilyProperty. Причем для свойства
Value у сеттера должен быть установлен объект именно того типа, которое хранится в этом свойстве зависимости.
Так, свойство зависимости MarginProperty хранит объект типа Thickness, поэтому определение сеттера выглядит следующим образом:

new Setter { Property = Control.MarginProperty, Value = new Thickness(10) }

Понравилась статья? Поделить с друзьями:

Читайте также:

  • Как изменить стиль дочерних элементов css
  • Как изменить стиль кнопки css
  • Как изменить стиль главного экрана
  • Как изменить стиль кнопки android studio
  • Как изменить стиль гиперссылки css

  • 0 0 голоса
    Рейтинг статьи
    Подписаться
    Уведомить о
    guest

    0 комментариев
    Старые
    Новые Популярные
    Межтекстовые Отзывы
    Посмотреть все комментарии