2016-01-22 46 views
2

我使用Xceed Extended WPF Toolkit在PropertyGrid中显示[Flags]属性的枚举。WPF Toolkit:CheckComboBox和[Flags] enum

[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

它运作良好,如果我选择框中的一些值,并打我的保存按钮 - 在我的模型中,我收到正确的枚举值。但它不工作在其他方向 - 如果我设置任何枚举值(带或不带标志)到我的属性,所有值都未选中,内容区域是

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

回答

5

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

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

UPDATE 31/01/2016 为了使动态生成的枚举,我做了以下修改的代码工作:

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

什么我忘了说 - 对不起,我会更新我的问题 - 是我不能依靠一个编译时枚举。我会动态地使用'[EnumBuilder](https://msdn.microsoft.com/de-de/library/system.reflection.emit.enumbuilder%28v=vs.110%29.aspx)''创建'Enum'。 –

+0

更新了代码以处理动态生成的带有flags属性的枚举。 –