3

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

网格的默认行为是在所有选定对象具有相同值时显示一个值,但在它们不同时简单地空白该字段。没有办法知道它们有何不同。

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

TestLong|[50 - 60]
 TestInt|10
TestEnum|[Multiple] 

即,如果值不同,则表明它们有何不同,但如果它们都相同,则表明该值?

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
    },
}
    ...
4

2 回答 2

2

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

虽然你可以做的,但它不完全是答案,是在网格处于多选模式时提出一个自定义UITypeEditor ,如下所示:

    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;
        }
    }
于 2013-03-22T10:14:14.233 回答
1

我已经解决了这个问题,虽然不是我最初的打算。

问题的根本原因是当有多个选定对象时没有应用 TypeConverters。为了解决这个问题,我引入了一个聚合类,它聚合了各个对象,但呈现为单个对象。这样,可以调用 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 }
                                                 });
于 2013-04-03T02:54:04.773 回答