#c# #wpf #xaml #controltemplate
#c# #wpf #xaml #controltemplate
Вопрос:
Я новичок в WPF, и я пытаюсь определить стиль кнопок для кнопок в моем приложении. Я добавил следующее ResourceDictionary
и добавил его в ресурсы приложения в App.xaml
:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ReciepeFinder">
<Style x:Key="NavButton" TargetType="Button">
<Setter Property="Width"
Value="120" />
<Setter Property="Height"
Value="120" />
<Setter Property="BorderThickness"
Value="0" />
<Setter Property="Background">
<Setter.Value>
<ImageBrush ImageSource="/ReciepeFinder;component/resources/images/home.png" />
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver"
Value="True">
<Setter Property="Background">
<Setter.Value>
<ImageBrush ImageSource="/ReciepeFinder;component/resources/images/home_selected.png" />
</Setter.Value>
</Setter>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
В принципе, без наведения курсора я хочу, чтобы кнопка отображалась resources/images/home.png
и при наведении курсора, resources/images/home_selected.png
. Когда я пытаюсь определить ControlTemplate
, как в приведенном выше коде, и запускаю приложение, кнопка просто не появляется. Однако, если я полностью удалю ControlTemplate
тег, кнопка появится с соответствующим Background
значением, но, очевидно, без эффекта наведения.
Что я делаю не так?
Дополнительный вопрос: Если бы я хотел использовать привязку, чтобы я мог просто задать путь для изображения без наведения и изображения при наведении в атрибутах других кнопок и заставить его вести себя таким же образом, как я мог бы этого добиться?
Ответ №1:
Если вы хотите изменить внешний вид элемента управления вместе с его визуальными состояниями, вам придется отредактировать его ControlTemplate
. Однако шаблон элемента управления содержит определения обязательных частей элемента управления, а также его внешний вид в разных состояниях и переходы между ними. Чтобы ControlTemplate
работать должным образом, вы должны реализовать все эти части и состояния. Вы можете найти информацию о необходимых частях и состояниях для каждого встроенного элемента управления в MSDN, например для Button
.
Вместо того, чтобы создавать свой ControlTemplate
с нуля, вы можете скопировать шаблон по умолчанию и адаптировать его, что намного проще. Вы можете извлечь ее с помощью Blend или Visual Studio.
Ваш шаблон управления не работает, потому что он по существу пуст. Нет ничего для отображения фона или любого содержимого. Он не содержит никаких визуальных элементов или элементов управления. Я создал для вас образец шаблона элемента управления, который должен соответствовать вашим требованиям.
<Style x:Key="NavButton" TargetType="{x:Type Button}">
<Setter Property="Background">
<Setter.Value>
<ImageBrush ImageSource="/ReciepeFinder;component/resources/images/home.png" />
</Setter.Value>
</Setter>
<Setter Property="BorderThickness" Value="0"/>
<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}">
<Border x:Name="border" Background="{TemplateBinding Background}" BorderThickness="{TemplateBinding BorderThickness}" BorderBrush="{TemplateBinding BorderBrush}" SnapsToDevicePixels="true"/>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="Background" TargetName="border">
<Setter.Value>
<ImageBrush ImageSource="/ReciepeFinder;component/resources/images/home_selected.png" />
</Setter.Value>
</Setter>
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter TargetName="border" Property="Opacity" Value="0.5"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Border
Отображается фон изображения. В общем, вы бы поместили a ContentPresenter
внутри Border
, потому что это показало бы содержимое, которое вы присвоили своему Content
свойству Button
, но поскольку вы отображаете изображения вместо содержимого, я опустил это.
Пожалуйста, обратите внимание, что внутри этого шаблона есть TemplateBinding
s. Они используются для привязки указанных свойств к элементу управления, к которому применяется шаблон. Другими словами, если вы устанавливаете Background
на кнопку напрямую или создаете Style
производную от NavButton
того места, где вы ее установили, будет использоваться это значение. Если вы просто жестко закодировали значение в шаблоне элемента управления, оно не изменилось бы.
Что касается вашего дополнительного вопроса: Когда вы используете эти TemplateBinding
параметры, установка Background
напрямую или в производном Style
будет работать, как указано выше, так что вы можете использовать его повторно. Поскольку HoverBackground
свойства нет, вам придется либо создать производную кнопку и добавить к ней свойство зависимости, либо создать присоединенное свойство, либо использовать, например, неиспользуемое Foreground
свойство для этого, хотя это не кажется правильным, поскольку это грязный обходной путь. Это пример для Foreground
просто потому, что его легко протестировать для вас, но он работает и для других опций, указанных выше.
<Setter Property="Foreground">
<Setter.Value>
<ImageBrush ImageSource="/ReciepeFinder;component/resources/images/home_selected.png" />
</Setter.Value>
</Setter>
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="Background" TargetName="border" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Foreground}"/>
</Trigger>
Пожалуйста, обратите внимание, что для доступа к свойству родительского шаблона в Setter
вы должны использовать другой синтаксис привязки, который имеет тот же эффект, что и TemplateBinding
.
Комментарии:
1. Большое вам спасибо! Я создал класс HoverableButton, который добавляет свойства onHover и NoHover и использовал их для привязки!
Ответ №2:
Потому что, когда вы устанавливаете ControlTemplate, вам в основном приходится переопределять способ отображения элемента управления. И вы определили только триггеры без какого-либо представления содержимого. Элемент управления ContentPresenter поможет вам в этом. Просто добавьте некоторую границу и презентацию содержимого после ControlTemplate.Запускает и ControlTemplate. Это должно сработать:
</ControlTemplate.Triggers>
<Border Background="{TemplateBinding Background}">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
</ControlTemplate>