我在WPF应用程序中有一个使用以下XAML样式的无格式按钮: 但是,我正试图用它做两件事。
我尝试了几件事,但似乎没有什么工作。我尝试了一个带有绑定值的转换器:
<x:ArrayExtension x:Key="ThumbsDown" Type="BitmapImage">
<BitmapImage UriSource="/Elpis;component/Images/thumbDown.png" />
<BitmapImage UriSource="/Elpis;component/Images/thumbBan.png" />
</x:ArrayExtension>
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
var data = (object[])parameter;
if (data.Length != 2 || value == null || value.GetType() != typeof(bool))
return data[0];
if(!((bool)value))
return data[0];
else
return data[1];
}
<Button Name="btnThumbDown" Grid.Column="1" Style="{StaticResource NoChromeButton}"
VerticalAlignment="Center"
HorizontalAlignment="Center"
Background="Transparent"
Click="btnThumbDown_Click">
<Image Width="32" Height="32" Margin="2"
Source="{Binding Banned,
Converter={StaticResource binaryChooser},
ConverterParameter={StaticResource ThumbsDown}}"/>
</Button>
但这会导致2个问题。我没有为悬停图像做任何事情,设计师抛出异常。
来自下方的不透明度触发器可以继续,因为我现在只想悬停。
有关如何使其正常运作的任何想法?
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style x:Key="NoChromeButton" TargetType="{x:Type ButtonBase}">
<Setter Property="Background" Value="Transparent"/>
<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 ButtonBase}">
<Grid x:Name="Chrome" Background="{TemplateBinding Background}" SnapsToDevicePixels="true">
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
Margin="{TemplateBinding Padding}" RecognizesAccessKey="True"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Opacity" Value="0.75"/>
</Trigger>
<Trigger Property="IsMouseOver" Value="False">
<Setter Property="Opacity" Value="1.0"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
答案 0 :(得分:3)
你将不得不做几件事:
ImageBrush
个资源。ControlTemplate
且鼠标悬停Style
的按钮创建Trigger
。Button
属性绑定到第1点的Background
资源之一,在ImageBrush
上设置图像。Trigger
将Background
属性更改为其他ImageBrush
资源。请参阅下面的代码以获取一个简单示例(您必须使用自己的图像代替“向上”和“向下”)。
<Window x:Class="ButtonHover.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:my="clr-namespace:ButtonHover"
Title="MainWindow" Height="227" Width="280">
<Window.Resources>
<ImageBrush x:Key="imageABrush"
ImageSource="/ButtonHover;component/Resources/up.png"/>
<ImageBrush x:Key="imageBBrush"
ImageSource="/ButtonHover;component/Resources/down.png"/>
<ControlTemplate x:Key="buttonTemplate" TargetType="Button">
<Border BorderThickness="3" BorderBrush="Black">
<Border.Style>
<Style>
<Style.Setters>
<Setter Property="Border.Background"
Value="{StaticResource imageABrush}"/>
</Style.Setters>
<Style.Triggers>
<Trigger Property="Button.IsMouseOver" Value="True">
<Setter Property="Border.Background"
Value="{StaticResource imageBBrush}"/>
</Trigger>
</Style.Triggers>
</Style>
</Border.Style>
</Border>
</ControlTemplate>
</Window.Resources>
<Grid>
<Button Height="75"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Width="75"
Template="{StaticResource buttonTemplate}" />
</Grid>
</Window>
此处的关键是在Style
的{{1}}而不是Border
直接实施触发器。尝试将Button
的属性设置为应用于Border
的{{1}}时,我无法使其工作。您还可以将Style
替换为Button
或任何其他Border
Rectangle
属性。
从此处开始,根据您的应用进行自定义。
答案 1 :(得分:1)
我会通过向按钮的Image
添加ControlTemplate
控件来解决此问题,然后为MultiDataTrigger
创建一个IsMouseOver
条件,并为其创建一个条件要绑定的布尔属性。然后触发器在激活时设置图像源。
以下是完成此操作的样式。我假设按钮有一个包含boolean属性的DataContext,并且boolean属性被称为MyBoolean。
<Style x:Key="NoChromeButton" TargetType="{x:Type ButtonBase}">
<Setter Property="Background" Value="Transparent"/>
<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>
<!-- I assume that the button has a DataContext with a boolean property called MyBoolean -->
<ControlTemplate TargetType="{x:Type ButtonBase}">
<Grid x:Name="Chrome" Background="{TemplateBinding Background}" SnapsToDevicePixels="true">
<!-- Not sure about what the button should look like, so I made it an image to the left
and the button's content to the right -->
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Image x:Name="ButtonImage" Grid.Column="0" Source="/Elpis;component/Images/thumbBan.png" />
<ContentPresenter Grid.Column="1"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
Margin="{TemplateBinding Padding}" RecognizesAccessKey="True"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
</Grid>
<ControlTemplate.Triggers>
<!-- As I understand it, ThumbBan should be shown when IsMouseOver == True OR the bound boolean is true,
so if you invert that you could say that the ThumbDown image should be shown
when IsMouseOver == false AND the bound boolean is false, which is what this trigger does -->
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsMouseOver, ElementName=Chrome}" Value="False" />
<Condition Binding="{Binding MyBoolean}" Value="False" />
</MultiDataTrigger.Conditions>
<MultiDataTrigger.Setters>
<Setter TargetName="ButtonImage" Property="Source" Value="/Elpis;component/Images/thumbDown.png" />
</MultiDataTrigger.Setters>
</MultiDataTrigger>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Opacity" Value="0.75"/>
</Trigger>
<!-- The trigger that sets opacity to 1 for IsMouseOver false is not needed, since 1 is the
default and will be the opacity as long as the trigger above is not active -->
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
如果我误解了要求,您可能必须切换触发器处于活动状态时应显示的图像。
这里的诀窍是使用MultiDataTrigger
,这样你提到的两个条件就可以合并。通常,在绑定到控件的Trigger
时,您可能会使用DataTrigger
而不是IsMouseOver
。但是,由于布尔属性需要DataTrigger
,因此IsMouseOver
绑定可以使用绑定的DataTrigger
属性写为ElementName
。通过这样做,您可以使用MultiDataTrigger来组合这两者。
为了添加对自定义所用图像的支持,以及要绑定到哪个属性,对于每个按钮实例,我将继承Button
类并添加几个DependencyProperties
。
public class ImageButton : Button
{
public static readonly DependencyProperty ActiveImageUriProperty =
DependencyProperty.RegisterAttached("ActiveImageUri", typeof(Uri), typeof(ImageButton),
new PropertyMetadata(null));
public static readonly DependencyProperty InactiveImageUriProperty =
DependencyProperty.RegisterAttached("InactiveImageUri", typeof(Uri), typeof(ImageButton),
new PropertyMetadata(null));
public static readonly DependencyProperty IsActiveProperty =
DependencyProperty.RegisterAttached("IsActive", typeof(bool), typeof(ImageButton),
new PropertyMetadata(false));
public Uri ActiveImageUri
{
get { return (Uri)GetValue(ActiveImageUriProperty); }
set { SetValue(ActiveImageUriProperty, value); }
}
public Uri InactiveImageUri
{
get { return (Uri)GetValue(InactiveImageUriProperty); }
set { SetValue(InactiveImageUriProperty, value); }
}
public bool IsActive
{
get { return (bool)GetValue(IsActiveProperty); }
set { SetValue(IsActiveProperty, value); }
}
}
然后可以通过以下方式使用此类:
<SomeNamespace:ImageButton Height="23" Width="100" Content="Button 1"
ActiveImageUri="/Elpis;component/Images/thumbBan.png"
InactiveImageUri="/Elpis;component/Images/thumbDown.png"
IsActive="{Binding MyBoolean}" />
<SomeNamespace:ImageButton Height="23" Width="100" Content="Button 2"
ActiveImageUri="/Elpis;component/Images/someOtherImage.png"
InactiveImageUri="/Elpis;component/Images/yetAnotherImage.png"
IsActive="{Binding SomeOtherBooleanProperty}" />
然后可以将控件模板修改为如下所示:
<Style TargetType="SomeNamespace:ImageButton">
<Setter Property="Background" Value="Transparent" />
<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 SomeNamespace:ImageButton}">
<Grid x:Name="Chrome" Background="{TemplateBinding Background}" SnapsToDevicePixels="true">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Image x:Name="ButtonImage" Grid.Column="0"
Source="{Binding ActiveImageUri, RelativeSource={RelativeSource TemplatedParent}}" />
<ContentPresenter Grid.Column="1"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
Margin="{TemplateBinding Padding}" RecognizesAccessKey="True"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}" />
</Grid>
<ControlTemplate.Triggers>
<!-- The "active" image should be shown when IsMouseOver == True OR the bound boolean is true,
so if you invert that you could say that the "inactive" image should be shown
when IsMouseOver == false AND the bound boolean is false, which is what this trigger does -->
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsMouseOver" Value="False" />
<Condition Property="IsActive" Value="False" />
</MultiTrigger.Conditions>
<MultiTrigger.Setters>
<Setter TargetName="ButtonImage" Property="Source"
Value="{Binding InactiveImageUri, RelativeSource={RelativeSource TemplatedParent}}" />
</MultiTrigger.Setters>
</MultiTrigger>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Opacity" Value="0.75" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
此处的主要更改是将图像的源设置为依赖项属性的值而不是硬编码的URI,并且MultiDataTrigger
已更改为绑定到MultiTrigger
的{{1}}依赖属性。以前,布尔属性的路径也是硬编码的,但现在可以通过在创建按钮时更改IsActive
属性的绑定来配置,如上例所示。