views:

107

answers:

0

HI Guys, I've been working on a little experiement to see if I could create a helper method to serialize any of my types to any type of HTML tag I specify.

I'm getting a NullReferenceException when _writer = _viewContext.Writer; is called in

protected virtual void Dispose(bool disposing) {/*...*/}

I think I'm at a point where it almost works (I've gotten other implementations to work) and I was wondering if somebody could point out what I'm doing wrong? Also, I'd be interested in hearing suggestions on how I could improve the design?

So basically, I have this code that will generate a Select box with a number of options:

// the idea is I can use one method to create any complete tag of any type 
// and put whatever I want in the content area
<% using (Html.GenerateTag<SelectTag>(Model, new { href = Url.Action("ActionName") })) { %>
    <%foreach (var fund in Model.Funds) {%>
        <% using (Html.GenerateTag<OptionTag>(fund)) { %>
            <%= fund.Name %>
        <% } %>
    <% } %>
<% } %>

This Html.GenerateTag helper is defined as:

public static MMTag GenerateTag<T>(this HtmlHelper htmlHelper, object elementData, object attributes) where T : MMTag
{
    return (T)Activator.CreateInstance(typeof(T), htmlHelper.ViewContext, elementData, attributes);
}

Depending on the type of T it'll create one of the types defined below,

    public class HtmlTypeBase : MMTag
    {
        public HtmlTypeBase()
        { }

        public HtmlTypeBase(ViewContext viewContext, params object[] elementData)
        {
            base._viewContext = viewContext;
            base.MergeDataToTag(viewContext, elementData);
        }
    }

    public class SelectTag : HtmlTypeBase
    {
        public SelectTag(ViewContext viewContext, params object[] elementData)
        {
            base._tag = new TagBuilder("select");

            //base.MergeDataToTag(viewContext, elementData);
        }
    }

    public class OptionTag : HtmlTypeBase
    {
        public OptionTag(ViewContext viewContext, params object[] elementData)
        {
            base._tag = new TagBuilder("option");

            //base.MergeDataToTag(viewContext, _elementData);
        }
    }

    public class AnchorTag : HtmlTypeBase
    {
        public AnchorTag(ViewContext viewContext, params object[] elementData)
        {
            base._tag = new TagBuilder("a");

            //base.MergeDataToTag(viewContext, elementData);
        }
    }

all of these types (anchor, select, option) inherit from HtmlTypeBase, which is intended to perform base.MergeDataToTag(viewContext, elementData);. This doesn't happen though. It works if I uncomment the MergeDataToTag methods in the derived classes, but I don't want to repeat that same code for every derived class I create.

This is the definition for MMTag:

public class MMTag : IDisposable
{
    internal bool _disposed;
    internal ViewContext _viewContext;
    internal TextWriter _writer;
    internal TagBuilder _tag;
    internal object[] _elementData;

    public MMTag() {}

    public MMTag(ViewContext viewContext, params object[] elementData)
    { }

    public void Dispose()
    {
        Dispose(true /* disposing */);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (!_disposed)
        {
            _disposed = true;
            _writer = _viewContext.Writer;
            _writer.Write(_tag.ToString(TagRenderMode.EndTag));
        }
    }

    protected void MergeDataToTag(ViewContext viewContext, object[] elementData)
    {
        Type elementDataType = elementData[0].GetType();
        foreach (PropertyInfo prop in elementDataType.GetProperties())
        {
            if (prop.PropertyType.IsPrimitive || prop.PropertyType == typeof(Decimal) || prop.PropertyType == typeof(String))
            {
                object propValue = prop.GetValue(elementData[0], null);
                string stringValue = propValue != null ? propValue.ToString() : String.Empty;
                _tag.Attributes.Add(prop.Name, stringValue);
            }
        }

        var dic = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
        var attributes = elementData[1];
        if (attributes != null)
        {
            foreach (PropertyDescriptor descriptor in TypeDescriptor.GetProperties(attributes))
            {
                object value = descriptor.GetValue(attributes);
                dic.Add(descriptor.Name, value);
            }
        }

        _tag.MergeAttributes<string, object>(dic);

        _viewContext = viewContext;
        _viewContext.Writer.Write(_tag.ToString(TagRenderMode.StartTag));
    }
}

Thanks

Dave