c# - WPF 工具包 : CheckComboBox and [Flags] enum

标签 c# wpf enums wpftoolkit xceed

我正在使用 Xceed Extended WPF Toolkit 在 PropertyGrid 中显示带有 [Flags] 属性的枚举。

[Flags]             
public enum TestEnum
{
    Test1 = 1,
    Test2 = 2,
    Test3 = 4,
    Test4 = 8,
    Test5 = 16,
    Test6 = 32,
    Test7 = 64,
}

因为我无法在编译时知道枚举定义,所以我会使用 EnumBuilder 动态创建一个枚举.

我创建了一个编辑器来将枚举显示为 CheckComboBox:

public class CheckComboBoxEditor : TypeEditor<CheckComboBox>, ITypeEditor
{
    protected override void SetValueDependencyProperty()
    {
        ValueProperty = CheckComboBox.SelectedValueProperty;
    }

    protected override CheckComboBox CreateEditor()
    {
        return new CheckComboBox();
    }

    protected override void ResolveValueBinding(PropertyItem propertyItem)
    {
        var _binding = new Binding("Value");
        _binding.Source = propertyItem;
        _binding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
        _binding.Mode = BindingMode.TwoWay;
        _binding.Converter = CreateValueConverter();
        BindingOperations.SetBinding(Editor, CheckComboBox.SelectedValueProperty, _binding);

        var _binding2 = new Binding("Value");
        _binding2.Source = propertyItem;
        _binding2.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
        _binding2.Mode = BindingMode.TwoWay;
        _binding2.Converter = CreateValueConverter();
        BindingOperations.SetBinding(Editor, CheckComboBox.SelectedItemProperty, _binding2);

        Editor.ItemsSource = Enum.GetValues(propertyItem.Value.GetType());
    }
}

如您所见,到目前为止,我已尝试分别绑定(bind) SelectedValueSelectedItem 属性。 CreateValueConverter() 在基类中定义并返回 null

如果我在框中选择一些值并点击我的保存按钮,它会很好地工作 - 在我的模型中,我收到正确的枚举值。但它在另一个方向上不起作用 - 如果我为我的属性设置任何枚举值(有或没有标志),所有值都未选中并且内容区域是

你有解决这个问题的想法吗?

最佳答案

在这种情况下,对于带有 FlagsAttribute 的枚举,最通用的解决方案是为枚举的所有项目和选定项目使用 VM 中的特定字段。类似的东西:

XAML

<Window.DataContext>
    <local:MainWindowViewModel />
</Window.DataContext>
<Grid>

    <xctkpg:PropertyGrid Grid.Row="1" SelectedObject="{Binding CurrentObject}" AutoGenerateProperties="False">
        <xctkpg:PropertyGrid.EditorDefinitions>
            <xctkpg:EditorTemplateDefinition TargetProperties="{x:Type local:TestEnum}">

                <xctkpg:EditorTemplateDefinition.EditingTemplate>
                    <DataTemplate>
                            <xctk:CheckComboBox ItemsSource="{Binding Instance.Items}" SelectedValue="{Binding Instance.SelValue}" />
                    </DataTemplate>
                </xctkpg:EditorTemplateDefinition.EditingTemplate>

            </xctkpg:EditorTemplateDefinition>
        </xctkpg:PropertyGrid.EditorDefinitions>
        <xctkpg:PropertyGrid.PropertyDefinitions>
            <xctkpg:PropertyDefinition TargetProperties="Value" />
        </xctkpg:PropertyGrid.PropertyDefinitions>
    </xctkpg:PropertyGrid>

</Grid>

C#

class MainWindowViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    private ItemViewModel _currentObject = new ItemViewModel() { Value = TestEnum.Test3 | TestEnum.Test7 };
    public ItemViewModel CurrentObject
    {
        get { return _currentObject; }
        set
        {
            _currentObject = value;
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(CurrentObject)));
        }
    }
}


public class ItemViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    private TestEnum _value;
    public TestEnum Value
    {
        get { return _value; }
        set
        {
            _value = value;
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Value)));
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(SelValue)));
        }
    }

    public string SelValue
    {
        get
        {
            return String.Join(",", Enum.GetValues(typeof(TestEnum)).OfType<TestEnum>().Where(v => (_value & v) != 0).Select(v => v.ToString()));
        }
        set
        {
            _value = value.Split(new[] { ','}, StringSplitOptions.RemoveEmptyEntries).Select(s => s.Trim()). 
                Aggregate((TestEnum)0, (acc, val) => acc | (TestEnum)Enum.Parse(typeof(TestEnum), val));

            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(SelValue)));
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Value)));
        }
    }

    public ObservableCollection<string> Items
    {
        get
        {
            return new ObservableCollection<string>(Enum.GetNames(typeof(TestEnum)));
        }
    }
}

2016 年 1 月 31 日更新 为了使代码适用于动态生成的枚举,我进行了以下更改:

XAML

 <xctkpg:EditorTemplateDefinition TargetProperties="Value">

C#

public class ItemViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    public static readonly Type EnumType = GenerateEnumType();

    private object _value;
    public object Value
    {
        get { return _value; }
        set
        {
            _value = value;
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Value)));
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(SelValue)));
        }
    }

    public string SelValue
    {
        get
        {
            return String.Join(",", 
                Enum.GetValues(EnumType).OfType<object>().Where(v => ((int)_value & (int)v) != 0).Select(v => v.ToString()));
        }
        set
        {
            var strings = value.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(s => s.Trim());        
            _value = Enum.ToObject(EnumType, strings.Aggregate(0, (acc, val) => acc | (int)Enum.Parse(EnumType, val)));


            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(SelValue)));
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Value)));
        }
    }

    public ObservableCollection<string> Items
    {
        get
        {
            return new ObservableCollection<string>(Enum.GetNames(EnumType));
        }
    }

    public static Type GenerateEnumType()
    {
        string asmNameString = "flags_enum";

        //    Create Base Assembly Objects
        AppDomain appDomain = AppDomain.CurrentDomain;
        AssemblyName asmName = new AssemblyName(asmNameString);
        AssemblyBuilder asmBuilder = appDomain.DefineDynamicAssembly(asmName, AssemblyBuilderAccess.Run);

        //    Create Module and Enumeration Builder Objects
        ModuleBuilder modBuilder = asmBuilder.DefineDynamicModule(asmNameString + "_module");
        EnumBuilder enumBuilder = modBuilder.DefineEnum(asmNameString, TypeAttributes.Public, typeof(int));

        Type fa = typeof(FlagsAttribute);


        CustomAttributeBuilder attributeBuilder =
                 new CustomAttributeBuilder(fa.GetConstructor(new Type[0]), new object[0]);

        enumBuilder.SetCustomAttribute(attributeBuilder);

        for (int i = 0; i < 7; i++)
        {
            enumBuilder.DefineLiteral($"Test{i + 1}", 1 << i);
        }

        return enumBuilder.CreateType();
    }
}

现在 ItemViewModel 的值可以这样设置:

ItemViewModel vm = new ItemViewModel();
vm.Value = Enum.ToObject(ItemViewModel.EnumType, 33);

关于c# - WPF 工具包 : CheckComboBox and [Flags] enum,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34941352/

相关文章:

c# - WPF Windows 窗体主机键盘事件

c# - 为什么消息框从不显示?

c# - 如何在事件处理程序内调用 WebBrowser 上的方法?

c# - XamDataGrid 中的自定义排序以进行多重绑定(bind)

wpf - 事件 'foo' 不是 RoutedEvent

c++ - 检查枚举有效性

go - 如何使用 Golang 中的数字枚举以用户友好的 JSON 格式保存节俭结构?

c# - 获取枚举值的属性

c# - 使用 dotPeek 反编译 .net 程序集给出无效语法

c# - 查找下一个文件 ERROR_INVALID_NAME