This project has moved. For the latest updates, please go here.

Widgets Advanced Overview

Widgets are little pieces of user interface that are 100% managed in the browser. You can easily drag them onto your site, rearrange them, removed, and even set properties on them all with your browser. You can use widgets to leverage outside data, call a custom web service, re-use an RSS feed, or even utilize the Graffiti data APIs.

Click here for a list of Graffiti pre-built widgets.

Related Articles

Rendering

By default, Widgets render with a column-wide UnOrdered List. You can change this within the theme using the macro methods LeftSideBar and RightSideBar.

With-in each individual LI element, an individual widget is rendered. By default a widget's Title is render as an H3 element and the Widget "data" is rendered by overriding the RenderData method.

Widget Methods

You can use a number of methods to customize your own widgets and override pre-built Widgets, including:
  • Title – You can override the Title property. This is useful if Title is not required or you want to preset it.
  • RenderData – (abstract) RenderData is responsible for returning the custom UI.
  • Render – By default, Render() returns Title wrapped in H3 tags and is the result of RenderData().
  • Name – Name is displayed in the Widget viewer.
  • IsUserValid() – IsUserValid() returns true by default, but can be overridden to hide Widgets from users at runtime.
Widgets are stored in Graffiti's ObjectStore using XML Serialization. If you need to store additional data, you can simply add fields and properties to your derived class and the data is persisted to the database when any changes are made.

Editing

Widgets leverage the Graffiti dynamic form API by deriving from Graffiti.Core.EditableForms. You can also leverage this same API to collect additional data from users within your Widget.

RSS, Atom, and Opml

Graffiti has a built in API you can use to read external feeds. To make this really easy to do, instead of deriving from Graffiti.Core.Widget, you should derive from Graffiti.Core.WidgetFeed.

Widget Examples

The following are examples of pre-built Widgets.

Example One:

The first example, Title & Body, allows users to quickly and easily add a block of content to their site. The title is a regular textbox and the body is managed with a WYWIWYG editor.

Before we look at the code, here is what's going on:
  1. Add a local public field "HTML". This field will store the "body" content.
  2. Override DataAsNameValueCollection which enables us to expose the HTML field to our form.
  3. Override AddFormElements and add the Title textbox and our WYSIWYG editor.
  4. Override SetValues which allows us to pull the HTML value out of our form and persist it to the database.
  5. Override RenderData to simply return whatever is stored in the HTML field.
  6. Override Name to display the Title value when this Widget is display in a list. This is optional, but it is really helpful since widgets can be used multiple times.

Here is a look at the code:

/// <summary>
/// A widget which displays a title and the a block of text
/// </summary>
[WidgetInfo("e7abe913-0de7-45a5-9cc5-6c6c5809e808",
    "Title and Body",
    "Represents a title and body")]
public class TitleAndBody : Widget
{
 
    /// <summary>
    /// Stores the Content. Title is part of the Widget.
    /// </summary>
    public string HTML;
 
    /// <summary>
    /// Override name to give a more meaningful
    /// value when displayed in a list.
    /// </summary>
    public override string Name
    {
        get
        {
            if (string.IsNullOrEmpty(Title))
                return "An empty box";
            else
                return Title + " (Title and Body widget)";
        }
    }
 
    /// <summary>
    /// Override DataAsNameValueCollection in include
    /// the HTML content.
    /// </summary>
    /// <returns></returns>
    protected override NameValueCollection DataAsNameValueCollection()
    {
        NameValueCollection nvc = base.DataAsNameValueCollection();
        nvc["HTML"] = HTML;
 
        return nvc;
    }
 
    /// <summary>
    /// Override SetValues so we can pull HTML out of Form object
    /// and store it locally to be saved to the database.
    /// </summary>
    public override StatusType SetValues(HttpContext context, NameValueCollection nvc)
    {
        base.SetValues(context, nvc);
        HTML = nvc["HTML"];
        return StatusType.Success;
    }
 
    /// <summary>
    /// Add our Title and HTML textboxes.
    /// </summary>
    /// <returns></returns>
    protected override FormElementCollection AddFormElements()
    {
        FormElementCollection fec = new FormElementCollection();
 
        fec.Add(new TextFormElement("Title", "Title", "The title of the section"));
        fec.Add(new WYWIWYGFormElement("HTML", "Your Content", null));
        return fec;
    }
 
    /// <summary>
    /// No processing, just render the data.
    /// </summary>
    /// <returns></returns>
    public override string RenderData()
    {
        return HTML;
    }
}


Example Two:

The second example is from Graffiti's RecentComment widget. This widget allows users to include the last N published comments in the sidebar.

The steps for this are very similar to those in Example One with one major difference. In this Widget, we need to do a bit more work in RenderData to format our comments.

[WidgetInfo("4eaf767d-c787-4e9c-aafc-e37d0ef3f70c",
    "Recent Comments",
    "Controls the display of a list of recent comments")]
[Serializable]
public class RecentCommentsWidget : Widget
{
    public override string Name
    {
        get
        {
            if (string.IsNullOrEmpty(Title))
                return "Recent Comments";
            else
                return Title;
        }
    }
 
    public override string Title
    {
        get
        {
            if (string.IsNullOrEmpty(base.Title))
                base.Title = "Recent Comments";
 
            return base.Title;
        }
        set
        {
 
            base.Title = value;
        }
    }
 
    private int _categoryId = -1;
 
    public int CategoryId
    {
        get { return _categoryId; }
        set { _categoryId = value; }
    }
 
    protected override FormElementCollection AddFormElements()
    {
        FormElementCollection fec = new FormElementCollection();
        fec.Add(AddTitleElement());
        ListFormElement lfe = new ListFormElement("numberOFcomments",
            "Number of Comments",
            "The number of most recent comments to list");
 
        lfe.Add(new ListItemFormElement("3", "3"));
        lfe.Add(new ListItemFormElement("5","5",true));
        lfe.Add(new ListItemFormElement("10","10"));
        fec.Add(lfe);
 
        return fec;
    }
 
    public override string RenderData()
    {
        StringBuilder sb = new StringBuilder("<ul>");
 
        sb.Append(new Macros().ULRecentComments(NumberOfComments));
 
        sb.Append("</ul>\n");
 
        return sb.ToString();
    }
 
    protected override NameValueCollection DataAsNameValueCollection()
    {
        NameValueCollection nvc = base.DataAsNameValueCollection();
        nvc["numberOFcomments"] = NumberOfComments.ToString();
        nvc["categoryid"] = CategoryId.ToString();
        return nvc;
    }
 
    public override StatusType SetValues(System.Web.HttpContext context, NameValueCollection nvc)
    {
        base.SetValues(context, nvc);
        NumberOfComments = Int32.Parse(nvc["numberOFcomments"]);
        CategoryId = Int32.Parse(nvc["categoryid"]);
        return StatusType.Success;
    }
 
    public override string FormName
    {
        get
        {
            return "Recent Comment Configuration";
        }
    }
 
    public int NumberOfComments = 5;

Example Three:

In this example we look at the Graffiti Twitter widget. Twitter is a very popular micro-blogging application with which users create 140-character status updates called Tweets. Twitter has a couple of API endpoints, but here we will leverage a very simple RSS feed which contains our recent tweets.

Before looking through the code, there is one very important thing to call out. We are deriving from the WidgeFeed instead of Widget. This gives us access to the Graffiti FeedManager which will not only request any RSS, Atom, or Opml feed for us, but it will also own the responsibility of caching and storing this data every hour. This way our site and pages are always very responsive, even when the service for our data may not be.

[WidgetInfo("3ec475ab-cd5c-47f6-8e37-e7752a46cc5a",
    "Twitter",
    "Twitter messages")]
public class TwitterWidget : WidgetFeed
{
    private string _UserName;
 
    public string UserName
    {
        get { return _UserName; }
        set { _UserName = value; }
    }
 
    private int _itemsToDisplay = 3;
 
    public int ItemsToDisplay
    {
        get { return _itemsToDisplay; }
        set { _itemsToDisplay = value; }
    }
 
 
    public override string FeedUrl
    {
        get { return "http://twitter.com/statuses/user_timeline/" + UserName + ".rss"; }
    }
 
    public override string RenderData()
    {
        StringBuilder sb = new StringBuilder("<ul>");
 
        if (!string.IsNullOrEmpty(UserName))
        {
            try
            {
                RssChannel channel = this.Document();
                if (channel != null && channel.Items != null)
                {
                    int min = Math.Min(channel.Items.Count, ItemsToDisplay);
                    for (int i = 0; i < min; i++)
                    {
                        string desc = channel.Items[i].Description;
                        int index = desc.IndexOf(":");
                       
                        sb.Append("<li>" +   ( (index > -1) ? desc.Substring(index + 1).Trim() : desc) + "</li>");
                       
                    }
                }
            }
            catch(Exception)
            {
            }
            sb.Append("</ul>\n");
        }
        return sb.ToString();
    }
 
    public override string Title
    {
        get
        {
            if (string.IsNullOrEmpty(base.Title))
                base.Title = "My Tweets";
 
            return base.Title;
        }
        set
        {
            base.Title = value;
        }
    }
 
    public override string Name
    {
        get
        {
            return "Twitter";
        }
    }
 
    protected override FormElementCollection AddFormElements()
    {
        FormElementCollection fec = new FormElementCollection();
        fec.Add(AddTitleElement());
        fec.Add(new TextFormElement("username", "UserName", "(your twitter username"));
        ListFormElement lfe = new ListFormElement("itemsToDisplay",
            "Number of Tweets",
            "(how many tweets do you want to display?)");
        lfe.Add(new ListItemFormElement("1","1"));
        lfe.Add(new ListItemFormElement("3", "3", true));
        lfe.Add(new ListItemFormElement("5", "5"));
        fec.Add(lfe);
 
        return fec;
    }
 
    protected override NameValueCollection DataAsNameValueCollection()
    {
        NameValueCollection nvc =  base.DataAsNameValueCollection();
        nvc["username"] = UserName;
        nvc["itemsToDisplay"] = ItemsToDisplay.ToString();
 
        return nvc;
    }
 
    public override StatusType SetValues(HttpContext context, NameValueCollection nvc)
    {
        StatusType statusType = base.SetValues(context, nvc);
        if(statusType == StatusType.Success)
        {
            ItemsToDisplay = Int32.Parse(nvc["itemsToDisplay"]);
            UserName = nvc["username"];
 
            try
            {
                RegisterForSyndication();
            }
            catch(Exception ex)
            {
                statusType = StatusType.Error;
                SetMessage(context,ex.Message);
            }
        }
 
        return statusType;
    }
 
}

Last edited Mar 31, 2010 at 6:59 PM by jkillebrew, version 1

Comments

No comments yet.