Adding Custom Exception Handling Attributes to Your Asp.Net MVC Actions

Attributes are a simple way to run common handling across many actions without repeating yourself

I ran into a position where I was handling exceptions and pushing them to xml ContentResults the same way on multiple actions across many controller repeating a lot of code. I was working on a REST interface that always was to return xml. All Exception were to be returned in xml as well. I found myself writing a lot of actions like this:
public ContentResult Fidget()
{
    try
    {
        //do a bunch of stuff
        //throw exceptions if I need to
    }
    catch(Exception ex) //now catch them and display xml
    {
        // turn the exception into xml
        return this.Content(myXmlException, "text/xml");
    }
    
    // if it made it here, return success xml
    return this.Content(successXml, "text/xml");
}

This worked great, but after a bunch of actions, the code was very redundant. I want to handle exception the same on a lot of actions, but not all, so I decided to go to an Attribute, specifically ActionFilterAttribute and the IExceptionFilter. If I override the OnException, I can automatically handle the Exceptions in the painted actions, so this is what I came up with:
[HttpGet]
public class XmlExceptionAttribute : 
    ActionFilterAttribute, IExceptionFilter
{
    public void OnException(ExceptionContext 
        filterContext)
    {
        if (filterContext.Exception == null) return;
        filterContext.ExceptionHandled = true;
        var response = filterContext.Controller
            .ControllerContext.HttpContext.Response;
        response.ContentType = "text/xml";
        response.Write((new Status(
            filterContext.Exception)).ToXmlString());
    }
}

*Note that the Status object is a just an object I made to be serializeable and .ToXmlString() is an extension method I came up with a while back to convert objects into xml strings on the fly.

Now, if the attribute is applied, all I have to do for the above behavior in an action is simply this:
[HttpGet]
[XmlException]
public ContentResult Fidget()
{
    //do a bunch of stuff, throw exceptions if I need to
    
    // if it made it here, return success xml
    return this.Content(successXml, "text/xml");
}

The behavior is now the EXACT same!

Serializing and DeSerializing XML Objects in .Net

these two simple extension methods with have you switching between XML and objects in no time

First off, it is pretty well known that if you have any Object and want to convert it into an XML strong, you can use the XmlSerializer to do so. I encapsulated the process is a simple extension method that run from any object - this will work as long as the object has a constructor with no inputs (ie: new SomeObject()) as that is the constraint passed up from XmlSerializer. Here is the method for converting from an object to an xml string:
public static string ToXmlString(this Object o)
{
    StringWriter xml = new 
        StringWriter(new StringBuilder());
    XmlSerializer xS = new XmlSerializer(o.GetType());
    xS.Serialize(xml, o);
    return xml.ToString();
}

With this, now all you need to do is:
MyObject obj = new MyObject();
// a bunch of stuff here...
// now I want the xml representation of this:
string xml = obj.ToXmlString();

Now to go backwards, you can use the similar Deserialize along with the XmlSerializer with this method:
public static T XmlToObject<T>(this string s)
{
    var xR = XmlReader.Create(new 
        StringReader(s));
    XmlSerializer xS = new XmlSerializer(typeof(T)); 
    T obj = (T)xS.Deserialize(xR);
    return obj;
}

It is important to notice that this is taking in a raw xml string, which would not be web safe. If you were taking in data from a web source, you would want to employ HttpUtility.UrlDecode(s) instead of just s above. Now if you want to turn your above string 'xml' into an object again, simply call it like this:
MyObject obj2 = xml.XmlToObject<SomeObject>();

ServerInfo - Easily scan a Machine/Server Farm for Information

new open-source project in Asp.Net MVC 2

More than once I have been asked what databases are on what server, if server X is running application Y or what websites server ABC is running. These are are relatively simple things to accomplish, and there are tools available to get this information, but this is an extremely simple and portable solution - all the data is kept in xml, so there is no need to install a backend.

All that it is required to get all this information is to enter ip addresses and the user has the rights to scan the machines requested.

In addition, this can keep track of all of the owners of the machines and has a GUI for running WMI Queries, which is extremely powerful if you know how to use it.

This is written with Asp.Net MVC 2, C# and xml; it requires .Net 4.0 framework. I will be updating this to MVC 3/Razor in the near future.

Keeping all your settings in an XML file in a centralized location

Often times you want to centralize some key data, and avoid a DB call

Most of my applications use a settings.xml file where keep all of my settings. I then use a Settings.cs class to view/update those values. It is great for centralizing data while avoiding unnecessary trips to the DB; change once, it's changed everywhere.

One of the most common things I use in my settings class is for my email settings, just in case I want to change the account or smtp that I am using, here is a sample of what one would look like:
<?xml version="1.0" encoding="utf-8" ?>
<settings>
  <smtp>smtp.gmail.com</smtp>
  <ssl>True</ssl>
  <mail_account>demo@naspinski.net</mail_account>
  <mail_password>Iparty</mail_password>
</settings>

Pretty basic stuff there, now here is the Settings.cs class I use to access it using a little Linq-to-XML:
using System.Linq;
using System.Web;
using System.Xml.Linq;

public static class Settings
{
  public static string Get(string setting)
  { // gets the specified setting
    XElement x = XElement.Load(HttpContext.Current.Server.MapPath("~") + "\\App_Data\\settings.xml");
    return (from p in x.Descendants(setting) select p).First().Value;
  }

  public static void Update(string setting, string value)
  { // changes the specified setting
    string file_location = HttpContext.Current.Server.MapPath("~") + "\\App_Data\\settings.xml";
    XElement x = XElement.Load(file_location);
    XElement xe = (from p in x.Descendants(setting) select p).First();
    xe.Value = value;
    x.Save(file_location);
  }
}

As you can see, I keep my settings in the App_Data folder. Here you can see how I use it in some apps, for example, this is how I use it with my Email.cs class:
public Email(string to, string from, string subject, string body) : base(Settings.Get("smtp"), 587)
{
  this.Credentials = new System.Net.NetworkCredential(Settings.Get("mail_account"), Settings.Get("mail_password"));
  ...

I think you can figure out Settings.Update() as it is basically the same. You can use whatever method to encrypt your data whereever you keep it.


GUI css writer: change your page style and rewrite your css on the fly

I have seen a lot of theme pickers out there, but not one that gives you 100% control like this

While working on a recent project, I had a complaint about the colors... I hate colors and I am no good at picking them, and when I do find some colors that I think are nice, apparently everyone else thinks they are crap.  This is why I truly respect web designers, as that is a true skill.  But to my chagrin I do not have a web designer, so I did the next best thing: get the blame off my back.  That's right, if you think you are so good with colors, why don't you pick them Mr. User?

 

So I went ahead and used the Farbtastic jQuery color picker Plug-in that I recently fell in love with, along with some nice Linq, XML, some good old .Net IO and a dash of clever css writing, and I was able to put together a (almost) fool-proof theme-designer with a friendly GUI.

 

First thing first, I needed both jQuery and the Farbtastic jQuery color picker Plug-in I just mentioned to be downloaded and called in my <head>.  Once I ran the Farbtastic demo to make sure everything was working fine, I set out on my adventure.

 

Next, I had to seperate my css into two distinct sheets, one: main_style.css would be jsut that, the main style; this never changes and is static.  Then, I made another stylesheet: theme.css that will hold my changing data and be re-written.  By using some clever techniques like not relying on any css border properties, I will be able to minimize the amount of code i have to re-write and be able to produce what looks like borders without actually using borders.  So instead of

/*theme.css*/
div.border { border: solid 5px Black; }

<!--html-->
<div class="border">some stuff with a border</div>

 

I will instead use something like this:

/*main_style.css*/
.border { padding:5px; }

/*theme.css*/
.border_color { background-color:Black; }
.content_color { background:White; }

<!--html-->
<div class="border border_color">
  <div class="inner_color">some stuff with a border</div>
</div>

 

These both produce what looks to be the exact same, the difference being that the second one does not actually have a border, it just looks like it with the padding and background color.  Now I know that the second one is more code, but that is the price you pay to have less code in your css and have your website be maintainable.  Once you write teh css writer one time, you will never have to touch the code again.  You can easily incorporate borders into the re-written css if you would like, that is just not the path I am going.

 

Now that that is understood, I can put in the color picker and the related TextBox controls to correspond to what colors I will be changing.  To do that, simply put this isnot your page where you want the picker: <div id="picker"></div> and then add your TextBox controls where you need them; each TextBox is the class 'colorwell' that allows them to be accessed easier by jQuery.  Once that is done, add a Button to submit everything.  So far, mine looks like the picture at the top of the post.  You will have to bind everything with jQuery now:

[code:js]

 <script type="text/javascript" charset="utf-8">
     $(document).ready(function() {
         var f = $.farbtastic('#picker');
         var p = $('#picker').css('opacity', 0.25);
         var selected;
         $('.colorwell')
      .each(function() { f.linkTo(this); $(this).css('opacity', 0.75); })
      .focus(function() {
          if (selected) {
              $(selected).css('opacity', 0.75).removeClass('colorwell-selected');
          }
          f.linkTo(this);
          p.css('opacity', 1);
          $(selected = this).css('opacity', 1).addClass('colorwell-selected');
      });
     });
 </script>

[/code]

 

Now comes the code-behind.  First thing I decided is that I am going to hold the data in an xml file; you could easily do this in a SQL database as well.  I named the file theme.xml and it looks like this:

<?xml version="1.0" encoding="utf-8"?>
<theme>
  <text>#444444</text>
  <borders>#4a4a4a</borders>
  <body>#ffffff</body>
  <links>#ff7700</links>
  <link_hover>#ffa500</link_hover>
  <button_text>#ffffff</button_text>
  <headers>#ffffff</headers>
  <background>#a53c3c</background>
</theme>

 

I populated it with my default color values of what will be changing.  Next, I made a class that will help me interact with the xml using Linq, I named this file xmlHelper.cs and placed it in my App_Code folder:

public static XElement getElement(XElement x, string element)
{ return (from e in x.Descendants(element) select e).First(); }

public static void writeElement(XElement x, string element, string value)
{
    XElement xe = getElement(x, element);
    xe.Value = value;
}

 

Now in my Page_Load I made a Dictionary<string, TextBox> and populated it with all of my TextBox controls and their corresponding xml Element name.  Now each time the page loads fresh, I simply load my values into the TextBox controls like this:

if (!IsPostBack)
{
    foreach (var v in colors)
        v.Value.Text = xmlHelper.getElement(x, v.Key).Value;
}

 

Now every time the page loads, it pulls the values out of the xml file and populates the TextBox controls.  Now that I have that, I need a way to write to the css file and the xml file.  A iadd that to my btnSubmit_Click event.  Once again, I can look through the Dictionary I had set up to this time use my xmlHelper class and write to the xml file.  That is done like this:

protected void btnSubmit_Click(object sender, EventArgs e)
{
    foreach (var v in colors) xmlHelper.writeElement(x, v.Key, v.Value.Text);
    x.Save(themePath);
    //writeCss(); //this will be used in a moment
}

 

Now the xml is written, we need to write the css.  For this I just use the old IO and StringBuilder to write my css and overwrite the old file:

protected void writeCss()
{
    string cssThemeFile = Server.MapPath("~") + "\\css\\theme.css";
    StringBuilder sb = new StringBuilder();
    sb.Append("html{background-color:" + txtBg.Text + ";}");
    sb.Append(".border_color{ background-color:" + txtBorders.Text + ";}");
    sb.Append(".content_color{ background-color:" + txtBody.Text + ";}");
    sb.Append(".header_text{color:" + txtHeader.Text + ";}");
    sb.Append("a{color:" + txtLink.Text + ";}");
    sb.Append("a:hover{color:" + txtLinkHover.Text + ";}");
    sb.Append(".button:hover{background-color:" + txtLinkHover.Text + ";}");
    sb.Append(".button{background:" + txtLink.Text + ";color:" + txtButtonText.Text + ";}");
    File.Delete(cssThemeFile);
    TextWriter tw = new StreamWriter(cssThemeFile);
    tw.WriteLine(sb.ToString());
    tw.Close();
}

 

That will write my theme.css file, and that is that.  Now on every click, the values will be written to the xml file (this is unneccessary, but it loads the previous settings up for your user).  After that, the css file is re-written and immediately used by your page.  This does require that you have write permissions to whatever directory/directories you house your theme.css and theme.xml files in.  This also is easy to implement with each user profile having their own saved style (easier with SQL).  I hope this comes in handy for someone, I love it and have used it on a few projects now.

 

...And now they see it's not soo easy picking colors that actually look good :)

 

 

Record Search with LINQ: searching just got a lot easier!

A suprisingly simple search technique that will work for SQL tables, XML files, DataTables, etc.

Linq is just awesome.  I was recently asked a question on how to search through a data file with linq.  At first it seemed difficult, but after some though and fighting my way through the strange quirks, I realized how much easier Linq could [once again] make my life.

 

Here is how it's done (this article uses XML, but I have done the same exact thing with both SQL and DataTables, the concept is the same.)

 

The original question stirred from how to search through a bunch of recipes.  Each recipe (XML elements bolded) has a name, type (chicken, vegetable, etc.), calories that it contained, the amount of people that the recipe serves, and the instructions on how to make it.  Here is the dummy XML:


<?xml version="1.0" encoding="utf-8" ?>
<recipes>
    <recipe>
        <name>Hamburger</name>
        <serves>1</serves>
        <calories>500</calories>
        <type>Beef</type>
        <instructions>throw some crap together and cook it</instructions>
    </recipe>
    <recipe>
        <name>Chicken Nuggets</name>
        <serves>4</serves>
        <calories>400</calories>
        <type>Chicken</type>
        <instructions>throw some crap together and cook it</instructions>
    </recipe>
    <recipe>
        <name>Lasagna</name>
        <serves>8</serves>
        <calories>800</calories>
        <type>Pasta</type>
        <instructions>throw some crap together and cook it</instructions>
    </recipe>
    <recipe>
        <name>Ribeye</name>
        <serves>1</serves>
        <calories>600</calories>
        <type>Beef</type>
        <instructions>throw some crap together and cook it</instructions>
    </recipe>
    <recipe>
        <name>Drumsticks</name>
        <serves>3</serves>
        <calories>700</calories>
        <type>Chicken</type>
        <instructions>throw some crap together and cook it</instructions>
    </recipe>
    <recipe>
        <name>Beef Kabobs</name>
        <serves>4</serves>
        <calories>350</calories>
        <type>Beef</type>
        <instructions>throw some crap together and cook it</instructions>
    </recipe>
    <recipe>
        <name>Green Beans</name>
        <serves>5</serves>
        <calories>50</calories>
        <type>Vegetables</type>
        <instructions>throw some crap together and cook it</instructions>
    </recipe>
</recipes>

 

Now that we know our data structure, we can figure outhow to set up this search.  I am going to show a few different approaches so you can pick and choose which ones to use.  I am going to cover keyword search, number range search and specific text search.

 

First we have name search.  For that I am going to look for keywords.  I will allow users to enter none or as many as they want (to be more specific) into a TextBox.  These will be delimited with the standard space( ), comma(,) and semicolon(;).

 

Next there is type and serves which I am going to set to DropDownLists.  I am going to do this because not all numbers/words will be supported, I feel it is best to guide the user along with this and only supply available search possibilities, with 'any' always being the first option.

 

Finally there is the calories range.   For this, I will use two TextBoxes that will take in numbers of course; a minimum and a maximum.

 

Here is what the search interface looks like:

 

Here is the markup:

 

<fieldset>
    <legend>Search Recipes</legend>
    <ul>
        <li>
            <label for="name">Name</label>
            <asp:TextBox runat="server" ID="txtName" />
        </li>
        <li>
            <label for="type">Type</label>
            <asp:DropDownList ID="ddlType" runat="server" />
        </li>
       
        <li>
            <label for="serves">Serves:</label>
            <asp:DropDownList ID="ddlServes" runat="server" />
        </li>

        <li>
            <label for="calories">Calories</label>
            <asp:TextBox ID="txtCalMin" runat="server" Columns="3" />
            <span style="float:left"> to </span>
            <asp:TextBox ID="txtCalMax" runat="server" Columns="3" />
        </li>

        <li>
            <asp:Button ID="btnSearch" runat="server" Text="Search"
                onclick="btnSearch_Click" />
        </li>
    </ul>
</fieldset>

 

Now with that out of the way, we can start with the code.  First a global XElement x; and IEnumerable<XElement> filteredResults; has to be declared, x will be used within the program and initialized on Page_Load, filteredResults will be explained later.  After that is initialized, on a fresh page load (!IsPostBack)the DropDownLists must be populated:

 

protected void Page_Load(object sender, EventArgs e)
{
    x = XElement.Load(Server.MapPath(".") + "\\App_Data\\recipes.xml");
    if(!IsPostBack)
    {
        // get all of your different food 'types' and put them in the ddl
        ddlType.Items.Add("any");
        foreach(string s in ((from p in x.Descendants("type") orderby p.Value select p.Value).Distinct()))
            ddlType.Items.Add(s);
        ddlServes.Items.Add("any");
        foreach (string s in (from p in x.Descendants("serves") orderby p.Value select p.Value).Distinct())
            ddlServes.Items.Add(s);
    }
}

 

Now that the form is all ready,it is time to delve in to the actualy search.  Now keep in mind, the Linq syntax will be a bit differen, but the methods are teh EXACT same when you are working with another data type (SQL, DataTable, etc.)  After a lot of deliberation on how to do this, I decided to split it up in to two seperate parts.  First filter the elements using all the filters EXCEPT the keywords (name), then apply the keyword search.  The reason I am doing it this way is because that search could include zero terms, or 5,000; therefore iteration makes the least amount of work.  Also, for ease of use, the search will workregardless of what a user enters.  By default, the search will return everything, then narrow it down as users select more criteria.  For this I will make it so the default min/max calories are 0/9999 respectively (I will ignore any input that isn't integers) and make sure to ignore the type and  serves if 'any' is selected.  Here is the code for search, I will explain it afterwards:

 

protected void btnSearch_Click(object sender, EventArgs e)
{
    try
    {
        string[] searchTerms = txtName.Text.Split(new string[] {" ", ",", ";"}, StringSplitOptions.RemoveEmptyEntries); //gets all your search terms
        int calMin = 0;
        int calMax = 9999;
        try { calMin = int.Parse(txtCalMin.Text); }catch { }
        try { calMax = int.Parse(txtCalMax.Text); }catch { }
        if (calMin > calMax) Response.Write("<h2 style=\"color:red;\">Error: Calories<div style=\"font-size:.5em;\">Minimum can not be larger than maximum</div></h2>");
      
        var searchResults = from p in x.Descendants("recipe") // filters everything by all of the fields except Name
                        where
                            (ddlType.SelectedIndex == 0 ? true : p.Element("type").Value.Equals(ddlType.SelectedValue.ToString())) &&
                            (ddlServes.SelectedIndex == 0 ? true : p.Element("serves").Value.Equals(ddlServes.SelectedValue)) &&
                            (int.Parse(p.Element("calories").Value) >= calMin) &&
                            (int.Parse(p.Element("calories").Value) <= calMax)
                    select p;

        foreach (string s in searchTerms) // since name can be multiple words, this iterates through them all, making sure that all of the terms are present
            searchResults = from p in searchResults where p.Element("name").Value.ToLower().Contains(s.ToLower()) select p;

        if (searchResults.Count() > 0)
        {
            // output your results
            foreach (XElement xe in searchResults)
                pnlOutput.Controls.Add(new LiteralControl("<a href=\"#" + xe.Element("name").Value + "\"><div><h3>" + xe.Element("name").Value + "</h3> Calories: " + xe.Element("calories").Value + "</div></a>"));
        }
        else
            pnlOutput.Controls.Add(new LiteralControl("No Entries match your search criteria"));
    }
    catch (Exception ex)
    {
        pnlOutput.Controls.Add(new LiteralControl("<h3>Error</h3>"+ex.Message));
    }
    pnlOutput.Visible = true;
}

 

First off, you can see that the searchTerms are made by splitting the input into an array, pretty simple.  Next the min/max calories are set to defaults and attempted to be changed to the inputs, and will only be changed if there are valid inputs; it will output an error if minCal > maxCal.

 

The first leg of the search is pretty simple just return all descendants in the XML file of type recipe that follow the searhc criteria.  First I check the type, if the DropDownList is at SelectedIndex of 0 (which is the 'any' selection) I will return all, otherwise, jsut the ones that equal the selected type; I do the exact same for serves.  Then a simple check that returns those that calMin >= calories <=calMax.

 

That was not the part that confused me, it was how to get a dynamic number of search terms to be iterated through.  But this is where I cam up with the simplest of solutions.  Just search EACH term alone, and interate through it with Linq and a foreach... the beauty of the IEnumerable.  For each search term in searchTerms I simply run a new Linq statement that checks if the ameElement.Contains(that_search_term).  Therefore, every time this runs through, it will drop all entries that don't contain the term, each iteration [likely] returning fewer entries -- so simple!  But not really, for some reason (still not completely sure why), if I simply run:

 

foreach (string s in searchTerms)
    searchResults = from p in searchResults where p.Element("name").Value.ToLower().Contains(s.ToLower()) select p;

 

Does NOT work, it really only honors the last term; it is not overwriting searchResults every iteration.  After some testing, I had found out that I had to make a new variable (that's the filteredResult that we declared above) that would instead take place of searchResults and then we can write over it.  But also, you may not do this inside the foreach loop itself, as that still only honors the last term.  BUT, if the method is taken out and placed seperately, the overwrite seems to work.  But NOT for searchResults, I have to use filteredResults.  If anyone understnad exactly why this extra step is necessary, pleae enlighten me!  Here is how it is called:

 

filteredResults = searchResults;
foreach (string s in searchTerms) // since name can be multiple words, this iterates through them all, making sure that all of the terms are present
    filteredResults = iterateThroughSearchTerm(s);

 

And here is the method:

 

protected IEnumerable<XElement> iterateThroughSearchTerm(string term)
{
    return (from p in filteredResults where p.Element("name").Value.ToLower().Contains(term.ToLower()) select p);
}

 

All that is left is to simply output your findings, an error message or a 'sorry, nothing found' message.  And there you go, a bunch of different search approaches all covered at once.  Here is some example code to get you started.

 

 





 

XML to DataTable with LINQ

Easy way to get your XML into a DataTable

Now I might just be blind, or incredibly incapable at searching google or reading my LINQ books (very possible) but I hadn't found a simple way to get a 2-level XML document into a DataTable for use in a GridView or just simple DataTable manipulation utilizing LINQ (I assume this is because DTs are 2 dimensional, and XML files can be all sorts of mash-ups of information). Since LINQ is so powerful, I assumed it wouldn't be all that difficult, turns out I was right, it's pretty easy. Here is the code:

[code:c#]

public DataTable XElementToDataTable(XElement x)
{
  DataTable dt = new DataTable();

  XElement setup = (from p in x.Descendants() select p).First();
  foreach (XElement xe in setup.Descendants()) // build your DataTable
    dt.Columns.Add(new DataColumn(xe.Name.ToString(), typeof(string))); // add columns to your dt

  var all = from p in x.Descendants(setup.Name.ToString()) select p;
  foreach (XElement xe in all)
  {
    DataRow dr = dt.NewRow();
    foreach (XElement xe2 in xe.Descendants())
      dr[xe2.Name.ToString()] = xe2.Value; //add in the values
    dt.Rows.Add(dr);
  }
  return dt;
}

[/code]


This is completely dynamic, so it doesn't matter what or how many elements you have. It does rely on the first element to set the DataTable columns, so make sure that one is complete. Though the XML does have to be limited to 2-dimensional elements; in fact, I am not even sure what happens if you feed the function bad data? The XML should resemble this structure:

<?xml version="1.0" encoding="utf-8"?>
<root>
      <person>
            <age>26</age> 
            <name>stan</name> 
            <hobbies>partying</hobbies> 
      </person>
      <person>
            <age>26</age> 
            <name>matt</name> 
            <hobbies>being lame</hobbies> 
      </person>
</root>

In that structure, each person will be a row, and age, name and hobbies will the the columns in the datatable:
agenamehobbies
26 stan partying
26 matt being lame
Call it like this:

[code:c#]

// load your xml file (this one is named people and it is in my App_Data folder)
XElement x = XElement.Load(Server.MapPath(".") + "\\App_Data\\people.xml");//get your file
// declare a new DataTable and pass your XElement to it
DataTable dt = XElementToDataTable(x);

[/code]


And that's it, you have your DataTable.


Cascading DropDowns using XML and LINQ

A simple alternative method to the Ajax Control Toolkit's

Don't get me wrong, the Ajax Control Toolkit (ACT) is great, but a couple of the controls leave something to be desired.  For one, the things required to integrate the cascading dropdowns is a bit excessive IMO.  No need ot require asmx files to do a simple lookup.  Also, if you are changing values in your dropdowns (as I needed to do), this gives you no simple way to do it.

 

Figuring that dropdowns are almost always relatively small collections of choices, and I would be editing them often for my purposes, an XML files seems to be the perfect fit for this situation.  On top of that, with the ease of LINQ, this would be extremely easy to make an interface for editing the properties so my users could edit them without knowing the first thing about XML. 

 

I am going to try to basically copy the ACT in terms of functionality as I really like it, but change it 'under the hood'.  First thing is to make a simple XML file with my options in it, I used old cars for an example:

<?xml version="1.0" encoding="utf-8" ?>
<dropdowns>
    <makes>
        <maker>-please select-</maker>
        <maker>Ford</maker>
        <maker>Chevy</maker>
        <maker>Dodge</maker>
    </makes>
    <cars>
        <car_maker name="Ford">
            <car>Bronco</car>
            <car>Galaxy</car>
            <car>Mustang</car>
        </car_maker>
        <car_maker name="Chevy">
            <car>Chevelle</car>
            <car>Corvette</car>
            <car>Nova</car>
        </car_maker>
        <car_maker name="Dodge">
            <car>Charger</car>
            <car>Ram</car>
        </car_maker>
    </cars>
</dropdowns>

 Now add the Dropdowns to yoru .aspx with a few more properies added (required for this to work) the properties are:

  1. cascadeTo : The dropDown you want to cascade to
  2. cascadeCategory : The category in XML your cascade properties lie
  3. cascadeDescendant : The final level in XML where your items lie
  4. cascadeBlank : [optional] What the cascading DropDown will display when it is awaiting a selection


Here is my .aspx, Notice I added "- Select Make -" to the ddlModel DropDownList as that will help give the user some guidance and it is the same as the 'cascadeBlank' I designated:

Make:<br />
<asp:DropDownList ID="ddlMake"  runat="server" cascadeTo="ddlModel" cascadeCategory="car_maker" cascadeDescendant="car" cascadeBlank="- Select Make -"  AutoPostBack="true" onselectedindexchanged="ddlMake_SelectedIndexChanged" />
<br /><br />
Model:<br />
<asp:DropDownList ID="ddlModel" runat="server" Enabled="false"  >
    <asp:ListItem>- Select Make -</asp:ListItem>
</asp:DropDownList>

Next, you need to access your XML document and fill your initial menu, so make sure to add something like this to your code-behind:

XElement x;
protected void Page_Load(object sender, EventArgs e)
{
    x = XElement.Load(Server.MapPath(".") + "\\App_Data\\dropdowns.xml");
    if (!IsPostBack)
        foreach (XElement elem in x.Descendants("maker")) addDdlItems(ddlMake, elem.Value);
}

public void addDdlItems(DropDownList ddl, string value)
{
    ListItem li = new ListItem();
    li.Value = value;
    ddl.Items.Add(li);
}

Now you simply need to call cascade.fromThisDropDown from your _SelectedIndexChange event.  This way you don't need to write any code, just place the cascade.cs fil into your App_Code folder:

protected void ddlMake_SelectedIndexChanged(object sender, EventArgs e)
 { cascade.fromThisDropDown(this.Page, (DropDownList)sender, x); }

And that's it.  You can place it inside an UpdatePanel for the same exact Ajax effect that you have with the ACT, but without those pesky asmx files. If you want to see the cascade.cs code, click here to view/hide.

 

Not completely dummy proof, but I think it's useful.  You can download just the cascade class, or the full working example:






 

Errors when publishing an open social google gadget

Simple fixes to simple errors when just starting out with open social widgets 

Just made my first basic open social gadget for Daily Atheist Quote and ran into an error which I couldn't find any documentation (I didn't really look all that hard).  Basically I tried to publish my first widget from the Google Scratchpad and I got this warning:

Please consider fixing the following issues below before publishing your gadget:

    * Author attribute is missing or undefined.
    * Author email attribute is invalid or undefined.
    * Height attribute must be defined.
    * Width attribute must be defined.
    * Description attribute is missing or undefined.
    * Thumbnail attribute must be defined and set to a valid image URL.

Couldn't figure out where this stuff actually went.  Turns out after messing around, it really was very simple and I just had to stick that info into the ModulePrefs:

 

[code:xml]

<ModulePrefs title="Daily Atheist Quote" description="Rotates Atheist Quotes Daily"
    title_url="http://dailyatheistquote.com" author="Stan Naspinski"
    thumbnail="http://dailyatheistquote.com/images/thumb.jpg"
    author_email="mrstan@gmail.com" height="200" width="300" >


[/code]

 

And that is it, your Widget should now validate through the Scratchpad Publishing just fine.