如何在 WPF 中为 [Flags] 枚举创建通用开关?
How to make a generic switch for [Flags] enums in WPF?
<Grid Grid.Column="1" Grid.Row="1" Grid.ColumnSpan="4">
<Grid.ColumnDefinitions>
<ColumnDefinition SharedSizeGroup="DayButtons"/>
<!-- and so on-->
</Grid.ColumnDefinitions>
<ToggleButton Grid.Column="0" IsChecked="{Binding blah blah blah Converter=Whatever, ConverterParameter={x: Static TooFreakingLong}">Monday</ToggleButton>
<!-- and so on -->
</Grid>
所以我已经为一周中的几天定义了一个枚举,尽管我可以制作一个简单的网格来显示一周中的所有日子并随意切换它们(考虑一个循环日历日应用程序)。但是,我想将此模式应用于我的应用程序中的其他 [Flags]
枚举,并且我不想一遍又一遍地编写所有这些锅炉 #plate 代码。有什么建议么?谢谢!
这应该有效。 MarkupExtension
枚举你给它的任何枚举类型的值,一旦你得到它,你就可以在 ItemsControl
或任何适当的子类如 ListBox
中做任何你喜欢的事情。我也编写了一个 MultiDataConverter,它采用单个枚举标志并检查它是否包含在一组标志中。鉴于此,您可以编写一个触发器来做任何事情。
如果您希望这些值始终可见并且 included/excluded 通过(例如)项 DataTemplate 中的复选框来自标志集,那将是另一个 MultiDataConverter。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Markup;
namespace EdTest
{
[Flags]
public enum TestEnum
{
Foo = 1 << 0,
Bar = 1 << 1,
Baz = 1 << 2,
Ping = 1 << 3,
Pong = 1 << 4,
Hoop = 1 << 5,
Floop = 1 << 6
};
public class EnumEnumerator : MarkupExtension
{
public EnumEnumerator(Type type)
{
_type = type;
}
private Type _type;
public override object ProvideValue(IServiceProvider serviceProvider)
{
return Enum.GetValues(_type);
}
}
}
...并且在 XAML 中,任何显示集合的控件都可以在任何适当的模板中显示它。你可以写 ValueConverters
来用枚举值本身做更多的事情。
<ItemsControl
xmlns:test="clr-namespace:EdTest"
ItemsSource="{test:EnumEnumerator test:TestEnum}"
>
<ItemsControl.Resources>
<test:FlagCheckConverter x:Key="FlagChecker" />
</ItemsControl.Resources>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border
x:Name="Border"
Padding="6"
Width="80"
Height="60"
BorderBrush="SteelBlue"
BorderThickness="1,1,0,0"
>
<ContentControl Content="{Binding}" />
</Border>
<DataTemplate.Triggers>
<DataTrigger Value="False">
<DataTrigger.Binding>
<MultiBinding Converter="{StaticResource FlagChecker}">
<!-- The DataContext value - a single enum value -->
<Binding />
<Binding
Path="DataContext.TestFlags"
RelativeSource="{RelativeSource AncestorType=ItemsControl}"
/>
</MultiBinding>
</DataTrigger.Binding>
<Setter TargetName="Border" Property="Visibility" Value="Collapsed" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel
Orientation="Horizontal"
/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
这里是 MultiDataConverter,它检查给定的枚举值是否是一组标志中的一个:
public class FlagCheckConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType,
object parameter, CultureInfo culture)
{
var checkValue = values[0];
var flags = values[1];
if (checkValue is Enum && flags is Enum)
{
return ((Enum)flags).HasFlag((Enum)checkValue);
}
return (object)false;
}
public object[] ConvertBack(object value, Type[] targetTypes,
object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
<Grid Grid.Column="1" Grid.Row="1" Grid.ColumnSpan="4">
<Grid.ColumnDefinitions>
<ColumnDefinition SharedSizeGroup="DayButtons"/>
<!-- and so on-->
</Grid.ColumnDefinitions>
<ToggleButton Grid.Column="0" IsChecked="{Binding blah blah blah Converter=Whatever, ConverterParameter={x: Static TooFreakingLong}">Monday</ToggleButton>
<!-- and so on -->
</Grid>
所以我已经为一周中的几天定义了一个枚举,尽管我可以制作一个简单的网格来显示一周中的所有日子并随意切换它们(考虑一个循环日历日应用程序)。但是,我想将此模式应用于我的应用程序中的其他 [Flags]
枚举,并且我不想一遍又一遍地编写所有这些锅炉 #plate 代码。有什么建议么?谢谢!
这应该有效。 MarkupExtension
枚举你给它的任何枚举类型的值,一旦你得到它,你就可以在 ItemsControl
或任何适当的子类如 ListBox
中做任何你喜欢的事情。我也编写了一个 MultiDataConverter,它采用单个枚举标志并检查它是否包含在一组标志中。鉴于此,您可以编写一个触发器来做任何事情。
如果您希望这些值始终可见并且 included/excluded 通过(例如)项 DataTemplate 中的复选框来自标志集,那将是另一个 MultiDataConverter。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Markup;
namespace EdTest
{
[Flags]
public enum TestEnum
{
Foo = 1 << 0,
Bar = 1 << 1,
Baz = 1 << 2,
Ping = 1 << 3,
Pong = 1 << 4,
Hoop = 1 << 5,
Floop = 1 << 6
};
public class EnumEnumerator : MarkupExtension
{
public EnumEnumerator(Type type)
{
_type = type;
}
private Type _type;
public override object ProvideValue(IServiceProvider serviceProvider)
{
return Enum.GetValues(_type);
}
}
}
...并且在 XAML 中,任何显示集合的控件都可以在任何适当的模板中显示它。你可以写 ValueConverters
来用枚举值本身做更多的事情。
<ItemsControl
xmlns:test="clr-namespace:EdTest"
ItemsSource="{test:EnumEnumerator test:TestEnum}"
>
<ItemsControl.Resources>
<test:FlagCheckConverter x:Key="FlagChecker" />
</ItemsControl.Resources>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border
x:Name="Border"
Padding="6"
Width="80"
Height="60"
BorderBrush="SteelBlue"
BorderThickness="1,1,0,0"
>
<ContentControl Content="{Binding}" />
</Border>
<DataTemplate.Triggers>
<DataTrigger Value="False">
<DataTrigger.Binding>
<MultiBinding Converter="{StaticResource FlagChecker}">
<!-- The DataContext value - a single enum value -->
<Binding />
<Binding
Path="DataContext.TestFlags"
RelativeSource="{RelativeSource AncestorType=ItemsControl}"
/>
</MultiBinding>
</DataTrigger.Binding>
<Setter TargetName="Border" Property="Visibility" Value="Collapsed" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel
Orientation="Horizontal"
/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
这里是 MultiDataConverter,它检查给定的枚举值是否是一组标志中的一个:
public class FlagCheckConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType,
object parameter, CultureInfo culture)
{
var checkValue = values[0];
var flags = values[1];
if (checkValue is Enum && flags is Enum)
{
return ((Enum)flags).HasFlag((Enum)checkValue);
}
return (object)false;
}
public object[] ConvertBack(object value, Type[] targetTypes,
object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}