C# PropertyGrid是否可以为不同的选定对象自定义显示值,而不是空白?

C# PropertyGrid是否可以为不同的选定对象自定义显示值,而不是空白?,c#,propertygrid,C#,Propertygrid,当选定对象具有不同的特性值时,是否可以自定义特性的显示值 网格的默认行为是,当所有选定对象具有相同的值时,显示一个值,但当它们不同时,仅将字段留空。没有办法知道他们有什么不同 例如,给定以下类和代码,是否可以将检查器和类配置为显示如下内容(整数值的范围,其他值的倍数) 也就是说,如果值不同,则表示它们之间的差异,但如果它们都相同,则表示该值 public enum TestEnum { EnumVal1, EnumVal2, EnumVal3 } public clas

当选定对象具有不同的特性值时,是否可以自定义特性的显示值

网格的默认行为是,当所有选定对象具有相同的值时,显示一个值,但当它们不同时,仅将字段留空。没有办法知道他们有什么不同

例如,给定以下类和代码,是否可以将检查器和类配置为显示如下内容(整数值的范围,其他值的倍数)

也就是说,如果值不同,则表示它们之间的差异,但如果它们都相同,则表示该值

public enum TestEnum
{
    EnumVal1,
    EnumVal2,
    EnumVal3
}

public class TestClass
{
    public long TestLong { get; set; }
    public int TestInt { get; set; }
    public TestEnum TestEnum { get; set; }
}

    ...
control.SelectedObjects = new []
{
    new TestClass 
    { 
        TestLong = 50, 
        TestInt = 10, 
        TestEnum = TestEnum.EnumVal1 
    },
    new TestClass 
    { 
        TestLong = 60, 
        TestInt = 10, 
        TestEnum = TestEnum.EnumVal3
    },
}
    ...

我不认为您可以更改显示,因为PropertyGrid一般使用(包括隐式的)来显示值,但是对于多个选择,不使用它

不过,您可以做的是,当网格处于多重选择模式时,提出一个自定义选项,如下所示:

    public class TestClass
    {
        // decorate the property with a custom UITypeEditor
        [Editor(typeof(MyMultiSelectionEditor), typeof(UITypeEditor))]
        public long TestLong { get; set; }
        public int TestInt { get; set; }
        public TestEnum TestEnum { get; set; }
    }

    public class MyMultiSelectionEditor : UITypeEditor
    {
        public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context)
        {
            // adapt to your need
            if (!IsPropertyGridInMultiView(context))
                return UITypeEditorEditStyle.None;

            return UITypeEditorEditStyle.Modal;
        }

        public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value)
        {
            if (IsPropertyGridInMultiView(context))
            {
                // multi view, show my custom stuff
                MessageBox.Show("hello from multi selection");
            }
            return base.EditValue(context, provider, value);
        }

        // gets a PropertyGrid instance from the context, if any
        private static PropertyGrid GetPropertyGrid(ITypeDescriptorContext context)
        {
            IServiceProvider sp = context as IServiceProvider;
            if (sp == null)
                return null;

            Control view = sp.GetService(typeof(IWindowsFormsEditorService)) as Control;
            if (view == null)
                return null;

            return view.Parent as PropertyGrid;
        }

        // determines if there is a PropertyGrid in the context, and if it's selection is multiple
        private static bool IsPropertyGridInMultiView(ITypeDescriptorContext context)
        {
            PropertyGrid pg = GetPropertyGrid(context);
            if (pg == null)
                return false;

            return pg.SelectedObjects != null && pg.SelectedObjects.Length > 1;
        }
    }

我已经解决了这个问题,虽然不是我原来打算的那样

问题的根本原因是当有多个选定对象时,没有应用TypeConverter。为了解决这个问题,我引入了一个聚合类,它对单个对象进行聚合,但作为单个对象呈现。这样,就可以调用TypeConverter

示例代码(真实生产代码的黑客版本)如下:

public interface ISupportAggregate
{
    object[] Individuals { get; }
}

public class AggregateTypeConverter : TypeConverter
{
    public const string MULTIPLE = @"[multiple]";

    private TypeConverter mTypeConverter;

    public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType)
    {
        Initialize(context);

        if (context != null && destinationType == typeof(string))
        {
            var aggregate = context.Instance as ISupportAggregate;
            if (aggregate != null && IsDifferingItems(context.PropertyDescriptor.Name, aggregate.Individuals))
            {
                return MULTIPLE;
            }
        }
        return mTypeConverter.ConvertTo(context, culture, value, destinationType);
    }

    public static bool IsDifferingItems(string propertyName, object[] items)
    {
        PropertyDescriptor itemProperty = TypeDescriptor.GetProperties(items[0].GetType())[propertyName];
        return items.Select(itemProperty.GetValue).Distinct().Count() > 1;
    }

    private void Initialize(ITypeDescriptorContext context)
    {
        if (mTypeConverter == null)
            mTypeConverter = TypeDescriptor.GetConverter(context.PropertyDescriptor.PropertyType);
    }

    #region Calling through to mTypeConverter

    public override object CreateInstance(ITypeDescriptorContext context, System.Collections.IDictionary propertyValues)
    {
        Initialize(context);
        return mTypeConverter.CreateInstance(context, propertyValues);
    }

    public override bool GetCreateInstanceSupported(ITypeDescriptorContext context)
    {
        Initialize(context);
        return mTypeConverter.GetCreateInstanceSupported(context);
    }

    public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes)
    {
        Initialize(context);
        return mTypeConverter.GetProperties(context, value, attributes);
    }

    public override bool GetPropertiesSupported(ITypeDescriptorContext context)
    {
        Initialize(context);
        return mTypeConverter.GetPropertiesSupported(context);
    }

    public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context)
    {
        Initialize(context);
        return mTypeConverter.GetStandardValues(context);
    }

    public override bool GetStandardValuesExclusive(ITypeDescriptorContext context)
    {
        Initialize(context);
        return mTypeConverter.GetStandardValuesExclusive(context);
    }

    public override bool GetStandardValuesSupported(ITypeDescriptorContext context)
    {
        Initialize(context);
        return mTypeConverter.GetStandardValuesSupported(context);
    }

    public override bool IsValid(ITypeDescriptorContext context, object value)
    {
        Initialize(context);
        return mTypeConverter.IsValid(context, value);
    }

    public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
    {
        Initialize(context);
        return mTypeConverter.CanConvertFrom(context, sourceType);
    }

    public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
    {
        Initialize(context);
        return mTypeConverter.ConvertFrom(context, culture, value);
    }

    public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
    {
        Initialize(context);
        return mTypeConverter.CanConvertTo(context, destinationType);
    }

    #endregion
}

public class AggregateTestClass : TestClass
{
    private readonly TestClass[] mObjects;

    public AggregateTestClass(TestClass[] objects)
    {
        mObjects = objects;
    }

    protected override object[] GetIndividuals()
    {
        return mObjects;
    }
}

public class TestClass : ISupportAggregate
{
    [TypeConverter(typeof(AggregateTypeConverter))]
    public int IntProperty { get; set; }

    [TypeConverter(typeof(AggregateTypeConverter))]
    public string StringProperty { get; set; }

    [BrowsableAttribute(false)]
    public object[] Individuals
    {
        get { return GetIndividuals(); }
    }

    virtual protected object[] GetIndividuals()
    {
        return new[] { this };
    }
}

public class TestSupportAggregate : ISupportAggregate, TestClass
{
    private readonly TestClass[] mItems;

    public TestSupportAggregate(TestClass[] items)
    {
        mItems = items;
    }

    public object[] Individuals
    {
        get { return mItems; }
    }
}


To use in code:

control.SelectedObject = new TestSupportAggregate(new[]
                                                 {
                                                     new TestClass { IntProperty = 5150 },
                                                     new TestClass { IntProperty = 1984 }
                                                 });

谢谢你,西蒙。我已经试过了,虽然它很有用,但并不是我所需要的。我想在编辑之前自定义显示值。
public interface ISupportAggregate
{
    object[] Individuals { get; }
}

public class AggregateTypeConverter : TypeConverter
{
    public const string MULTIPLE = @"[multiple]";

    private TypeConverter mTypeConverter;

    public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType)
    {
        Initialize(context);

        if (context != null && destinationType == typeof(string))
        {
            var aggregate = context.Instance as ISupportAggregate;
            if (aggregate != null && IsDifferingItems(context.PropertyDescriptor.Name, aggregate.Individuals))
            {
                return MULTIPLE;
            }
        }
        return mTypeConverter.ConvertTo(context, culture, value, destinationType);
    }

    public static bool IsDifferingItems(string propertyName, object[] items)
    {
        PropertyDescriptor itemProperty = TypeDescriptor.GetProperties(items[0].GetType())[propertyName];
        return items.Select(itemProperty.GetValue).Distinct().Count() > 1;
    }

    private void Initialize(ITypeDescriptorContext context)
    {
        if (mTypeConverter == null)
            mTypeConverter = TypeDescriptor.GetConverter(context.PropertyDescriptor.PropertyType);
    }

    #region Calling through to mTypeConverter

    public override object CreateInstance(ITypeDescriptorContext context, System.Collections.IDictionary propertyValues)
    {
        Initialize(context);
        return mTypeConverter.CreateInstance(context, propertyValues);
    }

    public override bool GetCreateInstanceSupported(ITypeDescriptorContext context)
    {
        Initialize(context);
        return mTypeConverter.GetCreateInstanceSupported(context);
    }

    public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes)
    {
        Initialize(context);
        return mTypeConverter.GetProperties(context, value, attributes);
    }

    public override bool GetPropertiesSupported(ITypeDescriptorContext context)
    {
        Initialize(context);
        return mTypeConverter.GetPropertiesSupported(context);
    }

    public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context)
    {
        Initialize(context);
        return mTypeConverter.GetStandardValues(context);
    }

    public override bool GetStandardValuesExclusive(ITypeDescriptorContext context)
    {
        Initialize(context);
        return mTypeConverter.GetStandardValuesExclusive(context);
    }

    public override bool GetStandardValuesSupported(ITypeDescriptorContext context)
    {
        Initialize(context);
        return mTypeConverter.GetStandardValuesSupported(context);
    }

    public override bool IsValid(ITypeDescriptorContext context, object value)
    {
        Initialize(context);
        return mTypeConverter.IsValid(context, value);
    }

    public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
    {
        Initialize(context);
        return mTypeConverter.CanConvertFrom(context, sourceType);
    }

    public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
    {
        Initialize(context);
        return mTypeConverter.ConvertFrom(context, culture, value);
    }

    public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
    {
        Initialize(context);
        return mTypeConverter.CanConvertTo(context, destinationType);
    }

    #endregion
}

public class AggregateTestClass : TestClass
{
    private readonly TestClass[] mObjects;

    public AggregateTestClass(TestClass[] objects)
    {
        mObjects = objects;
    }

    protected override object[] GetIndividuals()
    {
        return mObjects;
    }
}

public class TestClass : ISupportAggregate
{
    [TypeConverter(typeof(AggregateTypeConverter))]
    public int IntProperty { get; set; }

    [TypeConverter(typeof(AggregateTypeConverter))]
    public string StringProperty { get; set; }

    [BrowsableAttribute(false)]
    public object[] Individuals
    {
        get { return GetIndividuals(); }
    }

    virtual protected object[] GetIndividuals()
    {
        return new[] { this };
    }
}

public class TestSupportAggregate : ISupportAggregate, TestClass
{
    private readonly TestClass[] mItems;

    public TestSupportAggregate(TestClass[] items)
    {
        mItems = items;
    }

    public object[] Individuals
    {
        get { return mItems; }
    }
}


To use in code:

control.SelectedObject = new TestSupportAggregate(new[]
                                                 {
                                                     new TestClass { IntProperty = 5150 },
                                                     new TestClass { IntProperty = 1984 }
                                                 });