简体   繁体   English

WPF工具包:CheckComboBox和[Flags]枚举

[英]WPF Toolkit: CheckComboBox and [Flags] enum

I'm using Xceed Extended WPF Toolkit to display a enum with [Flags] attribute in a PropertyGrid . 我正在使用Xceed Extended WPF Toolkit在PropertyGrid显示带有[Flags]属性的枚举。

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

Because I can't know the enum definition at compile time, I would dynamically create an Enum using EnumBuilder . 因为我在编译时不知道枚举定义,所以我将使用EnumBuilder动态创建一个Enum。

I created an editor to display the enum as CheckComboBox : 我创建了一个编辑器以将枚举显示为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());
    }
}

As you can see, so far I've tried to bind each the SelectedValue and the SelectedItem property. 如您所见,到目前为止,我已经尝试绑定每个SelectedValueSelectedItem属性。 CreateValueConverter() is defined in the base class and returns null . CreateValueConverter()在基类中定义,并返回null

It works well if I select some Values in the box and hit my save Button - in my model, I receive the correct enum value. 如果我在框中选择一些值并点击保存按钮,则效果很好-在我的模型中,我收到了正确的枚举值。 But it doesn't work in the other direction - if i set any enum value (with or without flags) to my property, all values are unchecked and the content area is empty . 但这在另一个方向上不起作用 -如果我为属性设置任何枚举值(带有或不带有标志),则所有值均未选中,内容区域为

Do you have any idea to solve this problem? 您有解决这个问题的想法吗?

For Enum with FlagsAttribute in that case the most general solution would be to use specific fields in your VM for all items of the Enum and for selected items. 在这种情况下,对于具有FlagsAttribute的Enum,最通用的解决方案是在VM中为Enum的所有项目和选定项目使用特定字段。 Something like that: 像这样:

XAML 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# 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)));
        }
    }
}

UPDATE 31/01/2016 To make the code work for dynamically generated Enum, I've made following changes: 更新31/01/2016为了使代码适用于动态生成的枚举,我进行了以下更改:

XAML XAML

 <xctkpg:EditorTemplateDefinition TargetProperties="Value">

C# 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();
    }
}

Now for ItemViewModel Value could be set like this: 现在可以为ItemViewModel设置值,如下所示:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM