【问题标题】:WPF Toolkit: CheckComboBox and [Flags] enumWPF 工具包:CheckComboBox 和 [Flags] 枚举
【发布时间】:2016-01-22 07:35:38
【问题描述】:

我正在使用 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());
    }
}

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

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

你有什么办法解决这个问题吗?

【问题讨论】:

    标签: c# wpf enums wpftoolkit xceed


    【解决方案1】:

    在这种情况下,对于带有 FlagsAttribute 的 Enum,最通用的解决方案是对 Enum 的所有项目和选定项目使用 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 日更新 为了使代码适用于动态生成的 Enum,我进行了以下更改:

    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);
    

    【讨论】:

    • 我忘了提 - 抱歉,我会更新我的问题 - 我不能依赖编译时枚举。我会使用[EnumBuilder](https://msdn.microsoft.com/de-de/library/system.reflection.emit.enumbuilder%28v=vs.110%29.aspx) 动态创建Enum
    • 更新代码以处理动态生成的带有标志属性的枚举。
    猜你喜欢
    • 1970-01-01
    • 2021-09-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-12
    • 1970-01-01
    • 2019-02-16
    相关资源
    最近更新 更多