Передача списка int в качестве параметра пользовательскому веб-элементу управления

StackOverflow https://stackoverflow.com/questions/251439

  •  05-07-2019
  •  | 
  •  

Вопрос

Я хочу передать список int (List) в качестве декларативного свойства пользовательскому веб-элементу управления следующим образом:

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

Для этого я создал TypeConverter:

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

А затем добавил атрибут в свое свойство:

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

Но я получаю эту ошибку во время выполнения:

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.

Мой вопрос аналогичен найденному здесь, но решение не решает мою проблему:

Обновлять: Я нашел страницу, которая решила первую проблему.Я обновил приведенный выше код, чтобы показать свои исправления.Добавленный код CanConvertTo и ConvertTo методы.Теперь я получаю другую ошибку:

Object reference not set to an instance of an object.

Эта ошибка, по-видимому, косвенно вызвана чем-то в ConvertTo метод.

Это было полезно?

Решение

Подключив отладчик к Cassini, я вижу, что нулевая ссылка на самом деле поступает из System.Web.Compilation.CodeDomUtility.GenerateExpressionForValue, который по сути пытается получить выражение для массива int[], который вы передаете в конструктор списка.Поскольку для массива int[] нет дескриптора типа, он терпит неудачу (и выдает в процессе нулевую ссылку вместо «невозможно сгенерировать исключение набора свойств», как должно).

Я не могу придумать встроенный способ получения сериализуемого значения в List<int>, поэтому я просто использовал статический метод:

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

Другие советы

Хотя я не могу сказать, что у меня есть какой-то конкретный опыт решения этой ошибки, другие источники указывают, что вам необходимо добавить преобразование к типу InstanceDescriptor.проверить:

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

В котором приводится объяснение причин или альтернативно:

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

В котором приведен пример кода, похожего на ваш.

Я решил нечто подобное, создав 2 свойства:

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

ModuleIDstring преобразует свой набор значений в список и устанавливает свойство ModuleIDs.

Это также позволит использовать ModuleID из PropertyGrid и т. д.

Хорошо, не самое лучшее типобезопасное решение, но для меня оно работает.

передать список из кода позади...

аспкс:

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

код программной части:

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

uc.ModuleIds = list;

Обычно я делаю это так, чтобы свойство обертывало коллекцию ViewState.Ваш путь выглядит лучше, если его можно заставить работать, но это выполнит свою работу:

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

Я считаю, что проблема в наборе {}.Преобразователь типов хочет изменить List<int> обратно в строку, но CanConvertFrom() терпит неудачу для List<int>.

Вы можете передать его в строку и разделить запятой, чтобы заполнить частную переменную.Не имеет тонкости атрибуции, но будет работать.

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

Я думаю, что лучший вариант — сделать так, чтобы ваш пользовательский элемент управления имел свойство в стиле DataSource.

Вы принимаете свойство как объект, а затем выполняете некоторую проверку типа по IList/IEnumerable/т. д., чтобы убедиться, что оно правильное.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top