属性 'System.String' 类型的网格对象无法转换为 'x' 类型

property grid Object of type 'System.String' cannot be converted to type 'x'

我正在使用 属性 网格来显示对象属性。必须根据条件显示动态属性。我需要将列表项显示为下拉列表。 所以我写了一些必要的 classes。 (请考虑此列表也是动态的)。

我有一个自定义的 属性 网格,如您所见:

    public class myPropertyGrid : PropertyGrid
        {
        private System.ComponentModel.Container components = null;

        public myPropertyGrid()
        {           
            InitializeComponent();          
        }

        protected override void Dispose( bool disposing )
        {
            if( disposing )
            {
                if(components != null)
                {
                    components.Dispose();
                }
            }
            base.Dispose( disposing );
        }

        #region Codice generato da Progettazione componenti
        /// <summary> 
        /// Metodo necessario per il supporto della finestra di progettazione. Non modificare 
        /// il contenuto del metodo con l'editor di codice.
        /// </summary>
        private void InitializeComponent()
        {
            // 
            // UserControl1
            // 
            this.Name = "myPropertyGrid";           

        }
        #endregion      

        protected override PropertyTab CreatePropertyTab(Type tabType)
        {           
            myTab t = new myTab();
            return t;
        }           
    }   

    public class myTab : PropertyTab
    {
        public myTab()
        {            
        }

        // get the properties of the selected component
        public override System.ComponentModel.PropertyDescriptorCollection GetProperties(object component, System.Attribute[] attributes)
        {
            PropertyDescriptorCollection properties;
            if(attributes!=null)
                properties=TypeDescriptor.GetProperties(component,attributes);              
            else
                properties=TypeDescriptor.GetProperties(component);    

            //Componet must implement the ICUSTOMCLASS interface.
            ICustomClass bclass=(ICustomClass)component;    

            //The new array of properties, based on the PublicProperties properties of "model"
            PropertyDescriptor[] arrProp = new PropertyDescriptor[bclass.PublicProperties.Count];                                       

            for (int i=0;i<bclass.PublicProperties.Count;i++)
            {
                //Find the properties in the array of the propertis which neme is in the PubliCProperties
                PropertyDescriptor prop=properties.Find(bclass.PublicProperties[i].Name,true);
                //Build a new properties
                arrProp[i] = TypeDescriptor.CreateProperty(prop.ComponentType, prop, new CategoryAttribute("جزئیات"));
            }
            return new PropertyDescriptorCollection(arrProp);
        }

        public override System.ComponentModel.PropertyDescriptorCollection GetProperties(object component)
        {                     
            return this.GetProperties(component,null);
        }

        // PropertyTab Name
        public override string TabName
        {
            get
            {
                return "Properties";
            }
        }

        //Image of the property tab (return a blank 16x16 Bitmap)
        public override System.Drawing.Bitmap Bitmap
        {
            get
            {               
                return new Bitmap(16,16);;
            }
        }
    }

我有一个名为 PropertyList 的 class,这个 class 用于向 属性 网格添加属性并从 属性 网格。

    public class PropertyList : NameObjectCollectionBase
    {
        public void Add(Object value)
        {
            //The key for the object is taken from the object to insert
            this.BaseAdd(((CustomProperty)value).Name, value);
        }

        public void Remove(String key)
        {
            this.BaseRemove(key);
        }

        public void Remove(int index)
        {
            this.BaseRemoveAt(index);
        }

        public void Clear()
        {
            this.BaseClear();
        }

        public CustomProperty this[String key]
        {
            get
            {
                return (CustomProperty)(this.BaseGet(key));
            }
            set
            {
                this.BaseSet(key, value);
            }
        }

        public CustomProperty this[int indice]
        {
            get
            {
                return (CustomProperty)(this.BaseGet(indice));
            }
            set
            {
                this.BaseSet(indice, value);
            }
        }
    }

还有一个 class 用于自定义 属性

public class CustomProperty
    {
        private string sName = string.Empty;
        private bool bReadOnly = false;
        private bool bVisible = true;
        private object objValue = null;
        private object tag = null;
        private string displayName = string.Empty;
        public CustomProperty(object tag, string sName, object value, Type type, bool bReadOnly, bool bVisible)
        {
            this.tag = tag;
            this.sName = sName;
            this.objValue = value;
            this.type = type;
            this.bReadOnly = bReadOnly;
            this.bVisible = bVisible;
        }

        public CustomProperty(object tag, string displayName, string sName, object value, Type type, bool bReadOnly, bool bVisible)
            :this(tag,sName,value,type,bReadOnly,bVisible)
        {
            this.displayName = displayName;
        }

        private Type type;

        public Type Type
        {
            get { return type; }
        }

        public bool ReadOnly
        {
            get
            {
                return bReadOnly;
            }
        }

        public string Name
        {
            get
            {
                return sName;
            }
            set
            {
                sName = value;
            }
        }

        public bool Visible
        {
            get
            {
                return bVisible;
            }
        }

        public object Value
        {
            get
            {
                return objValue;
            }
            set
            {
                objValue = value;

            }
        }
        public object Tag
        {
            get
            {
                return tag;
            }
            set
            {
                tag = value;
            }
        }

        public string DisplayName
        {
            get
            {
                return displayName;
            }
            set
            {
                displayName = value;
            }
        }
}

我想添加从动态列表创建的组合框,为此我编写了以下代码:

[TypeConverter(typeof(VersionConvertor))]
[Editor(typeof(VersionTypeEditor), typeof(UITypeEditor))]
public class VersionClass
{
    public VersionClass()
    {

    }
}

public class VersionConvertor : TypeConverter
{

    public override bool GetStandardValuesSupported(ITypeDescriptorContext context)
    {
        return true;
    }

    public override bool GetStandardValuesExclusive(ITypeDescriptorContext context)
    {
        return true;
    }

}
public class VersionTypeEditor : UITypeEditor
{

    private IWindowsFormsEditorService _editorService;
    private ListBox lb;
    public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context)
    {
        // drop down mode (we'll host a listbox in the drop down)
        return UITypeEditorEditStyle.DropDown;
    }

    public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value)
    {
        _editorService = (IWindowsFormsEditorService)provider.GetService(typeof(IWindowsFormsEditorService));

        // use a list box
        lb = new ListBox();
        lb.SelectionMode = SelectionMode.One;
        lb.SelectedValueChanged += OnListBoxSelectedValueChanged;
        lb.DisplayMember = "Name";


        List<string> listItem = new List<string>();
        listItem.Add("a"); // assume that these items are added dynamically
        listItem.Add("b");
        foreach (string item in listItem)
        {
            lb.Items.Add(item);
        }

        lb.SelectedItem = listItem[0];
        lb.ValueMember = listItem[0];

        _editorService.DropDownControl(lb);
        if (lb.SelectedItem == null) // no selection, return the passed-in value as is
            return value;

        return lb.SelectedItem;
    }
    private void OnListBoxSelectedValueChanged(object sender, EventArgs e)
    {
        lb.SelectedItem = ((ListBox)(sender)).SelectedItem;
        _editorService.CloseDropDown();
    }
}

当我点击字母表下拉列表并选择一个项目时,出现错误:

如何修复此错误?

这是一个老问题,但我有一个答案。

ComboBox 中的选项是字符串;您明确将它们添加到 VersionTypeEditor.EditValue() 中的 lb.Items。从我收集到的问题中,Alphabet 属性 必须设置为 VersionClass 的一个实例。这就是错误的根源:表单需要一个 VersionClass,而 ComboBox 正在向它传递一个字符串。

您需要将字符串转换为 VersionClass。您已经设置了 VersionConverter class 并使用 TypeConverterAttribute 将其应用于 VersionClass。现在您需要通过覆盖 TypeConverter.CanConvertFrom()TypeConverter.ConvertFrom() 方法来添加转换逻辑。这是一个示例(未经测试):

public class VersionConverter : TypeConverter
{
    public override bool GetStandardValuesSupported(ITypeDescriptorContext context)
    {
        return true;
    }

    public override bool GetStandardValuesExclusive(ITypeDescriptorContext context)
    {
        return true;
    }

    public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
    {
        if (type == typeof(string))
        {
            return true;
        }
        return base.CanConvertFrom(context, sourceType);
    }

    public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
    {
        if (value is string)
        {
            // conversion logic goes here
            return convertedVersionClass;
        }
        return base.ConvertFrom(context, culture, value);
    }
}

希望这能回答您的基本问题。但是,我还注意到您在 VersionTypeEditor.EditValue() 中使用 lb.Items.Add() 明确添加了选项。通过覆盖 VersionConverter 中的更多方法,您可以更轻松地动态填充 ComboBox。具体来说,您想覆盖 TypeConverter.ConvertTo() 以获取 VersionClass 和 return 将出现在 ComboBox 中的字符串表示形式,然后将 TypeConverter.GetStandardValues() 覆盖为 return a TypeConverter.StandardValuesCollection 包含您希望在 ComboBox 中显示的对象。

我在这里陈述的很多内容,包括 TypeConverter.ConvertTo()TypeConverter.GetStandardValues(),在 this MDSN page 上有更详细的讨论。