[英]C# WPF - How to Combine datatrigger and trigger?
我不知道是否需要结合DataTrigger和Trigger,如果有更好的方法请告诉我。
我的目标是,创建一个菜单(带图标),图标会在遇到悬停或选定的事件时发生变化。
这是一个枚举定义所有菜单类型:
public enum PageTypes:byte
{
NotSet = 0,
HomePage = 1,
ShopPage = 2,
AboutPage = 3
}
然后我创建了一个MenuItemModel来表示每个菜单项:
public class MenuItemModel : INotifyPropertyChanged
{
private PageTypes _menuItemType = PageTypes.NotSet;
public PageTypes MenuItemType { get { return _menuItemType; } set { if (value != _menuItemType) { _menuItemType = value; RaisePropertyChanged(() => MenuItemType); } } }
private bool _isSelected = false;
public bool IsSelected { get { return _isSelected; } set { if (value != _isSelected) { _isSelected = value; RaisePropertyChanged(() => IsSelected); } } }
}
好的,然后我开始创建UI。
<!-- MenuItem Template -->
<DataTemplate x:Key="MenuTemplate">
<Button Command="{Binding ClickCommand}" CommandParameter="{Binding}">
<Image>
<Image.Style>
<Style TargetType="Image">
<Setter Property="Source" Value="/Image/Home_normal.png"/>
<Style.Triggers>
<DataTrigger Binding="{Binding MenuItemType}" Value="ShopPage">
<Setter Property="Source" Value="/Image/Shop_normal.png"/>
</DataTrigger>
<DataTrigger Binding="{Binding MenuItemType}" Value="AboutPage">
<Setter Property="Source" Value="/Image/About_normal.png"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Image.Style>
</Image>
</Button>
</DataTemplate>
到目前为止一切都很简单,但是当我尝试制作mouseOver和Selected效果时,问题就来了。
例如,如果鼠标超过home_normal.png,则应更改为home_hover.png,如果IsSelected属性为TRUE,则应忽略hover触发器,然后使用home_selected.png。 但是有3个图像,我怎么知道应该改变什么图像?
<!-- MenuItem Template -->
<DataTemplate x:Key="MenuTemplate">
<Button Command="{Binding ClickCommand}" CommandParameter="{Binding}">
<Image>
<Image.Style>
<Style TargetType="Image">
<Setter Property="Source" Value="/Image/Home_normal.png"/>
<Style.Triggers>
<DataTrigger Binding="{Binding MenuItemType}" Value="ShopPage">
<Setter Property="Source" Value="/Image/Shop_normal.png"/>
</DataTrigger>
<DataTrigger Binding="{Binding MenuItemType}" Value="AboutPage">
<Setter Property="Source" Value="/Image/About_normal.png"/>
</DataTrigger>
<!-- MY PLAN -->
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Source" Value="?_hover.png"/>
</Trigger>
<DataTrigger Binding="{Binding IsSelected}" Value="True">
<Setter Property="Source" Value="?_selected.png"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Image.Style>
</Image>
</Button>
</DataTemplate>
如果您可以在“我的计划”评论中看到问号,那就是我的问题:我应该在“价值”字段中做什么?
您可以像这样使用MultiDataTrigger
。 但是你应该为所有类型的页面添加相同的3个触发器。 请注意,下面的触发器会覆盖下面,条件的作用类似于逻辑AND。
<p:Style.Triggers xmlns:p="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<DataTrigger Binding="{Binding MenuItemType}" Value="ShopPage">
<Setter Property="Source" Value="/Image/Shop_normal.png"/>
</DataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding MenuItemType}" Value="ShopPage" />
<Condition Binding="{Binding RelativeSource={RelativeSource Mode=Self}, Path=IsMouseOver}" Value="true" />
</MultiDataTrigger.Conditions>
<Setter Property="Source" Value="/Image/Shop_MouseOver.png" />
</MultiDataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding MenuItemType}" Value="ShopPage" />
<Condition Binding="{Binding RelativeSource={RelativeSource Mode=Self}, Path=IsSelected}" Value="true" />
</MultiDataTrigger.Conditions>
<Setter Property="Source" Value="/Image/Shop_IsSelected.png" />
</MultiDataTrigger>
</p:Style.Triggers>
在我看来,你已经收到并接受的答案是一个很好的答案。 它完全基于XAML,这似乎是您的场景中的主要目标,它应该可以很好地工作。 也就是说,仅XAML解决方案相当冗长,涉及大量冗余代码。 在上面的场景中已经看到了这种情况,您有两种按钮类型,每种类型都有三种可能的状态。 当你添加按钮类型和状态时,它只会变得更糟。
如果您愿意做一些代码隐藏,我认为您可以实现相同的效果,但冗余更少。
具体来说,如果使用<MultiBinding>
,则可以将相关属性绑定到可用于查找正确图像源的集合。 为了让我完成这个,我需要创建一些小容器类型来存储查找数据,当然还有IMultiValueConverter
实现来使用它们:
容器类型:
[ContentProperty("Elements")]
class BitmapImageArray
{
private readonly List<ButtonImageStates> _elements = new List<ButtonImageStates>();
public List<ButtonImageStates> Elements
{
get { return _elements; }
}
}
class ButtonImageStates
{
public string Key { get; set; }
public BitmapImage[] StateImages { get; set; }
}
转换器:
class OrderedFlagConverter : IMultiValueConverter
{
public object Convert(object[] values,
Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
BitmapImageArray imageData = (BitmapImageArray)parameter;
string type = (string)values[0];
foreach (ButtonImageStates buttonStates in imageData.Elements)
{
if (buttonStates.Key == type)
{
int index = 1;
while (index < values.Length)
{
if ((bool)values[index])
{
break;
}
index++;
}
return buttonStates.StateImages[index - 1];
}
}
return DependencyProperty.UnsetValue;
}
public object[] ConvertBack(object value,
Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
在您的示例中,使用上面的内容可能如下所示:
<DataTemplate x:Key="MenuTemplate">
<Button Command="{Binding ClickCommand}" CommandParameter="{Binding}">
<Image>
<Image.Source>
<MultiBinding>
<MultiBinding.Converter>
<l:OrderedFlagConverter/>
</MultiBinding.Converter>
<MultiBinding.ConverterParameter>
<l:BitmapImageArray>
<l:ButtonImageStates Key="ShopPage">
<l:ButtonImageStates.StateImages>
<x:Array Type="{x:Type BitmapImage}">
<BitmapImage UriSource="/Image/Shop_selected.png"/>
<BitmapImage UriSource="/Image/Shop_hover.png"/>
<BitmapImage UriSource="/Image/Shop_normal.png"/>
</x:Array>
</l:ButtonImageStates.StateImages>
</l:ButtonImageStates>
<l:ButtonImageStates Key="AboutPage">
<l:ButtonImageStates.StateImages>
<x:Array Type="{x:Type BitmapImage}">
<BitmapImage UriSource="/Image/About_selected.png"/>
<BitmapImage UriSource="/Image/About_hover.png"/>
<BitmapImage UriSource="/Image/About_normal.png"/>
</x:Array>
</l:ButtonImageStates.StateImages>
</l:ButtonImageStates>
</l:BitmapImageArray>
</MultiBinding.ConverterParameter>
<Binding Path="ButtonType"/>
<Binding Path="IsMouseOver" RelativeSource="{RelativeSource Self}"/>
<Binding Path="IsSelected"/>
</MultiBinding>
</Image.Source>
</Image>
</Button>
</DataTemplate>
转换器将输入绑定到影响按钮可视状态的属性。 第一个绑定值只是按钮的类型; 这用于查找按钮的正确按钮状态数组。 其余的绑定值(在这种方法中你可以任意多个)是搜索的标志; 图像以与标志相同的顺序存储,最后有一个附加的“默认”图像(即如果没有设置标志,则返回默认图像)。
这样,添加新的按钮类型只涉及添加一个新的ButtonImageStates
对象,为该按钮类型指定正确的键,添加新的按钮状态只涉及向每个按钮类型的列表添加一行:与图像对应的BitmapImage
引用对于该按钮类型的状态。
这样做可以大大减少必须添加的代码量,因为需要新的按钮类型和状态:只需要在XAML中提及一次给定的按钮类型,同样每个触发属性只提一次。 仅XAML方法将需要大量重复的样板,并且实际的图像文件引用将分散在样式声明中。
这是一个基本技术的简单演示。 缺乏一个好的MCVE ,我不想浪费时间重新创建代码的一部分,这些代码对于演示来说并非严格必要:
ItemsControl
来显示按钮。 以下是示例中使用的图像(我是程序员,而不是艺术家......我认为甚至不打扰图像内容,因为这也不是严格要求演示基本技术,但我认为我可以处理四个基本图像:) ):
这些将添加到项目的“Resources”文件夹中, Build Action
设置为Resource
。
XAML:
<Window x:Class="TestSO34193266MultiTriggerBinding.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:l="clr-namespace:TestSO34193266MultiTriggerBinding"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<l:OrderedFlagConverter x:Key="orderedFlagConverter1"/>
<BitmapImage x:Key="bitmapRedNormal"
UriSource="pack://application:,,,/Resources/red_normal.png"/>
<BitmapImage x:Key="bitmapRedHover"
UriSource="pack://application:,,,/Resources/red_hover.png"/>
<BitmapImage x:Key="bitmapGreenNormal"
UriSource="pack://application:,,,/Resources/green_normal.png"/>
<BitmapImage x:Key="bitmapGreenHover"
UriSource="pack://application:,,,/Resources/green_hover.png"/>
<l:ViewModel x:Key="redViewModel" ButtonType="Red"/>
<l:ViewModel x:Key="greenViewModel" ButtonType="Green"/>
<x:Array x:Key="items" Type="{x:Type l:ViewModel}">
<StaticResource ResourceKey="redViewModel"/>
<StaticResource ResourceKey="greenViewModel"/>
</x:Array>
<x:Array x:Key="redButtonStates" Type="{x:Type BitmapImage}">
<StaticResource ResourceKey="bitmapRedHover"/>
<StaticResource ResourceKey="bitmapRedNormal"/>
</x:Array>
<x:Array x:Key="greenButtonStates" Type="{x:Type BitmapImage}">
<StaticResource ResourceKey="bitmapGreenHover"/>
<StaticResource ResourceKey="bitmapGreenNormal"/>
</x:Array>
<l:BitmapImageArray x:Key="allButtonStates">
<l:ButtonImageStates Key="Red" StateImages="{StaticResource redButtonStates}"/>
<l:ButtonImageStates Key="Green" StateImages="{StaticResource greenButtonStates}"/>
</l:BitmapImageArray>
<ItemsPanelTemplate x:Key="panelTemplate">
<StackPanel IsItemsHost="True" Orientation="Horizontal"/>
</ItemsPanelTemplate>
<DataTemplate x:Key="template" DataType="l:ViewModel">
<Button>
<Image Stretch="None">
<Image.Source>
<MultiBinding Converter="{StaticResource orderedFlagConverter1}"
ConverterParameter="{StaticResource allButtonStates}">
<Binding Path="ButtonType"/>
<Binding Path="IsMouseOver" RelativeSource="{RelativeSource Self}"/>
</MultiBinding>
</Image.Source>
</Image>
</Button>
</DataTemplate>
<!-- explicit namespace only for the benefit of Stack Overflow formatting -->
<p:Style TargetType="ItemsControl"
xmlns:p="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<Setter Property="ItemsSource" Value="{StaticResource items}"/>
<Setter Property="ItemsPanel" Value="{StaticResource panelTemplate}"/>
</p:Style>
</Window.Resources>
<StackPanel>
<ItemsControl ItemTemplate="{StaticResource template}"/>
</StackPanel>
</Window>
C#:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
class ViewModel
{
public string ButtonType { get; set; }
}
class OrderedFlagConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
BitmapImageArray imageData = (BitmapImageArray)parameter;
string type = (string)values[0];
foreach (ButtonImageStates buttonStates in imageData.Elements)
{
if (buttonStates.Key == type)
{
int index = 1;
while (index < values.Length)
{
if ((bool)values[index])
{
break;
}
index++;
}
return buttonStates.StateImages[index - 1];
}
}
return DependencyProperty.UnsetValue;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
[ContentProperty("Elements")]
class BitmapImageArray
{
private readonly List<ButtonImageStates> _elements = new List<ButtonImageStates>();
public List<ButtonImageStates> Elements
{
get { return _elements; }
}
}
class ButtonImageStates
{
public string Key { get; set; }
public BitmapImage[] StateImages { get; set; }
}
一个小注:由于某种原因,我在XAML编辑器中的<Window>
元素声明中得到以下错误消息:
集合属性'TestSO34193266MultiTriggerBinding.ButtonImageStates'。'StateImages'为空。
我显然没有跳过XAML编辑器要求我清楚关于ButtonImageStates
的声明和/或实现的一些ButtonImageStates
,但是我不知道这是什么。 代码编译并运行得很好,所以我没有费心去试图找出那部分。 情况很可能是有一种更好的方式来表示按钮状态图像的映射,但这种方式起作用,除了虚假错误对我来说似乎很好。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.