如何避免在菜单中重复XAML块 [英] How to avoid repeating blocks of XAML in a menu
问题描述
我必须创建一个菜单.它有10个条目,并且它们之间的差异仅是一个参数.
I have to create a menu. It has 10 entries and they differ by one parameter.
条目1:
<MenuItem Visibility="{Binding MenuSelected.Type, Converter={StaticResource TypeToVisibilityConverter}, ConverterParameter='PAZ', Mode=OneWay}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<i:InvokeCommandAction Command="{Binding CmdContextMenu}" CommandParameter="PAZ" />
</i:EventTrigger>
</i:Interaction.Triggers>
<MenuItem.Header>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock
Grid.Column="0"
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontFamily="Segoe MDL2 Assets"
Foreground="{Binding MenuSelected.Type, Converter={StaticResource TypeToColorConverter}, ConverterParameter='PAZ', Mode=OneWay}"
Text="{Binding MenuSelected.Type, Converter={StaticResource TypeToIconConverter}, ConverterParameter='PAZ', Mode=OneWay}" />
<TextBlock
Grid.Column="1"
Margin="{StaticResource XSmallLeftMargin}"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Text="PAZ" />
</Grid>
</MenuItem.Header>
</MenuItem>
条目2:
<MenuItem Visibility="{Binding MenuSelected.Type, Converter={StaticResource TypeToVisibilityConverter}, ConverterParameter='APP', Mode=OneWay}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<i:InvokeCommandAction Command="{Binding CmdContextMenu}" CommandParameter="APP" />
</i:EventTrigger>
</i:Interaction.Triggers>
<MenuItem.Header>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock
Grid.Column="0"
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontFamily="Segoe MDL2 Assets"
Foreground="{Binding MenuSelected.Type, Converter={StaticResource TypeToColorConverter}, ConverterParameter='APP', Mode=OneWay}"
Text="{Binding MenuSelected.Type, Converter={StaticResource TypeToIconConverter}, ConverterParameter='APP', Mode=OneWay}" />
<TextBlock
Grid.Column="1"
Margin="{StaticResource XSmallLeftMargin}"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Text="APP" />
</Grid>
</MenuItem.Header>
</MenuItem>
正如您所看到的,唯一的区别是 PAZ
和 APP
之间的不同点……其他所有方面都一样.有什么办法可以避免只更改3个字母就重复10次吗?
As you can see, the only difference is between PAZ
and APP
in different points... and same goes on for all the others.
Is there a way I can avoid to repeat it 10 times just changing the 3 letters?
我不想使用代码隐藏功能动态创建菜单...而是从XAML处理它.
I do not want to use code-behind to create the menu dynamically... but to process it from XAML.
推荐答案
根据您的问题,我假定 CmdContextMenu
和 MenuSelected
是主视图模型的属性,而不是单独的菜单项类型.如果不同,则必须相应地修改代码.
From your question I assume that CmdContextMenu
and MenuSelected
are properties on your main view model and not in a separate menu item type. If this is different, you have to adapt the code accordingly.
为了删除冗余代码,请在视图模型中为菜单项创建一个集合.
In order to remove the redundant code, create a collection for your menu items in your view model.
public class MyMainViewModel : INotifyPropertyChanged
{
public IEnumerable<string> MyTypes { get; } = new List<string>
{
"PAZ",
"APP"
};
// ...other properties and methods (like "CmdContextMenu" and "MenuSelected" I Assume).
}
接下来,您必须更改值转换器,因为 <可以绑定多个值的code> IMultiValueConverter .调整所有转换器以实现 IMultiValueConverter
.这是一个转换器外观的示例.当然,这取决于您的实现.本质上,使用 values
数组访问绑定值.
Next, you have to change the value converters, because the ConverterParameter
is not a dependency property and cannot be bound. Instead, you can use IMultiValueConverter
s that can bind multiple values. Adapt all of your converters to implement IMultiValueConverter
. Here is an example of how a converter could look like. Of course, it depends on your implementation. In essence, use the values
array to access bound values.
public class TypeToVisibilityConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
return values[0].Equals(values[1]) ? Visibility.Visible : Visibility.Collapsed;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
接下来,为菜单项的标题创建一个数据模板.如您所见,绑定被 MultiBinding
替换,后者使用 IMultiValueConverter
作为 Converter
.
Next, create a data template for the header of your menu items. As you can see, the bindings are replaced with MultiBinding
s that use an IMultiValueConverter
as Converter
.
每个块中的第一个绑定是一个 RelativeSource
绑定,用于访问父 Menu
的数据上下文,因为我假设 MenuSelected
属性是在主视图模型上定义的.其他空绑定将绑定到当前菜单项的数据上下文,该菜单项是 MyTypes
集合中的项.
The first binding in each block is a RelativeSource
binding to access the data context of the parent Menu
, because I assume that the MenuSelected
property is defined on your main view model. The other empty bindings will bind to the data context of the current menu item, which is an item from the MyTypes
collection.
<DataTemplate x:Key="MyMenuItemHeaderTemplate" DataType="{x:Type system:String}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0"
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontFamily="Segoe MDL2 Assets">
<TextBlock.Foreground>
<MultiBinding Converter="{StaticResource TypeToColorConverter}">
<Binding Path="DataContext.MenuSelected.Type" RelativeSource="{RelativeSource AncestorType={x:Type Menu}}" Mode="OneWay"/>
<Binding/>
</MultiBinding>
</TextBlock.Foreground>
<TextBlock.Text>
<MultiBinding Converter="{StaticResource TypeToIconConverter}">
<Binding Path="DataContext.MenuSelected.Type" RelativeSource="{RelativeSource AncestorType={x:Type Menu}}" Mode="OneWay"/>
<Binding/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
<TextBlock Grid.Column="1"
Margin="{StaticResource XSmallLeftMargin}"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Text="{Binding}"/>
</Grid>
</DataTemplate>
创建使用此数据模板的新标题项目样式. Visibility
(可见性)还使用如上所述的多值转换器.无需使用事件触发器,只需将命令直接分配给菜单项并传递 CommandParameter
(绑定到当前菜单项的数据上下文)即可.
Create a new header item style that uses this data template. The Visibility
also uses a multi-value converter as above. Instead of using an event trigger, you can simply assign the command to the menu item directly and pass a CommandParameter
, which is bound to the data context of the current menu item.
<Style x:Key="MyMenuItemStyle" TargetType="{x:Type MenuItem}" BasedOn="{StaticResource {x:Type MenuItem}}">
<Setter Property="HeaderTemplate" Value="{StaticResource MyMenuItemHeaderTemplate}"/>
<Setter Property="Visibility">
<Setter.Value>
<MultiBinding Converter="{StaticResource TypeToVisibilityConverter}">
<Binding Path="DataContext.MenuSelected.Type" RelativeSource="{RelativeSource AncestorType={x:Type Menu}}" Mode="OneWay"/>
<Binding/>
</MultiBinding>
</Setter.Value>
</Setter>
<Setter Property="Command" Value="{Binding DataContext.CmdContextMenu, RelativeSource={RelativeSource AncestorType={x:Type Menu}}}"/>
<Setter Property="CommandParameter" Value="{Binding}"/>
</Style>
最后,创建一个 Menu
并绑定 MyTypes
集合以及样式.
Finally, create a Menu
and bind the MyTypes
collection, as well as the style.
<Menu ItemsSource="{Binding MyTypes}" ItemContainerStyle="{StaticResource MyMenuItemStyle}"/>
这篇关于如何避免在菜单中重复XAML块的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!