Blogengine.Net widget howto, using singletons

by René 12. June 2011 22:47

Since today I posted my Facebook widget on this site(turned out it actually was the second, but oh well - mine is better Wink - not meant to be rude rtur, I've seen and appreciate the work you did for Blogengine.net) I decided to elaborate a bit on the design I picked for implementing it. My main concern was to keep all code for working with the settings in one place, not spread out over multiple classes. See the point with Blogengine.Net widgets is that there are (obviously) specifications:

  1. There may only two controls, widget and edit, and they live in the same folder
  2. Those controls use code behind files, so total is 4 files:widget.ascx, widget.ascx.cs, edit.widget.ascx, edit.ascx.cs
  3. The Widget Class must extend WidgetBase and override the Name and isEditable properties, and the LoadWidget method
  4. The Edit class must extend WidgetEditBase and override Save method

That's it in a nutshell. I won't elaborate more since this is all documented on the Blogengine.net site. Now add the following points:

  1. It deviates from the normal way of doing things to add more classes/folders to other locations - who needs change anyway?
  2. It's bad practise to put stand alone class files outside the App_Code folder

Still, I want an extra class for the data, and put it neatly in place...now what?

My solution

That's right, I made a singleton class FaceBookData, and nested it in the Edit class. Now to make the FaceBookData be able to access the GetSettings() method, I put the behavior in an interface that I nested in the same namespace as my Widget and Edit classes:

 

    /// <summary>
    /// Interface for the GetSettings() behavior of both WidgetEditBase and    WidgetBase
    /// </summary>
    public interface IFaceBookData
    {
        StringDictionary GetSettings();
    }

Now the data class can use the method, no matter which method calls the singleton "getInstance" method first. For the save method no interface is needed, since only WidgetEditBase has a SaveSettings() method. One important thing not to forget is to add a reference in the widget.ascx f

<%@ Control Language="C#" AutoEventWireup="true" 
CodeFile="Widget.ascx.cs" Inherits="Widgets.FaceBook.Widget" %> 
<%@ Reference Control="~/widgets/Facebook/Edit.ascx" %>


And that's it! You have your very own singleton data loading/saving class for your widget!

Very nice Rene, but what on earth is a singleton?

Ah valid question I guess. A singleton is a class of which only one instance can be alive at each and every moment. So they're a bit like Elvis. There can only be one of them. Besides, who wants two Elvisses?

What are they used for? Well if you really wanna be darn sure there is only one instance of the to be singleton class lingering around in your application. Think of loggers, file managers, database connectors...etc.

How to make one? The standard way of doing this is by creating a private field of the classes type, set the constructor to private, and make some getInstance() method.(quite foolproof, but can be broken using reflection, I won't cover that here) Example following the Elvis idea:

 

public class Elvis
{
    private static Elvis instance = null;//here we store Elvis
    private Elvis()//nobody except this class can call the constructor now!
	{
		//your constructor logic
	}
    public static Elvis GetInstance()//static, so it can be called without an instance reference
    {
        if (instance == null)//if Elvis isn't here
        {
            instance = new Elvis();//then GET him
        }
        return instance;//return Elvis
    }
}

 

The reason in this case why you always want one, is to make really really sure both Widget and Edit are reading from the same object.

Now in the case of this widget, it looks like this, there's a whole lot of stuff added, but in basics it does the same!

 

public class FaceBookSettings
        {
            #region Properties
            private static FaceBookSettings instance = null;
            private IFaceBookData source;
            /// <summary>
            /// The URL to the Facebook page
            /// </summary>
            public string FacebookURL { get; set; }
            /// <summary>
            /// Width of the box in pixels
            /// </summary>
            public string Width { get; set; }
            /// <summary>
            /// Height of the box in pixels
            /// </summary>
            public string Height { get; set; }
            /// <summary>
            /// Colorscheme to use(light/dark)
            /// </summary>
            public string ColorScheme { get; set; }
            /// <summary>
            /// HTML color for the border(optional)
            /// </summary>
            public string BorderColor { get; set; }
            /// <summary>
            /// boolean value to show small icons of people who liked
            /// </summary>
            public string ShowFaces { get; set; }
            /// <summary>
            /// boolean value to show the stream of comments on the wall of the page
            /// </summary>
            public string ShowStream { get; set; }
            /// <summary>
            /// boolean value to show the header
            /// </summary>
            public string ShowHeader { get; set; }
            #endregion
            #region private constructor
            private FaceBookSettings(IFaceBookData source)
            {
                this.source = source;
                this.LoadSettings();
            }
            #endregion
            #region public methods
            /// <summary>
            /// Gets the singleton instance of FaceBookSettings
            /// </summary>
            /// <param name="source">IFaceBookData element that called the method(probably a this ref)</param>
            /// <returns>The singleton instance of settings are loaded and returned</returns>
            public static FaceBookSettings getSettings(IFaceBookData source)
            {
                if (instance == null)
                {
                    instance = new FaceBookSettings(source);
                }
                return instance;
            }

            /// <summary>
            /// Saves the settings
            /// <remarks>Only abstract class WidgetEditBase contains a SaveSettings method, hence this method
            /// can only be called by FaceBook.Edit</remarks>
            /// </summary>
            /// <param name="source">FaceBook.Edit caller(probably a this ref)</param>
            public void SaveSettings(FaceBook.Edit source)
            {
                var settings = source.GetSettings();

                settings["facebookurl"] = this.FacebookURL;
                settings["width"] = this.Width;
                settings["height"] = this.Height;
                settings["colorscheme"] = this.ColorScheme;
                settings["bordercolor"] = this.BorderColor;
                settings["showfaces"] = this.ShowFaces;
                settings["showstream"] = this.ShowStream;
                settings["showfaces"] = this.ShowHeader;
                source.SaveSettings(settings);
            }
            #endregion
            #region methods
            private void LoadSettings()
            {
                var settings = this.source.GetSettings();
                this.FacebookURL = (settings.ContainsKey("facebookurl")) ? settings["facebookurl"] : @"http://www.facebook.com/platform";
                this.Width = (settings.ContainsKey("width")) ? settings["width"] : "240";
                this.Height = (settings.ContainsKey("height")) ? settings["height"] : "427";
                this.ColorScheme = (settings.ContainsKey("colorScheme")) ? settings["colorScheme"] : "light";
                this.BorderColor = (settings.ContainsKey("borderColor")) ? settings["borderColor"] : "";
                this.ShowFaces = (settings.ContainsKey("showFaces")) ? settings["showFaces"] : "true";
                this.ShowStream = (settings.ContainsKey("showStream")) ? settings["showStream"] : "false";
                this.ShowHeader = (settings.ContainsKey("showHeader")) ? settings["showHeader"] : "true";
            }

If you got this far reading, congratulations! Full source code can be found over here!

 




Tags: , , , ,

Asp.Net | software engineering

Comments (2) -

6/17/2011 7:36:32 AM #

Orgreenic

Hey, i've been reading this blog for a while and have a question, maybe you can help... it's how do i add your feed to my rss reader as i want to follow you. Thanks.

Orgreenic Caribbean | Reply

6/18/2011 1:06:51 AM #

Rene

Hi, thank you very much for your interest. The RSS subscribe is in the top right menu bar, it's the orange RSS icon. Most modern browsers support RSS. Alternatively, you can subscribe by filling in your email in the bottom right menu bar, and you'll receive email notifications.

Rene Netherlands | Reply

Pingbacks and trackbacks (2)+

Add comment

biuquote
  • Comment
  • Preview
Loading

Many thanks to the people of BlogEngine.Net
for providing me with an extendable platform to play with, and to n30 for the base of my layout

About the author

This site is maintained by René van de Vorst, a friendly code, dog and food lover living in Eindhoven, the Netherlands.

coders revenge

Month List