【问题标题】:How to return a property as value from TypeConverter in a PropertyGrid如何从 PropertyGrid 中的 TypeConverter 将属性作为值返回
【发布时间】:2014-08-21 08:53:56
【问题描述】:

我为 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 属性与上面Message 类中的SenderId 相同。为了便于阅读,我的示例被高度简化):

直到我在列表中选择一个项目,我在 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 并且我已经尝试在 UserConverter 中覆盖 ConvertTo 但是当我在 ComboBox 中选择一个项目时它似乎根本没有命中该方法。

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

【问题讨论】:

    标签: c# winforms propertygrid typeconverter


    【解决方案1】:

    我尝试在 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);
    }
    

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

    【讨论】:

    • 拥有 CanConvertFrom string = true、CanConvertTo int = true 并实现 ConvertFrom string 返回 10,将导致组合框选择起作用,但列表中的项目不再显示用户的全名,而是显示转换后的值10 作为字符串。这将导致另一个问题“我如何控制 ComboBox 的 DisplayMember”。
    • @JaniHyytiäinen,尝试更改 CanConvertTo 为字符串类型返回 true 而不是 int。
    • 我试过了。还尝试覆盖 ConvertFrom 字符串、ConvertFrom int、ConvertTo string、ConvertTo int 始终具有相同的结果。如果我在组合框中显示了一个字符串并选择了一个项目,则会在 mscorlib 中引发上述异常,而不是 TypeConverter 尝试使用 ConvertFrom 字符串或 ConvertTo int。
    • @JaniHyytiäinen,我终于有时间深入研究这个问题,因此我在答案中添加了一些代码示例。
    【解决方案2】:

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

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

    干杯。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-06-06
      • 2020-03-26
      • 1970-01-01
      • 2017-12-03
      • 1970-01-01
      • 2014-11-07
      • 2011-08-31
      相关资源
      最近更新 更多