如何在PropertyGrid中将TypeConverter中的属性作为值返回

时间:2014-08-21 08:53:56

标签: c# winforms propertygrid typeconverter

我为PropertyGrid分配了以下类型的类:

public class Message{
    [TypeConverter(typeof(UserConverter))]
    public int SenderId { get; set; }
}

转换器

public class UserConverter: TypeConverter
{
    public override bool GetStandardValuesSupported(ITypeDescriptorContext context)
    {
        return true;
    }
    public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context)
    {
        return new StandardValuesCollection(_users);
    }
}

当然是用户

public class User
{
    public int Id { get; set; }
    public string FullName { get; set; }
    public override string ToString()
    {
        return User.fullName;
    }
}

到目前为止一切正常(请注意:下图中的Sender属性与上面SenderId类中的Message相同。此处的示例高度简化,以便于阅读) :

ComboBox showing full name

在我选择列表中的项目之前,我在mscorlib中得到一个例外:

System.ArgumentException occurred
  _HResult=-2147024809
  _message=Object of type 'System.String' cannot be converted to type 'System.Int32'.
  HResult=-2147024809
  IsTransient=false
  Message=Object of type 'System.String' cannot be converted to type 'System.Int32'.
  Source=mscorlib
  StackTrace:
       at System.ComponentModel.ReflectPropertyDescriptor.SetValue(Object component, Object value)
  InnerException: 

它似乎甚至没有通过我的TypeConverter。我意识到属性类型是int并且我已经尝试覆盖ConvertTo中的UserConverter,但是当我在ComboBox中选择一个项目时它似乎根本没有触及该方法。

如何控制这种组合框的返回值?在此特定情况下,我想返回Message.SenderId而不是object.ToString()覆盖。

2 个答案:

答案 0 :(得分:0)

  

我已尝试在UserConverter中覆盖ConvertTo,但它似乎根本没有触及该方法

除了ConvertTo之外,您还应该覆盖CanConvertFromConvertFrom方法,以指定转换器实际上可以在所需类型之间进行转换。

无需覆盖CanConvertTo,因为根据MSDN:

  

没有必要覆盖此方法以转换为字符串类型。Source

以下是基于您提供的模型的这些方法的简单实现:

public override bool CanConvertFrom(
    ITypeDescriptorContext context, Type sourceType)
{
    return sourceType == typeof(string);
}

public override object ConvertFrom(
    ITypeDescriptorContext context, CultureInfo culture, object value)
{
    return users.OfType<User>().First(u => u.ToString() == value.ToString()).Id;
}

public override object ConvertTo(
    ITypeDescriptorContext context, 
    CultureInfo culture, 
    object value, 
    Type destinationType)
{
    if (destinationType == typeof(string))
    {
        if (value is int)
        {
            return users.OfType<User>().First(u => u.Id == (int)value).ToString();
        }
        return value.ToString();
    }
    return base.ConvertTo(context, culture, value, destinationType);
}

注意:这是一个非常原始的实现,没有检查无效值,异常处理等。所以我不鼓励在生产中使用它。

答案 1 :(得分:0)

我想你现在已经找到了解决方案,但无论如何。 问题在于它正在尝试为用户找到int转换器... 如果将其添加到转换器的构造函数中(可能在静态构造函数中):

Attribute[] attr = new Attribute[1];
        TypeConverterAttribute vConv = new TypeConverterAttribute(typeof(UserConverter));
        attr[0] = vConv;
        TypeDescriptor.AddAttributes(typeof(int), attr);

它会起作用,但它也会在你的课程中显示所有内容作为用户的下拉框;-)这根本不是你想要的。

如果我们可以像这样简单地为User类提供一个强制转换,那将会很酷:

public static implicit operator int(User user) 
{
    return user.Id;  // implicit conversion
}

但不幸的是,这不起作用。它将继续尝试使用Int32并抛出异常。

实现此功能的唯一方法是为Id编写一个包装器:

public class SenderId
{
    public int Id { get; set; }
    public string DisplayMember {get; set;}
    // this is how it will display the items:
    public override string ToString()
        {
            return $"{DisplayMember} [{Id}]";
        }
}

您还应该实现Equals和GetHashCode。然后像这样使用它:

public class Message
{
    [TypeConverter(typeof(UserConverter))]
    public SenderId SenderId { get; set; }
}

转换器:

public class UserConverter: TypeConverter
{
    public override bool GetStandardValuesSupported(ITypeDescriptorContext context)
    {
        return true;
    }
    public override StandardValuesCollection GetStandardValues(
        ITypeDescriptorContext context)
    {
        return new StandardValuesCollection(_senderIds);
    }
}

干杯。