I have a custom control which has a property of type Collection<System.Drawing.Point>
. When I use CollectionEditor
to edit this property, the CollectionEditor
window shows "Object does not match target type."
for the "X"
and "Y"
properties. But if I use System.Drawing.PointF
instead, there's no failure.
Can anyone please explain why this difference occurs?
The difference between Point and PointF lies indeed with PointConverter. Why this causes a problem is quite a long story, but at the end of the day it boils down to the following:
The System.ComponentModel.ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor)
implementation in System.ComponentModel.Design.CollectionEditor
.CollectionEditorCollectionForm.SelectionWrapper
simply returns this
.
According to the MSDN page of the aforementioned method of the ICustomTypeDescriptor
interface, an implementation should
Return(s) an object that contains the property described by the specified property descriptor.
If I understand it correctly, in this case the implementation contradicts the documentation.
This is based on some research of my own, so don't take it for granted. I posted a report of this issue on Microsoft Connect, so hopefully we'll know for sure in a few days. I'll report back when an answer is received.
I'm no .NET / C# expert, but the issue appears to be somewhere within the PointConverter
class, which is used as the TypeConverterAttribute
for the System.Drawing.Point
class. The Collection Editor must be using something within the PointConverter
class that fails.
I suspect PointConverter
because the PointF
class has no TypeConverterAttribute
, and it works fine.
In the following example, which I cobbled together using some code from MSDN, your problem is seen when using the Point
class in a collection, but not with the MyPoint
class which is using a custom TypeConverter
.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;
using System.Globalization;
namespace WindowsControlLibrary1
{
public class MyTypeConverter : TypeConverter
{
// Overrides the CanConvertFrom method of TypeConverter.
// The ITypeDescriptorContext interface provides the context for the
// conversion. Typically, this interface is used at design time to
// provide information about the design-time container.
public override bool CanConvertFrom(ITypeDescriptorContext context,
Type sourceType)
{
if (sourceType == typeof(string))
{
return true;
}
return base.CanConvertFrom(context, sourceType);
}
// Overrides the ConvertFrom method of TypeConverter.
public override object ConvertFrom(ITypeDescriptorContext context,
CultureInfo culture, object value)
{
if (value is string)
{
string[] v = ((string)value).Split(new char[] { ',' });
return new MyPoint(int.Parse(v[0]), int.Parse(v[1]));
}
return base.ConvertFrom(context, culture, value);
}
// Overrides the ConvertTo method of TypeConverter.
public override object ConvertTo(ITypeDescriptorContext context,
CultureInfo culture, object value, Type destinationType)
{
if (destinationType == typeof(string))
{
return ((MyPoint)value).X + "," + ((MyPoint)value).Y;
}
return base.ConvertTo(context, culture, value, destinationType);
}
}
[SerializableAttribute]
[TypeConverterAttribute(typeof(MyTypeConverter))]
public struct MyPoint
{
private int x;
private int y;
public MyPoint(int _x, int _y)
{
x = _x;
y = _y;
}
public int X
{
get { return x; }
set { x = value; }
}
public int Y
{
get { return y; }
set { y = value; }
}
}
public partial class UserControl1 : UserControl
{
private List<System.Drawing.Point> points;
private List<System.Drawing.PointF> pointfs;
private List<MyPoint> mypoints;
public List<System.Drawing.Point> PointList
{
get{ return points;}
set{ points = value;}
}
public List<System.Drawing.PointF> PointFList
{
get {return pointfs;}
set{pointfs = value;}
}
public List<MyPoint> MyPointList
{
get { return mypoints; }
set { mypoints = value; }
}
public UserControl1()
{
InitializeComponent();
}
}
}
My solution is before you use collectioneditor to edit list(of point), use TypeDescriptor.AddAttributes(GetType(Drawing.Point), New TypeConverterAttribute())
to set typeconverter of Point to nothing, and after that use TypeDescriptor.AddAttributes(GetType(Drawing.Point), New TypeConverterAttribute(GetType(PointConverter)))
to set typeconverter to default.