Pergunta

Eu quero passar uma lista int (Lista) como uma propriedade declarativa para um controle de usuário web como esta:

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

Eu criei um TypeConverter para fazer isso:

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

E, em seguida, acrescentou o atributo a minha propriedade:

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

Mas eu recebo este erro em tempo de execução:

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.

A minha pergunta é semelhante ao encontrado aqui , mas a solução não resolver o meu problema:

Update: eu encontrei uma página que resolveu o primeiro problema. Eu atualizei o código acima para mostrar as minhas correções. O código é adicionado os métodos CanConvertTo e ConvertTo. Agora eu tenho um erro diferente:.

Object reference not set to an instance of an object.

Este erro parece estar indiretamente causada por algo no método ConvertTo.

Foi útil?

Solução

Depois de ligar um depurador em Cassini, vejo que o nulo ref é realmente provenientes de System.Web.Compilation.CodeDomUtility.GenerateExpressionForValue, que é basicamente tentando obter uma expressão para a matriz int [] você passa para o construtor Lista . Como não há descritor de tipo para a matriz int [], ele falhar (e lança um ref nulo no processo, em vez de "não pode gerar exceção conjunto de propriedades" que deveria).

Eu não consigo descobrir um construído em forma de obter um valor serializado em um List , então eu usei apenas um 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);
    }
}

Outras dicas

Enquanto eu não posso dizer que tenho qualquer experiência particular com este erro, outras fontes indicam que você precisa adicionar uma conversão para o tipo InstanceDescriptor. confira:

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

Que fornece uma explicação das razões ou, em alternativa:

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

Que fornece exemplo de código semelhante ao seu.

Eu resolvi algo simular criando 2 propriedades:

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

O ModuleIDstring converte seu valor definido para uma lista e define a propriedade ModuleIDs.

Isso também fará com que os ModuleIDs utilizável por uma PropertyGrid etc.

Ok, não o melhor, typesafe solução, mas para mim ele funciona.

passar a lista do código por trás ...

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;

A maneira que eu normalmente fazer isso é fazer com que a propriedade enrole a coleção ViewState. Seu caminho parece melhor se ele pode ser feito para trabalhar, mas isso vai começar o trabalho feito:

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

Eu acredito que o problema é o conjunto {}. O conversor de tipo quer mudar a parte de trás List<int> em uma string, mas CanConvertFrom() falhar por List<int>.

Você pode passá-lo em uma string e dividir em vírgula para preencher uma variável privada. Não tem a sutileza de atribuição, mas vai 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)));
    }
}

Eu acho que você é melhor opção é fazer o seu usercontrol tem uma propriedade DataSource de estilo.

Você pega a propriedade como um objeto e, em seguida, fazer alguma verificação de tipo contra IList / IEnumerable / etc para se certificar de que ele está correto.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top