在我看来,您已经收到并接受的答案是一个不错的答案。它完全基于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="" CommandParameter="">
<Image>
<Image.Source>
<MultiBinding>
<MultiBinding.Converter>
<l:OrderedFlagConverter/>
</MultiBinding.Converter>
<MultiBinding.ConverterParameter>
<l:BitmapImageArray>
<l:ButtonImageStates Key="ShopPage">
<l:ButtonImageStates.StateImages>
<x:Array Type="">
<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="">
<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=""/>
<Binding Path="IsSelected"/>
</MultiBinding>
</Image.Source>
</Image>
</Button>
</DataTemplate>
该转换器以影响按钮视觉状态的属性绑定作为输入。第一个绑定值仅是按钮的类型;这用于查找正确的按钮状态数组。剩余的绑定值(在此方法中可以任意多个)是要搜索的标志;图像按照标志的相同顺序存储,末尾还有一个额外的“默认”图像(即如果未设置任何标志,则返回默认图像)。
通过这种方式,添加新的按钮类型只需添加一个新的ButtonImageStates对象,指定该按钮类型的正确键,并且添加新的按钮状态只需要向每个按钮类型的列表添加一行:与该按钮类型的该状态的图像对应的BitmapImage引用。
以这种方式进行操作可以大大减少需要添加的代码量,因为需要新的按钮类型和状态:给定的按钮类型只需在XAML中提及一次,同样每个触发属性只被提及一次。仅使用XAML的方法将需要大量重复的模板代码,并且实际的图像文件引用将分散在样式声明中。
这是基本技术的简单演示。由于没有一个好的
MCVE可以开始,我不想浪费时间重新创建不是演示所必需的代码部分:
- 我只创建了四个状态图像,并且当然只编写了处理两种不同按钮类型的四个可能状态的代码。
- 我也没有将其放在菜单中;我只使用一个普通的
ItemsControl
来呈现按钮。
- 当然,视图模型是一个简化的类;我没有关注属性更改通知,因为它在这里不需要。如果包括那个,例子仍然可以工作。
这是演示中使用的图像(我是一名程序员,不是艺术家……我甚至考虑过不去管图像内容,因为这也不是演示基本技术所必需的,但我认为我可以处理四个基本图像:):
![red_normal.png](https://istack.dev59.com/BOzgk.webp)
![red_hover.png](https://istack.dev59.com/rd6BF.webp)
![green_normal.png](https://istack.dev59.com/ibZxD.webp)
![green_hover.png](https://istack.dev59.com/Lt2fq.webp)
这些被添加到项目的"Resources"文件夹中,
Build Action
设置为
Resource
。
XAML:
<Window x:Class="TestSO34193266MultiTriggerBinding.MainWindow"
xmlns="http:
xmlns:x="http:
xmlns:l="clr-namespace:TestSO34193266MultiTriggerBinding"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<l:OrderedFlagConverter x:Key="orderedFlagConverter1"/>
<BitmapImage x:Key="bitmapRedNormal"
UriSource="pack:
<BitmapImage x:Key="bitmapRedHover"
UriSource="pack:
<BitmapImage x:Key="bitmapGreenNormal"
UriSource="pack:
<BitmapImage x:Key="bitmapGreenHover"
UriSource="pack:
<l:ViewModel x:Key="redViewModel" ButtonType="Red"/>
<l:ViewModel x:Key="greenViewModel" ButtonType="Green"/>
<x:Array x:Key="items" Type="">
<StaticResource ResourceKey="redViewModel"/>
<StaticResource ResourceKey="greenViewModel"/>
</x:Array>
<x:Array x:Key="redButtonStates" Type="">
<StaticResource ResourceKey="bitmapRedHover"/>
<StaticResource ResourceKey="bitmapRedNormal"/>
</x:Array>
<x:Array x:Key="greenButtonStates" Type="">
<StaticResource ResourceKey="bitmapGreenHover"/>
<StaticResource ResourceKey="bitmapGreenNormal"/>
</x:Array>
<l:BitmapImageArray x:Key="allButtonStates">
<l:ButtonImageStates Key="Red" StateImages=""/>
<l:ButtonImageStates Key="Green" StateImages=""/>
</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=""
ConverterParameter="">
<Binding Path="ButtonType"/>
<Binding Path="IsMouseOver" RelativeSource=""/>
</MultiBinding>
</Image.Source>
</Image>
</Button>
</DataTemplate>
<!-- explicit namespace only for the benefit of Stack Overflow formatting -->
<p:Style TargetType="ItemsControl"
xmlns:p="http:
<Setter Property="ItemsSource" Value=""/>
<Setter Property="ItemsPanel" Value=""/>
</p:Style>
</Window.Resources>
<StackPanel>
<ItemsControl ItemTemplate=""/>
</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
的声明和/或实现所需达成的协议,但我不知道是什么。代码可以正确编译和运行,所以我没有费心去解决那个部分。也许有更好的方法来表示按钮状态图像的映射,但这种方式有效,并且除了虚假的错误之外,对我来说还好。