4

Me and the webforms parser are having some difficulties today, regarding control properties I hope some of you can help me with!

I have a Control with a property named Value with object as type. When every time I declare it in my aspx I get the error

Cannot create an object of type 'System.Object' from its string representation '...' for the 'Value' property.

Wtf, isnt everything Objects? :)

I then tried to add a TypeConverter on my property, but no luck.

My Control

[ParseChildren(true, "Value")]
[TypeConverter(typeof(ExpandableObjectConverter))]
[ControlBuilder(typeof(ParamControlBuilder))]
public class Param : Control
{
    public string Name { get; set; }

    [TypeConverter(typeof(StringToObjectConverter))]
    public object Value { get; set; }

    protected override void AddParsedSubObject(object obj)
    {
        base.AddParsedSubObject(obj);
    }

    public override void DataBind()
    {
        base.DataBind();
    }

    protected override void DataBindChildren()
    {
        base.DataBindChildren();
    }
}

The TypeConverter

public class StringToObjectConverter : TypeConverter 
{
    public override bool IsValid(ITypeDescriptorContext context, object value)
    {
        return true;
    }

    public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
    {
        if (sourceType == typeof(string))
        {
            return true;
        }

        return base.CanConvertFrom(context, sourceType);
    }

    public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
    {
        if (destinationType == typeof(object))
        {
            return true;
        }

        if (destinationType == typeof(InstanceDescriptor))
        {
            return true;
        }

        return base.CanConvertTo(context, destinationType);
    }

    public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
    {
        return value.ToString();
    }

    public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
    {
        if (destinationType == typeof(object))
        {
            return (object)value;
        }

        if (destinationType == typeof(InstanceDescriptor))
        {
            return new InstanceDescriptor(typeof(object).GetConstructor(new Type[] { }), new object[] { });
        }

        return base.ConvertTo(context, culture, value, destinationType);
    }
}

What i want to do is to be able to write in my aspx page the following

<my:param name="A object" value="<# value_of_a_method()" %> runat="server" />
<my:param name="A object" value="this_is_just_a_string" runat="server" />

The first example works fine, the second fails with mentioned error. And no, i can't believe that the only way around this is to databind everytime, even for constant strings like

<my:param name="A object" value='<%# "this_is_just_a_string" %>' runat="server" />
4
  • That converter is for the Control, not the property. That asde, if you use it as written, it would always deserialize as a string; there's a big difference between casting a string to object vs deserializing it to what it was previously.,. Commented Nov 24, 2011 at 17:04
  • So i should basically create my own Wrapper-class that just holds a object, and put the a Converter on that which can convert Strings and Object to and from my wrapper? And then the type of my Value-property would be this wrapper instead of Object? Commented Nov 24, 2011 at 18:23
  • you havent really made it clear what you are trying to achieve. Persisting and recreating a value known only as "object" is actually remarkably hard... Commented Nov 24, 2011 at 18:32
  • updated my question with want to achieve. I don't need to persist the values between postbacks, so no viewstate involved. I just need a way to declarative express what values to use, strings, object, whatever. Commented Nov 24, 2011 at 23:56

1 Answer 1

3

What about adding a ValueAsString property that is actually the one serialized and prevent Value from being serialized?

ValueAsString would then be responsible for serializing and deserializing Value appropriately (such as XML or JSON serialization).

Update It turns out that, due to the way that ASP.Net builds the controls, there isn't a way to do what you are looking to do.

It is possible to implement a mechanism to get object types, such as strings or internal classes, to be processed, but if you include primitive types such as Ints in the mix, the framework won't even execute the TypeConverter for your Object Value.

For example, in the following two cases (using VB syntax), both Params will be created, but the Value for Param1 will be nothing:

<cti:Param ID="Param1" Value="<%# CInt(1) %>" runat="server" />
<cti:Param ID="Param2" Value="test" runat="server" />

For completeness, here is how I was able to implement an object converter without having ASP.Net throw a fit after realizing that it always needs an actual class to instantiate as opposed to just an Object:

First, create an intermediate class that the underlying logic can instantiate. This class will have one constructor for each object type class that can be implemented:

public class MyObjectConverter
{
    public MyObjectConverter() : base()
    {
    }
    public MyObjectConverter(string oValue) : this()
    {
        this.Value = oValue;
    }

    public object Value { get; set; }

    public override string ToString()
    {
        if (this.Value == null)
        {
            return string.Empty;
        }
        else
        {
            return this.Value.ToString();
        }
    }
}

Next, modify the StringToObjectConverter's ConvertTo method so that it creates an instance of the new class using the appropriate constructor (an object constructor still confuses the code here):

public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
{
    if (destinationType == typeof(object))
    {
        return (object)value;
    }

    if (destinationType == typeof(InstanceDescriptor))
    {
        return new InstanceDescriptor(typeof(MyObjectConverter).GetConstructor(new Type[] {value.GetType()}), new object[] {value});
    }

    return base.ConvertTo(context, culture, value, destinationType);
}

Finally, in the Param object, modify the Value property to handle the new type:

private object m_Value;
[TypeConverter(typeof(StringToObjectConverter))]
public object Value
{
    get
    {
        return m_Value;
    }
    set
    {
        if (value is MyObjectConverter)
        {
            m_Value = ((MyObjectConverter)value).Value;
        }
        else
        {
            m_Value = value;
        }
    }
}

I suppose it might be possible to generate dynamic classes or constructors or the object converter class, but that is not something that I have experience with, so I can't really speak to whether or not that is a viable solution.

So, to summarize, there are two issues with this approach:

1) You cannot use it for primitive data types. However, even using an Object Value property without bringing strings into the equation does not support this functionality.

2) You must explicitly define constructors for all of the data types that you wish to support.

Sign up to request clarification or add additional context in comments.

3 Comments

the issue is that i want to match an already existing xml-format i have as much possible, basically being able to copy/paste some markup unto a aspx-page, add runat="server" and things should just work,
Updated the answer with a new approach and findings.
thank you for the updated answer. This is similar to what i ended up with, and i kinda realized that the only feasible option is just to support primitive types in the aspx markup, and force the developer to switch to Codebehind if they want to bind complex types. I mean, thats what Webforms does already for ie. ListItem in ListControls, there you you can't do anything fancy with the markup.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.