Sunday, July 29, 2007

ActiveRecord Inheritence

I finally got some time to sit down and spend a quick 30 minutes to an hour messing around with ActiveRecord's single table inheritance and I have to say that I was sadly disappointed. Here's what I tried ...

Keep in mind that I still need to do some research on this, because I didn't get around to actually testing out a couple ideas, but ... I will post a follow up when I get a chance.

I created two simple tables that consisted of:

User
id:int
first_name:string
last_name:string
type:string

Employee
id:int
user_id:string
salary:string


As wonderful as ActiveRecord is I was hoping that I would be able to do something as simple as:

class User <>ruby script\console and typed in ...

employee = Employee.create( :first_name => "Bryan", :last_name => "Ray" )


I then went to my database and was pleasantly surprised when I did a SELECT * FROM users at first! Hey look ... Rails automatically found that my "type" was an "Employee". Cool! So, I thought ... now I should be able to do something like ...

emp = Employee.find(1)

and Rails would load me up an Employee object so that I could set the salary. Unfortunately, this wasn't the case. After doing the above, I had full access to the emp.first_name and emp.last_name, but it did not recognize emp.salary as valid.

I probably need to do a little more research on exactly how to accomplish this ... if anyone has any suggestions I would love to hear them.

Monday, April 16, 2007

XML Driven Menu Strip

XML Driven Menu Strip

Contents

Introduction 1

Why an XML Driven Menu Strip? 1

Pros and Cons 1

Points of Interest 2

Things to Notice 2

Article 2

The XML Structure 2

Valid XML Attributes 3

MenuStripEnhanced Control 4

Possible Future Enhancements 6

Revision History 6

Conclusion 6


Introduction

In this article, I'm going to show how to generate a dynamic MenuStrip (.NET 2.0's replacement of the MainMenu control) using recursion in C#. The MenuStrip will be based on XML data which contains all the details about the generated Menu. Things like: Caption, Name, Text, and the corresponding Event it has to trigger when a user clicks a MenuItem.

This is a generalized re-usable control written in such a way that it can be easily plugged in to any application based on the applications requirements.

Why an XML Driven Menu Strip?

The only thing I can come up with to really answer this is … "why not?" I mean, .NET 3.0 will pretty much make this control out-dated with WPF and XAML, but in the meantime why shouldn't I be able to supply a path to an XML configuration file to setup my MenuStrip control and have it generated at run time as opposed to hard coding all the ToolStripMenuItem values at design time.

Honestly, I've battled with the concept of an XML driven Menu and while I'm still not fully convinced that it would be 100% useful in all situations … I can see how it would be useful in some applications.

The company that I work for has multiple clients that run our application. Each customer puts more focus on different areas of the application than the other customers; which is perfectly understandable. As many developers know, customers tend to want different things out of the application. This typically controls how dynamic an application should be as well as how customizable an application should be. This is where I originally got the idea for the XML Driven Menu Strip.

Pros and Cons

Pros of a XML Driven Menu

  • Adding and Removing ToolStripMenuItem does not require an application rebuild.
  • Customers, end-users, developers, etc can rename MenuItem in the XML configuration file to better suit their needs or to give a MenuItem more "meaning" in their environment.
  • A decent way to get new developers started on the learning curve for the application.

Cons of an XML Driven Menu

  • Customers, End Users, Developers, etc have the ability to rename their MenuItem element's at any time. This could become confusing if someone renames the "File > Exit" MenuItem to "File > Open."
  • Supporting the application becomes a bit more difficult as Support no longer has the ability to say for certain where to go in the Menu structure as their "File" could be easily renamed to "Option1" (just an example).

Of course, I'm sure there are many more pros and cons that you could come up with, but those are just a few that I came up with off the top of my head.

Points of Interest

One main thing here is that this control uses the new, relatively speaking, .NET 2.0 MenuStrip control and not the .NET 1.1 MainMenu control. There are some noticeable differences in the two. For a more detailed explanation check out Jim Hollenhorst's CodeProject article "Upgrading from MainMenu and ToolBar to MenuStrip and ToolStrip."

Things to Note

Some other interesting things that the article illustrates.

Article

The main goal for this control is to generate an applications Menu via a MenuStrip control at runtime based on values provided in an XML configuration file.

The XML Structure

<root>

<TopLevelMenu
ID="&amp;File">

<MenuItem
ID="New"
OnClick="_New" />

<MenuItem
ID="Open"
OnClick="_Open" />

<MenuItem
ID="-" />

<MenuItem
ID="Close"
OnClick="_Close" />

<MenuItem
ID="-" />

<MenuItem
ID="E&amp;xit"
OnClick="_Exit" />

</TopLevelMenu>


<TopLevelMenu
ID="&amp;Edit">

<MenuItem
ID="Undo"
OnClick="_Undo" />

<MenuItem
ID="-" />

<MenuItem
ID="Cut"
OnClick="_Cut" />

<MenuItem
ID="Copy"
OnClick="_Copy" />

<MenuItem
ID="Paste"
OnClick="_Paste" />

<MenuItem
ID="-" />

<MenuItem
ID="Options">

<MenuItem
ID="Sub Menu Item">

<MenuItem
ID="Sub Sub Menu Item" />

</MenuItem>

</MenuItem>

</TopLevelMenu>

</root>

There's really nothing too extreme about the XML structure. It uses a root element to encompass the TopLevelMenu elements. The application can support N number of TopLevelMenu elements. Under each of the TopLevelMenu element's are MenuItem elements which also have N-level support.

The XML list above will generate a simple Menu that appears in as:


Currently, the control doesn't have an XSD file, but perhaps in future revisions I'll get around to adding one for added functionality. The control only has a limited number of attributes at the moment, but in the near future it will attempt to provide many hooks in to all the ToolStripMenuItem events and properties.

Valid XML Attributes

Name

[required]

This is the .Name value that gets assigned to the ToolStripMenuItem.

Text

[required]

This is the .Text value that gets assigned to the ToolStripMenuItem.

OnClick

[optional]

This is the OnClick event handler that will be called when the ToolStripMenuItem is clicked.


The OnClick attribute is the most important attribute as it tells the application which event should be fired when a user clicks that specific MenuItem. Let's take a look at the "New" MenuItem in the above XML.

<MenuItem
ID="New"
OnClick="_New" />


This means that in the Form that this MenuStrip is being displayed it should have the following code to handle the Event.


private
void MenuItemOnClick_New( object sender, EventArgs e )

{


MessageBox.Show( "New Clicked" );

}

This event handler is based on the following format:

MenuItemOnClick

This is a hard-coded value in the control. It doesn't come from your XML configuration file.

_New

This is defined in the OnClick attribute. If you change the _New to _NewItem then your Form Event should also be changed as well.


The Dynamic Menu control exposes Event Handlers for each and every MenuItem element you have created with an attribute value of "OnClick." If you don't want to create a Event handler for a MenuItem then you don't need to specify the OnClick attribute for it in the XML file.

Now let's get in to the nitty gritty about the control…

MenuStripEnhanced Control

The MenuStripEnhanced control inherits from System.Windows.Forms.MenuStrip. So it provides all the original functionality of the standard MenuStrip as well as the added functionality for loading its data via an XML configuration file.

To draw the Menu items on the form at runtime I have overridden the OnPaint event of the MenuStrip control. I think check to see if the XML configuration file exists and make sure that the MenuStrip currently has no Items loaded in to it … indicating that it needs to populate itself.

protected
override
void OnPaint( PaintEventArgs e )

{

base.OnPaint( e );


if ( System.IO.File.Exists( XmlPath ) && this.Items.Count == 0 )

{

LoadDynamicMenu();

}

}

After I check to make sure each of the conditions have been met I call the private LoadDynamicMenu() function to begin parsing the XML configuration file.

private
MenuStrip LoadDynamicMenu()

{

XmlDocument document = new
XmlDocument();

document.Load( XmlPath );



XmlElement element = document.DocumentElement;



foreach ( XmlNode node in document.FirstChild.ChildNodes )

{

ToolStripMenuItem menuItem = new
ToolStripMenuItem();


menuItem.Name = node.Attributes["Name"].Value;

menuItem.Text = node.Attributes["Text"].Value;



this.Items.Add( menuItem );

GenerateMenusFromXML( node, (ToolStripMenuItem)this.Items[this.Items.Count - 1] );

}


return
this;

}

This code is fairly straight forward. It loads up the XML configuration file in to an XmlDocument() object and walks through all the TopLevelMenu element's. It will then pass in the current xml node as well as the ToolStripMenuItem in to the GenerateMenusFromXML( XmlNode rootNode, ToolStripMenuItem menuItem ) function. The GenerateMenusFromXML() is a recursive function that will parse N-level MenuItem's.

private
void GenerateMenusFromXML( XmlNode rootNode, ToolStripMenuItem menuItem )

{

ToolStripItem item = null;

ToolStripSeparator separator = null;


foreach ( XmlNode node in rootNode.ChildNodes )

{

if ( node.Attributes["Text"].Value == "-" )

{

separator = new
ToolStripSeparator();


menuItem.DropDownItems.Add( separator );

}

else

{

item = new
ToolStripMenuItem();

item.Name = node.Attributes["Name"].Value;

item.Text = node.Attributes["Text"].Value;


menuItem.DropDownItems.Add( item );


if ( node.Attributes["FormLocation"] != null )

item.Tag = node.Attributes["FormLocation"].Value;


// add an event handler to the menu item added

if ( node.Attributes["OnClick"] != null )

{

FindEventsByName( item, this.Form, true, "MenuItemOn", node.Attributes["OnClick"].Value );

}


GenerateMenusFromXML( node, (ToolStripMenuItem)menuItem.DropDownItems[menuItem.DropDownItems.Count - 1] );

}

}

}

So far the code has been fairly straight forward, but things get a bit more interesting when the GenerateMenusFromXML() finds an OnClick attribute on a MenuItem element.

// add an event handler to the menu item added

if ( node.Attributes["OnClick"] != null )

{

FindEventsByName( item, this.Form, true, "MenuItemOn", node.Attributes["OnClick"].Value );

}


The FindEventsByName() uses reflection to search the passed in receiver object, which is actually the Form that the MenuStripEnhanced control is attached to, to find the Event Handler that was provided in the value of the OnClick attribute.

Possible Future Enhancements

  • Add Icon attribute to support MenuItem element.
  • Add more Event attributes to the XML configuration.
  • Add support for encrypting the XML file so that it cannot be changed by just anyone.
  • Add XSD support for XML Configuration file.

Revision History

Monday, April 16, 2007

  • Initial Release

Conclusion

Here's your chance to really freeform it. What additional information do you have that didn't fit anywhere else? Are you going to continue working on your topic? Maybe a sneak peek at what you're thinking about writing next. What sort of feedback interests you?