I want to pass an int list (List) as a declarative property to a web user control like this:
<UC:MyControl runat="server" ModuleIds="1,2,3" />
I created a TypeConverter to do this:
public class IntListConverter : System.ComponentModel.TypeConverter
{
public override bool CanConvertFrom(
System.ComponentModel.ITypeDescriptorContext context,
Type sourceType)
{
if (sourceType == typeof(string)) return true;
return base.CanConvertFrom(context, sourceType);
}
public override object ConvertFrom(
System.ComponentModel.ITypeDescriptorContext context,
System.Globalization.CultureInfo culture, object value)
{
if (value is string)
{
string[] v = ((string)value).Split(
new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
List<int> list = new List<int>();
foreach (string s in vals)
{
list.Add(Convert.ToInt32(s));
}
return list
}
return base.ConvertFrom(context, culture, value);
}
public override bool CanConvertTo(ITypeDescriptorContext context,
Type destinationType)
{
if (destinationType == typeof(InstanceDescriptor)) return true;
return base.CanConvertTo(context, destinationType);
}
public override object ConvertTo(ITypeDescriptorContext context,
System.Globalization.CultureInfo culture, object value, Type destinationType)
{
if (destinationType == typeof(InstanceDescriptor) && value is List<int>)
{
List<int> list = (List<int>)value;
ConstructorInfo construcor = typeof(List<int>).GetConstructor(new Type[] { typeof(IEnumerable<int>) });
InstanceDescriptor id = new InstanceDescriptor(construcor, new object[] { list.ToArray() });
return id;
}
return base.ConvertTo(context, culture, value, destinationType);
}
}
And then added the attribute to my property:
[TypeConverter(typeof(IntListConverter))]
public List<int> ModuleIds
{
get { ... }; set { ... };
}
But I get this error at runtime:
Unable to generate code for a value of type 'System.Collections.Generic.List'1[[System.Int32, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]'. This error occurred while trying to generate the property value for ModuleIds.
My question is similar to one found here, but the solution does not solve my problem:
Update: I found a page which solved the first problem. I updated the code above to show my fixes. The added code is the CanConvertTo and ConvertTo methods. Now I get a different error.:
Object reference not set to an instance of an object.
This error seems to be indirectly caused by something in the ConvertTo method.
Answers:
Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.
Method 1
After hooking a debugger into Cassini, I see that the null ref is actually coming from System.Web.Compilation.CodeDomUtility.GenerateExpressionForValue, which is basically trying to get an expression for the int[] array you pass into the List constructor. Since there’s no type descriptor for the int[] array, it fails (and throws a null ref in the process, instead of the “can’t generate property set exception” that it should).
I can’t figure out a built in way of getting a serializable value into a List<int>, so I just used a static method:
class IntListConverter : TypeConverter {
public static List<int> FromString(string value) {
return new List<int>(
value
.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
.Select(s => Convert.ToInt32(s))
);
}
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) {
if (destinationType == typeof(InstanceDescriptor)) {
List<int> list = (List<int>)value;
return new InstanceDescriptor(this.GetType().GetMethod("FromString"),
new object[] { string.Join(",", list.Select(i => i.ToString()).ToArray()) }
);
}
return base.ConvertTo(context, culture, value, destinationType);
}
}
Method 2
WHile I can’t say I have any particular experience with this error, other sources indicate that you need to add a conversion to the type InstanceDescriptor. check out:
http://weblogs.asp.net/bleroy/archive/2005/04/28/405013.aspx
Which provides an explanation of the reasons or alternatively:
http://forums.asp.net/p/1191839/2052438.aspx#2052438
Which provides example code similar to yours.
Method 3
I solved something simular by creating 2 properties:
public List<int> ModuleIDs { get .... set ... }
public string ModuleIDstring { get ... set ... }
The ModuleIDstring converts its value set to a list and sets the ModuleIDs property.
This will also make the ModuleIDs usable from a PropertyGrid etc.
Ok, not the best, typesafe solution, but for me it works.
Method 4
pass the list from the code behind…
aspx:
<UC:MyControl id="uc" runat="server" />
code-behind:
List<int> list = new List<int>(); list.add(1); list.add(2); list.add(3); uc.ModuleIds = list;
Method 5
The way I normally do this is to make the property wrap the ViewState collection. Your way looks better if it can be made to work, but this will get the job done:
public IList<int> ModuleIds
{
get
{
string moduleIds = Convert.ToString(ViewState["ModuleIds"])
IList<int> list = new Collection<int>();
foreach(string moduleId in moduleIds.split(","))
{
list.Add(Convert.ToInt32(moduleId));
}
return list;
}
}
Method 6
I believe the problem is the set{}. The type converter want to change the List<int> back into a string, but CanConvertFrom() fails for List<int>.
Method 7
You can pass it into a string and split on comma to populate a private variable. Does not have the nicety of attribution, but will work.
private List<int> modules;
public string ModuleIds
{
set{
if (!string.IsNullOrEmpty(value))
{
if (modules == null) modules = new List<int>();
var ids = value.Split(new []{','});
if (ids.Length>0)
foreach (var id in ids)
modules.Add((int.Parse(id)));
}
}
Method 8
I think that you’re best option is to make your usercontrol have a DataSource-style property.
You take the property as an object and then do some type checking against IList/ IEnumerable/ etc to make sure that it is correct.
All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0