2008-10-30 13 views
12

Quiero pasar una lista int (Lista) como una propiedad declarativa a un control de usuario de la web de esta manera:Pasando lista int como un parámetro para un control de usuario Web

<UC:MyControl runat="server" ModuleIds="1,2,3" /> 

que creó un TypeConverter para hacer esto :

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

Y luego añade el atributo de mi propiedad:

[TypeConverter(typeof(IntListConverter))] 
public List<int> ModuleIds 
{ 
    get { ... }; set { ... }; 
} 

pero me sale este error en tiempo de ejecución:

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.

Mi pregunta es similar a la encontrada here, pero la solución no resuelve mi problema:

Actualización: encontré una página que resolvió el primer problema. Actualicé el código anterior para mostrar mis correcciones. El código agregado es los métodos CanConvertTo y ConvertTo. Ahora me sale un error diferente .:

Object reference not set to an instance of an object.

Este error parece ser causado indirectamente por algo en el método ConvertTo.

+0

Seguramente no escribió IntListConverter en el nombre de clase y IntegerListConverter en el atributo, ¿o sí? – Alan

+0

Ja, no ... Arreglaré eso. –

+0

Gracias por hacer esta pregunta. Me enfrenté casi al mismo problema. –

Respuesta

10

Después de conectar un depurador en Cassini, veo que la referencia nula proviene en realidad de System.Web.Compilation.CodeDomUtility.GenerateExpressionForValue, que básicamente trata de obtener una expresión para la matriz int [] que pasa a la Lista constructor. Como no hay un descriptor de tipo para la matriz int [], falla (y arroja una ref nula en el proceso, en lugar de la "excepción de conjunto de propiedades no puede generar" que debería).

No puedo imaginar una construida en la forma de obtener un valor serializable en una lista <int>, por lo que sólo se utiliza un método estático:

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

Se ve bien, gracias! Loco reflejo. –

+0

+1 Gracias por su respuesta, me ahorró un montón de tiempo –

+1

+1 Por esta respuesta también. Una solución de trabajo a un problema muy oscuro. Tuve el mismo problema que el OP que utilizar una 'Lista ' en lugar de 'List '. La solución funcionó perfectamente con algunos cambios menores para dar cuenta de la cadena frente a int. –

0

pasar la lista del código detrás ...

aspx:

<UC:MyControl id="uc" runat="server" /> 

de código subyacente:

List<int> list = new List<int>(); 
list.add(1); 
list.add(2); 
list.add(3); 

uc.ModuleIds = list; 
+0

Esta es la solución que he estado usando, pero definitivamente menos que óptima. –

+0

Para mí, esto se ve mucho mejor que la clase de convertidor de tipo. ¿Cuál es más legible? – Rismo

+0

El problema con esto es que requiere modificaciones en el código subyacente, lo que rompe la separación de las partes declarativas y no declarativas del código. –

0

La forma en que normalmente hago esto es hacer que la envoltura de la propiedad la colección ViewState. Su forma se ve mejor si se puede hacer que funcione, pero esto hará el trabajo:

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; 
    } 
} 
+0

Idea interesante, pero aún no óptima, ya que no permitiría que el marcado declarativo funcione. –

0

Creo que el problema es el conjunto {}. El convertidor de tipos desea cambiar el List<int> de nuevo a una cadena, pero CanConvertFrom() falla para List<int>.

+0

No, ese caso se maneja con un método ConvertTo, que no se muestra, para mayor claridad. –

0

Puede pasarlo a una cadena y dividir en coma para completar una variable privada. No tiene la calidad de la atribución, pero funcionará.

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

Esto es posible, pero no resuelve el problema. Lo que estoy tratando de hacer debería ser posible, ¿no es así? –

1

Aunque no puedo decir que tengo alguna experiencia en particular con este error, otras fuentes indican que es necesario agregar una conversión al tipo InstanceDescriptor. visita:

http://weblogs.asp.net/bleroy/archive/2005/04/28/405013.aspx

que proporciona una explicación de las razones o como alternativa:

http://forums.asp.net/p/1191839/2052438.aspx#2052438

que proporciona código de ejemplo similar al suyo.

+0

Esta parece ser la dirección correcta, pero sigo recibiendo un error. Ver mi actualización –

0

Creo que su mejor opción es hacer que su usercontrol tenga una propiedad de estilo DataSource.

Toma la propiedad como un objeto y luego realiza alguna verificación de tipo contra IList/IEnumerable/etc para asegurarse de que sea correcta.

1

Solucioné algo simular mediante la creación de 2 propiedades:

public List<int> ModuleIDs { get .... set ... } 
public string ModuleIDstring { get ... set ... } 

El ModuleIDstring convierte su valor establecido en una lista y establece la propiedad ModuleIDs.

Esto también hará que los IDs_de_módulos utilizable a partir de un PropertyGrid etc.

, no es la mejor solución Ok, typesafe, pero para mí es que funciona.

+0

. Esta es probablemente la solución con la que terminaremos, pero aún es extraño que no podamos encontrar la manera de solucionar el error real. –

Cuestiones relacionadas