Hi,
I am having a bit of difficulty with TypeConverters and Generic Lists and I was hoping that I could get a bit of advice.
I have a type converter that is used to create the constructor code for my component. (It is an XNA a Game Component, but I don't think that that has anything to do with the problem because it appears on a Winform) inside another component.
For instance I have the following:
public class A{ private string s; public string SProp { get{ s = value;} set {return s;} }
public A() { s = ""; } public A(string inS) { s = inS; } }
By itself, when class A is an object on a form (or in my XNA Component) the TypeConverter code works fine, the property grid on the desinger is fine, if I debug the ConvertTo on the type converter I see that the variable s is all set up okay on the object.
However if on my form, I have a List<A>, the type converter works to an extent, as in it will add code to the perform the List.Add(new A("")), however the constructor with the parameter is not being called, instead the parameterless constructor is being called.
Obviously, if the input to ConvertTo on the type converter is not set up, then my type converter won't work or display the values the user has put in. This only occurs when my object is in a Generic List.
Can anyone suggest anything to look at? | | Kinlan Sunday, September 24, 2006 11:16 AM | do you have an override to ConvertTo InstanceDescriptor? can we see some code for your TypeConverter?
| | joeycalisay Monday, September 25, 2006 10:20 AM | using System;
using System.Collections.Generic;
using System.Text;
using System.ComponentModel;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using XNAParalax.XNATypeConverters;
using XNAParalax.XNAScroller;
using System.Globalization;
using System.ComponentModel.Design.Serialization;
using System.Reflection;
namespace XNAParalax
{
/// <summary>
/// The Paralax Background Component.
/// </summary>
[Serializable]
[TypeConverter(typeof(ParalaxBackground.LayerConverter))]
public class ParalaxBackground
{
private string m_textureFile;
private Vector2 m_Offset;
private bool mTileX;
private bool mTileY;
private IScroller mXScrollerComponent;
private IScroller mYScrollerComponent;
/// <summary>
/// The offset of the texture.
/// </summary>
[TypeConverter(typeof(Vector2Converter))]
public Vector2 Offset
{
get { return m_Offset; }
set { m_Offset = value; }
}
/// <summary>
/// The scroller that is used to modify the X Position.
/// </summary>
public IScroller XScrollerComponent
{
get { return mXScrollerComponent; }
set { mXScrollerComponent = value; }
}
/// <summary>
/// The scroller that is used to modify the Y-Position of the layer.
/// </summary>
public IScroller YScrollerComponent
{
get { return mYScrollerComponent; }
set { mYScrollerComponent = value; }
}
/// <summary>
/// Should the Background Image be tiled in the Y-Axis
/// </summary>
[DefaultValue("false")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public bool TileY
{
get { return mTileY; }
set {
if (value)
{
mTileX = false;
}
mTileY = value;
}
}
/// <summary>
/// Should the image be tiled in the X-Axis
/// </summary>
[DefaultValue("true")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public bool TileX
{
get { return mTileX; }
set {
if (value)
{
mTileY = false;
}
mTileX = value;
}
}
/// <summary>
/// The filename of the texture to load as the background.
/// </summary>
[DefaultValue("Test")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public string FileName
{
get { return m_textureFile; }
set { m_textureFile = value; }
}
[NonSerialized]
private Texture2D m_texture;
/// <summary>
/// The Instance of the Background Texture.
/// </summary>
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public Texture2D BackGroundTexture
{
get { return m_texture; }
set { m_texture = value; }
}
public ParalaxBackground()
{
m_textureFile = "";
mTileX = false;
mTileY = false;
}
public ParalaxBackground(string filename)
{
m_textureFile = filename;
mTileX = true;
mTileY = false;
mXScrollerComponent = new NullScroller();
mYScrollerComponent = new NullScroller();
m_Offset = new Vector2(0, 0);
}
/// <summary>
/// The Layer Converter is used to aid in design time serialization and thus allow easy access to editing of the layers.
/// </summary>
public class LayerConverter : ExpandableObjectConverter
{
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{
return sourceType == typeof(string);
}
public override bool GetCreateInstanceSupported(ITypeDescriptorContext context)
{
return true;
}
public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
{
string sValue = value as string;
object retVal = null;
if (sValue != null)
{
sValue = sValue.Trim();
if (sValue.Length != 0)
{
// And finally create the object
retVal = new ParalaxBackground(sValue);
}
}
else
retVal = base.ConvertFrom(context, culture, value);
return retVal;
}
public override object CreateInstance(ITypeDescriptorContext context, System.Collections.IDictionary propertyValues)
{
return new ParalaxBackground(propertyValues["FileName"] as string);
}
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
{
if (destinationType == typeof(InstanceDescriptor))
{
return true;
}
// Always call the base to see if it can perform the conversion.
return base.CanConvertTo(context, destinationType);
}
// This code performs the actual conversion from a Triangle to an InstanceDescriptor.
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
{
ParalaxBackground v2 = (ParalaxBackground)value;
if (destinationType == typeof(InstanceDescriptor))
{
System.Type[] argTypes = new System.Type[1];
argTypes[0] = typeof(string);
// Lookup the appropriate Doofer constructor
ConstructorInfo constructor = typeof(ParalaxBackground).GetConstructor(argTypes);
object[] arguments = new object[1];
arguments[0] = v2.FileName;
return new InstanceDescriptor(constructor, arguments);
}
else if (destinationType == typeof(string))
{
return v2.FileName;
}
// Always call base, even if you can't convert.
return base.ConvertTo(context, culture, value, destinationType);
}
public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes)
{
PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(value, attributes);
// Return a sorted list of properties
return properties;
}
public override bool GetPropertiesSupported(ITypeDescriptorContext context)
{
return true;
}
}
}
}
| | Kinlan Monday, September 25, 2006 6:07 PM | the conversion to InstanceDescriptor looks great, can you add the line:
return base.CanConvertFrom (context, sourceType);
in your CanConvertFrom override if it's not string (i'm guessing it's your custom type to the InstanceDescriptor is the issue). your CanCovertTo override by the way does not handle conversion to string but you have conversion code.
lastly, can you try debugging your ConvertTo override to see if it is being executed. btw: you should be using DesignerSerializationVisibility.Visible for primitive type properties not Content.
| | joeycalisay Tuesday, September 26, 2006 1:34 AM | Hi,
Thanks for the reposnse, I will try them out tonight when I get back from work. I know that the ConvertTo is being called, I have debugged it in the past. When the type is in a Generic List the "value" parameter object is filled with the properties initialised by the default constructor, and that is where my problem lies. If I don't use a Generic List, the other constructor (i.e the one with the string) is called.
Kind Regards,
Paul Kinlan | | Kinlan Tuesday, September 26, 2006 7:08 AM | Still no luck so far.
When the ParalaxBackGround is in a Generic List, it produces the following code:
this .paralaxBackground.Backgrounds.Add(new XNAParalax.ParalaxBackground(""));
When it should produce:
this .paralaxBackground.Backgrounds.Add(new XNAParalax.ParalaxBackground("c:\\test.png"));
if it isn't part of a Generic List it uses the correct constructor logic.
Paul Kinlan
| | Kinlan Tuesday, September 26, 2006 6:27 PM | Should I have a type converter for the GenericList? So that I can tell it to call the correct constructor? | | Kinlan Tuesday, September 26, 2006 6:27 PM | | Kinlan wrote: | | Should I have a type converter for the GenericList? So that I can tell it to call the correct constructor? |
|
i haven't tried vs2005 yet, i'll try it tonite after work, if you can somehow provide your source code, please do... | | joeycalisay Wednesday, September 27, 2006 1:37 AM | All the source code is on CodePlex.
http://www.codeplex.com/SourceControl/ListDownloadableCommits.aspx?ProjectName=xnaparalax
It needs VC Express and XNA Game Studio, because it is XNA project.
Paul. | | Kinlan Wednesday, September 27, 2006 7:17 AM | I think there might be a problem with the XNA Game studio, because I can create a WinForm User Control. based along the same lines and everything seems to work fine. What I will do is strip my control down to the bare essentials and see if it works.
public class A
{
private string m_textureFile;
/// <summary>
/// The filename of the texture to load as the background.
/// </summary>
[ DefaultValue("Test")]
[ DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public string FileName
{
get { return m_textureFile; }
set { m_textureFile = value; }
}
public A()
{
m_textureFile = "a";
}
public A(string fn)
{
m_textureFile = fn;
}
public class LayerConverter : ExpandableObjectConverter
{
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{
return sourceType == typeof(string);
}
public override bool GetCreateInstanceSupported(ITypeDescriptorContext context)
{
return true;
}
public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
{
string sValue = value as string;
object retVal = null;
if (sValue != null)
{
sValue = sValue.Trim();
if (sValue.Length != 0)
{
// And finally create the object
retVal = new A(sValue);
}
}
else
retVal = base.ConvertFrom(context, culture, value);
return retVal;
}
public override object CreateInstance(ITypeDescriptorContext context, System.Collections.IDictionary propertyValues)
{
return new A(propertyValues["FileName"] as string);
}
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
{
if (destinationType == typeof(InstanceDescriptor))
{
return true;
}
// Always call the base to see if it can perform the conversion.
return base.CanConvertTo(context, destinationType);
}
// This code performs the actual conversion from a Triangle to an InstanceDescriptor.
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
{
A v2 = (A)value;
if (destinationType == typeof(InstanceDescriptor))
{
System. Type[] argTypes = new System.Type[1];
argTypes[0] = typeof(string);
// Lookup the appropriate Doofer constructor
ConstructorInfo constructor = typeof(A).GetConstructor(argTypes);
object[] arguments = new object[1];
arguments[0] = v2.FileName;
return new InstanceDescriptor(constructor, arguments);
}
else if (destinationType == typeof(string))
{
return v2.FileName;
}
// Always call base, even if you can't convert.
return base.ConvertTo(context, culture, value, destinationType);
}
public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes)
{
PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(value, attributes);
// Return a sorted list of properties
return properties;
}
public override bool GetPropertiesSupported(ITypeDescriptorContext context)
{
return true;
}
}
}
With the following user control.
public partial class UserControl1 : UserControl
{
private List<A> myVar;
[ DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public List<A> MyProperty
{
get { return myVar; }
}
public UserControl1()
{
InitializeComponent();
myVar= new List<A>();
}
}
This works!!! Grr :) | | Kinlan Wednesday, September 27, 2006 7:56 AM | Got It working...
I thought I better strip it down to the bare minimum.... and doing so I got it working how I wanted.
internal class LayerConverter : TypeConverter
{
public override bool CanConvertTo(ITypeDescriptorContext context, Type destType)
{
if (destType == typeof(InstanceDescriptor))
return true;
return base.CanConvertTo(context, destType);
}
public override object ConvertTo(ITypeDescriptorContext context,
System.Globalization. CultureInfo culture, object value, Type destType)
{
if (destType == typeof(InstanceDescriptor))
{
System.Reflection. ConstructorInfo ci =
typeof(ParalaxBackground).GetConstructor(
System. Type.EmptyTypes);
return new InstanceDescriptor(ci, null, false);
}
return base.ConvertTo(context, culture, value, destType);
}
}
The above code is good engough for me, and it works and It is far simpler... There was no need for me to do all this string conversion from the root of the property.
My moto should have been KISS :)
Thanks for the help, I really appreciate the time you spent on it.
Paul Kinlan | | Kinlan Wednesday, September 27, 2006 8:24 PM |
|