在Generic类上为PropertyGrid使用ExpandableObjectConverter

时间:2014-03-07 01:43:21

标签: c# generics user-interface propertygrid

我希望有一个属性网格,当它有两个SteppedValue对象时会看起来像这样:

enter image description here

但我也希望SteppedValue对象是通用的,例如:

var fastEma = new SteppedValue<int>(); 
var slowEma = new SteppedValue<float>();

我主要通过实现ExpandableObjectConverter来实现...但我遇到的唯一问题是当我在ConvertFrom中实例化要返回的对象时......我无法想象如何获取T的System.Type(来自此时的通用实例。

问题代码

public override object ConvertFrom(ITypeDescriptorContext iTypeDescriptorContext, CultureInfo cultureInfo, object value)
{
    if (value is string)
    {
        var classType = typeof(SteppedValue<>);
        Type[] typeArgs = { typeof(int) }; // <== Need to determine this 
                                           //  needs to be T of the generic
                                           //  instance (not "typeof(int)")
        var instanceType = classType.MakeGenericType(typeArgs);
        object o = Activator.CreateInstance(instanceType, value);
        return o;
    }
    return base.ConvertFrom(iTypeDescriptorContext, cultureInfo, value);
}

完整代码

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.Linq;

namespace Test
{
    [TypeConverter(typeof(SteppedValueConverter))]
    public class SteppedValue<T>
    {
        public T Begin { get; set; }
        public T End { get; set; }
        public T Step { get; set; }

        public SteppedValue(T begin, T end, T step) 
        { 
            Begin = begin;
            End = end;
            Step = step;
        }

        public SteppedValue(string str)
        {
            var parts = ParseParts(str);
            Begin = parts[0];
            End = parts[1];
            Step = parts[2];
        }

        public static T ToT(object obj)
        {
            return (T)Convert.ChangeType(obj, typeof(T));
        }

        public static SteppedValue<T> Parse(string str)
        {
            return new SteppedValue<T>(str);
        }

        public List<T> ParseParts(string str)
        {
            while (str.Contains("  ")) str = str.Replace("  ", " ");
            var arr = str.Trim().Split(' ');
            if (arr.Length == 0)
                return new List<T> { default(T), default(T), default(T) };
            else if (arr.Length == 1)
                return new List<T> { ToT(arr[0]), ToT(arr[0]), default(T) };
            else if (arr.Length == 3 && arr[1].ToLower() == "to")
                return new List<T> { ToT(arr[0]), ToT(arr[2]), default(T) };
            else if (arr.Length == 5 && arr[1].ToLower() == "to" && arr[3].ToLower() == "step")
                return new List<T> { ToT(arr[0]), ToT(arr[2]), ToT(arr[4]) };
            throw new Exception("Unable to parse'" + str + "'");
        }

        public override string ToString()
        {
            if (Begin.Equals(End))
                return Begin.ToString();
            else if (Step == null || Step.ToString() == "")
                return Begin + " to " + End;
            else
                return Begin + " to " + End + " step " + Step;
        }
    }

    public class SteppedValueConverter : ExpandableObjectConverter
    {
        public override bool CanConvertFrom(ITypeDescriptorContext iTypeDescriptorContext, Type type)
        {
            if (type == typeof(string))
                return true;
            return base.CanConvertFrom(iTypeDescriptorContext, type);
        }

        public override object ConvertFrom(ITypeDescriptorContext iTypeDescriptorContext, CultureInfo cultureInfo, object value)
        {
            if (value is string)
            {
                var classType = typeof(SteppedValue<>);
                Type[] typeArgs = { typeof(int) }; // <== Need to determine this
                var instanceType = classType.MakeGenericType(typeArgs);
                object o = Activator.CreateInstance(instanceType, value);

                return o;
            }
            return base.ConvertFrom(iTypeDescriptorContext, cultureInfo, value);
        }

        public override object ConvertTo(ITypeDescriptorContext iTypeDescriptorContext, CultureInfo cultureInfo, object value, Type destinationType)
        {

            if (destinationType == typeof(string) && value.GetType().GetGenericTypeDefinition() == typeof(SteppedValue<>))
                return value.ToString();

            return base.ConvertTo(iTypeDescriptorContext, cultureInfo, value, destinationType);
        }
    }
}

1 个答案:

答案 0 :(得分:2)

你可以从上下文中获取类型,就像这样(在SteppedValue的情况下,它是第一个泛型类型):

Type steppedValueType = iTypeDescriptorContext.PropertyDescriptor.PropertyType.GetGenericArguments()[0];