views:

305

answers:

3

This question is being asked because I have no prior experience with delegate best practices

I have a unordered lists in html, where the structure is the same throughout my site, but the content of the lists may differ.

Examples:
List of object A

<ul>
  <li>
    <ul>
      <li>A.someMember1</li>
      <li>A.someMember2</li>
      <li>A.someMember3</li>
    </ul>
  </li>
</ul>

List of object B

<ul>
  <li>
    <ul>
      <li>B.someMember1</li>
      <li>B.someMember2</li>
      <li>B.someMember3</li>
      <li>B.someMember4</li>
    </ul>
  </li>
</ul>

I created two delegates:

protected delegate void RenderHtmlMethod(HtmlTextWriter writer);
protected delegate void RenderHtmlMethodWithObjects(HtmlTextWriter writer, object obj);

and the following method

 private void RenderList(HtmlTextWriter writer, string title, RenderHtmlMethod headingDelegate,
  RenderHtmlMethodWithObjects itemsDelegate, object objToRender)
 {

   writer.RenderBeginTag(HtmlTextWriterTag.Fieldset);

   writer.RenderBeginTag(HtmlTextWriterTag.Legend);
   writer.HtmlEncode(title);
   writer.RenderEndTag();//end Legend

   writer.AddAttribute(HtmlTextWriterAttribute.Class, "resultList");
   writer.RenderBeginTag(HtmlTextWriterTag.Ul);
   {
    headingDelegate(writer);

    itemsDelegate(writer, objToRender);
   }
   writer.RenderEndTag();//ul

   writer.RenderEndTag(); //fieldset

 }

That way, I can make methods that render the heading (just another li with an embedded ul) and then render the necessary list items for each list of object.

I can't redefine my classes to implement any interfaces, although, I could create a wrapper for the classes and implement the render method there. What do you think about this?

Does my structure make sense? Or am I insane?

A: 

Why are you passing in delegates to perform the rendering? Do you want each list to be rendered slightly different? If you are looking for consistency, I would just pass the list of objects that you want to be rendered along with some header text and render it all inside your method. For example, my proposed method signature might be something like:

private void RenderList<T>(HtmlTextWriter writer, string title, string headerText, List<T> objectsToRender) where T : IRender

That being said, if every list really should be rendered in a unique manner, then I don't think you're any more insane than the voices in my head.

UPDATE

I guess I need to expand my code sample...

private void RenderList<T>(HtmlTextWriter writer, string title, string headerText, List<T> objectsToRender) where T : IRender
{
    writer.RenderBeginTag(HtmlTextWriterTag.Fieldset);

    writer.RenderBeginTag(HtmlTextWriterTag.Legend);
    writer.HtmlEncode(title);
    writer.RenderEndTag();//end Legend

    writer.AddAttribute(HtmlTextWriterAttribute.Class, "resultList");
    writer.RenderBeginTag(HtmlTextWriterTag.Ul);
    {
        writer.RenderBeginTag(HtmlTextWriterTag.Div); //begin Header
        writer.AddAttribute(HtmlTextWriterAttribute.Class, "header");
        writer.HtmlEncode(headerText);
        writer.RenderEndTag(); //end Header

        // render all objects
        foreach (T obj in objectsToRender)
        {
            writer.RenderBeginTag(HtmlTextWriterTag.Li); // begin Custom Object Rendering
            obj.CustomRender(writer);
            writer.RenderEndTag(); // end Custom Object Rendering
        }
    }
    writer.RenderEndTag();//ul

    writer.RenderEndTag(); //fieldset
}

// IRender interface
public interface IRender
{
    void CustomRender(HtmlTextWriter writer);
}
Chris Shouts
A: 

I'd go for a generic method Render(HtmlTextWriter) and define all other parameters as properties of the class:

interface IRenderable
{
    void Render(HtmlTextWriter writer);
}

class ListComponent : IRenderable
{
    public List<IRenderable> Items { get; set; }
    public string Title { get; set; }

    public void Render(HtmlTextWriter writer)
    {
        writer.RenderBeginTag(HtmlTextWriterTag.Fieldset);

        writer.RenderBeginTag(HtmlTextWriterTag.Legend);
        writer.HtmlEncode(Title);
        writer.RenderEndTag();//end Legend

        writer.AddAttribute(HtmlTextWriterAttribute.Class, "resultList");
        writer.RenderBeginTag(HtmlTextWriterTag.Ul);

        foreach (var item in Items)
        {
            writer.RenderBeginTag(HtmlTextWriterTag.Li);
            item.Render(writer);
            writer.RenderEndTag();//li
        }

        writer.RenderEndTag();//ul

        writer.RenderEndTag(); //fieldset
    }
}
taoufik
+4  A: 

I suppose it's not a bad decision. However, I think that better is to create special interface (or abstract class) which is used to generate your lists.

public abstract class ListRenderer
{
  public abstract IEnumerable Items {get;}
  public abstract String GenerateHeaderText();
  public String GenerateItemText(objectItem);
  public abstract void RenderList(TextWriter writer);
}

Then you just create your wrapper around your concrete items and pass this object to your generator method. If your list is build in a common way it's possible to implement all logic inside ListRenderer and then override only GenerateHeaderText and GenerateItemText

Vetragon
I chose this seeing that it leads to more organized code when implemented than my previous solution. I chose to create an interface
phsr