C# WPF无铬按钮,带绑定图像状态和悬停图像
我在WPF应用程序中有一个无铬按钮,它使用下面的XAML样式: 然而,有两件事我正试图用它来做C# WPF无铬按钮,带绑定图像状态和悬停图像,c#,wpf,xaml,C#,Wpf,Xaml,我在WPF应用程序中有一个无铬按钮,它使用下面的XAML样式: 然而,有两件事我正试图用它来做 我希望显示的图像是两个图像之一,具体取决于按钮所在模板的datacontext中的绑定属性。该属性是布尔值。如果为假,则显示一个图像,如果为真,则显示另一个图像 我想显示的图像改变时,鼠标悬停在按钮上,并将显示的图像时,上述绑定属性为真 我试过几种方法,但似乎都不管用。我尝试了一个绑定值如下的转换器: <x:ArrayExtension x:Key="ThumbsDown" Ty
<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>
<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>
公共对象转换(对象值、类型targetType、对象参数、System.Globalization.CultureInfo区域性)
{
变量数据=(对象[])参数;
if(data.Length!=2 | | value==null | | value.GetType()!=typeof(bool))
返回数据[0];
如果(!((布尔)值))
返回数据[0];
其他的
返回数据[1];
}
但这会导致两个问题。我对悬停图像所做的任何操作都不再有效,设计师抛出了一个异常
下面的不透明度触发器可以启动,因为我现在真的只需要悬停
有没有关于如何使其正常工作的想法
<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>
您必须做几件事:
ImageBrush
资源样式的按钮创建控制模板
,鼠标悬停在触发器上
Background
属性从点1绑定到ImageBrush
资源之一,在按钮上设置图像
Trigger
上,将Background
属性更改为另一个ImageBrush
资源
这里的关键是在边框的样式上实现触发器,而不是直接在按钮上实现触发器。当尝试从应用于按钮的样式设置边框的属性时,我无法使其工作。您还可以将边框
替换为矩形
,或将任何其他内容控件
替换为背景
属性
从这里开始,根据您认为适合您的应用程序进行自定义。您必须做几件事:
为要显示的每个图像设置ImageBrush
资源
为具有内置样式的按钮创建控制模板
,鼠标悬停在触发器上
通过将Background
属性从点1绑定到ImageBrush
资源之一,在按钮上设置图像
将鼠标悬停在Trigger
上,将Background
属性更改为另一个ImageBrush
资源
请参阅下面的代码以获得一个简单的示例(您必须使用自己的图像来代替“向上”和“向下”)
这里的关键是在边框的样式上实现触发器,而不是直接在按钮上实现触发器。当尝试从应用于按钮的样式设置边框的属性时,我无法使其工作。您还可以将边框
替换为矩形
,或将任何其他内容控件
替换为背景
属性
从这里开始,根据您认为适合您的应用程序进行自定义。我将通过向按钮的控件模板添加图像控件来解决这个问题,然后创建一个多数据触发器
,其中一个条件用于IsMouseOver
,另一个条件用于要绑定的布尔属性。然后,触发器在激活时设置图像的源
下面是实现这一点的样式。我假设按钮有一个包含布尔属性的DataContext,布尔属性称为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>
然后,可以以下列方式使用此类:
<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}" />
然后可以修改控制模板
<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>