24

I have an int array as a property of a Web User Control. I'd like to set that property inline if possible using the following syntax:

<uc1:mycontrol runat="server" myintarray="1,2,3" />

This will fail at runtime because it will be expecting an actual int array, but a string is being passed instead. I can make myintarray a string and parse it in the setter, but I was wondering if there was a more elegant solution.

juan
  • 80,295
  • 52
  • 162
  • 195
ern
  • 1,522
  • 13
  • 19

11 Answers11

20

Implement a type converter, here is one, warning : quick&dirty, not for production use, etc :

public class IntArrayConverter : System.ComponentModel.TypeConverter
{
    public override bool CanConvertFrom(System.ComponentModel.ITypeDescriptorContext context, Type sourceType)
    {
        return sourceType == typeof(string);
    }
    public override object ConvertFrom(System.ComponentModel.ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
    {
        string val = value as string;
        string[] vals = val.Split(',');
        System.Collections.Generic.List<int> ints = new System.Collections.Generic.List<int>();
        foreach (string s in vals)
            ints.Add(Convert.ToInt32(s));
        return ints.ToArray();
    }
}

and tag the property of your control :

private int[] ints;
[TypeConverter(typeof(IntsConverter))]
public int[] Ints
{
    get { return this.ints; }
    set { this.ints = value; }
}
juan
  • 80,295
  • 52
  • 162
  • 195
mathieu
  • 30,974
  • 4
  • 64
  • 90
6

@mathieu, thanks so much for your code. I modified it somewhat in order to compile:

public class IntArrayConverter : System.ComponentModel.TypeConverter
{
    public override bool CanConvertFrom(System.ComponentModel.ITypeDescriptorContext context, Type sourceType)
    {
        return sourceType == typeof(string);
    }
    public override object ConvertFrom(System.ComponentModel.ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
    {
        string val = value as string;
        string[] vals = val.Split(',');
        System.Collections.Generic.List<int> ints = new System.Collections.Generic.List<int>();
        foreach (string s in vals)
            ints.Add(Convert.ToInt32(s));
        return ints.ToArray();
    }
}
ern
  • 1,522
  • 13
  • 19
5

Seems to me that the logical—and more extensible—approach is to take a page from the asp: list controls:

<uc1:mycontrol runat="server">
    <uc1:myintparam>1</uc1:myintparam>
    <uc1:myintparam>2</uc1:myintparam>
    <uc1:myintparam>3</uc1:myintparam>
</uc1:mycontrol>
Billy Jo
  • 1,326
  • 19
  • 32
  • 1
    Thanks. This may work, but it's a lot of extra code up front. I'm trying to stay as minimalist as possible. – ern Sep 22 '08 at 19:37
4

Great snippet @mathieu. I needed to use this for converting longs, but rather than making a LongArrayConverter, I wrote up a version that uses Generics.

public class ArrayConverter<T> : TypeConverter
{
    public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
    {
        return sourceType == typeof(string);
    }

    public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
    {
        string val = value as string;
        if (string.IsNullOrEmpty(val))
            return new T[0];

        string[] vals = val.Split(',');
        List<T> items = new List<T>();
        Type type = typeof(T);
        foreach (string s in vals)
        {
            T item = (T)Convert.ChangeType(s, type);
            items.Add(item);
        }
        return items.ToArray();
    }
}

This version should work with any type that is convertible from string.

[TypeConverter(typeof(ArrayConverter<int>))]
public int[] Ints { get; set; }

[TypeConverter(typeof(ArrayConverter<long>))]
public long[] Longs { get; set; }

[TypeConverter(typeof(ArrayConverter<DateTime))]
public DateTime[] DateTimes { get; set; }
spleenboy
  • 41
  • 1
2

Have you tried looking into Type Converters? This page looks worth a look: http://www.codeguru.com/columns/VB/article.php/c6529/

Also, Spring.Net seems to have a StringArrayConverter (http://www.springframework.net/doc-latest/reference/html/objects-misc.html - section 6.4) which, if you can feed it to ASP.net by decorating the property with a TypeConverter attribute, might work..

Rob
  • 45,296
  • 24
  • 122
  • 150
2

You could also do something like this:

namespace InternalArray
{
    /// <summary>
    /// Item for setting value specifically
    /// </summary>

    public class ArrayItem
    {
        public int Value { get; set; }
    }

    public class CustomUserControl : UserControl
    {

        private List<int> Ints {get {return this.ItemsToList();}
        /// <summary>
        /// set our values explicitly
        /// </summary>
        [PersistenceMode(PersistenceMode.InnerProperty), TemplateContainer(typeof(List<ArrayItem>))]
        public List<ArrayItem> Values { get; set; }

        /// <summary>
        /// Converts our ArrayItem into a List<int> 
        /// </summary>
        /// <returns></returns>
        private List<int> ItemsToList()
        {
            return (from q in this.Values
                    select q.Value).ToList<int>();
        }
    }
}

which will result in:

<xx:CustomUserControl  runat="server">
  <Values>
            <xx:ArrayItem Value="1" />
  </Values>
</xx:CustomUserControl>
Michael Myers
  • 188,989
  • 46
  • 291
  • 292
Dgc
  • 21
  • 2
1

To add child elements that make your list you need to have your control setup a certain way:

[ParseChildren(true, "Actions")]
[PersistChildren(false)]
[ToolboxData("<{0}:PageActionManager runat=\"server\" ></PageActionManager>")]
[NonVisualControl]
public class PageActionManager : Control
{

The Actions above is the name of the cproperty the child elements will be in. I use an ArrayList, as I have not testing anything else with it.:

        private ArrayList _actions = new ArrayList();
    public ArrayList Actions
    {
        get
        {
            return _actions;
        }
    }

when your contorl is initialized it will have the values of the child elements. Those you can make a mini class that just holds ints.

mattlant
  • 15,384
  • 4
  • 34
  • 44
0

Do do what Bill was talking about with the list you just need to create a List property on your user control. Then you can implement it as Bill described.

David Basarab
  • 72,212
  • 42
  • 129
  • 156
0

You could add to the page events inside the aspx something like this:

<script runat="server">
protected void Page_Load(object sender, EventArgs e)
{
    YourUserControlID.myintarray = new Int32[] { 1, 2, 3 };
}
</script>
user19264
  • 491
  • 5
  • 11
0

You can implement a type converter class that converts between int array and string data types. Then decorate your int array property with the TypeConverterAttribute, specifying the class that you implemented. Visual Studio will then use your type converter for type conversions on your property.

0

If you use DataBinding on one of the parent Controls, you can use a DataBinding Expression:

<uc1:mycontrol runat="server" myintarray="<%# new [] {1, 2, 3} %>" />

With a custom expression builder, you can do something similar. The expression builder:

[ExpressionPrefix("Code")]
public class CodeExpressionBuilder : ExpressionBuilder
{
    public override CodeExpression GetCodeExpression(System.Web.UI.BoundPropertyEntry entry, object parsedData, ExpressionBuilderContext context)
    {
        return new CodeSnippetExpression(entry.Expression.Trim());
    }
}

Usage:

<uc1:mycontrol runat="server" myintarray="<%$ Code: new [] {1, 2, 3} %>" />
Protector one
  • 6,926
  • 5
  • 62
  • 86