Login | Register   
Twitter
RSS Feed
Download our iPhone app
TODAY'S HEADLINES  |   ARTICLE ARCHIVE  |   FORUMS  |   TIP BANK
Browse DevX
Sign up for e-mail newsletters from DevX


advertisement
 

Build an AJAX-Enabled CMS with Visual WebGUI: Adding Menu Components (III)

We continue our tutorial on building your own content management system.


advertisement
In the last two articles in the series I implemented the Model and the Controller parts for the menu system, and one implementation for the View part representing a Treeview-like menu. In this part I’ll show how to create a new implementation for menu visual component featuring an Outlook-like navigation pane. Finally, I will show how you can actually add a feature-rich, appealing menu to your Visual WebGui application, by using visual designer and only few lines of code.

Another implementation for IWTMenu interface is WTNavTreeMenu. It combines the Outlook-style navigation bar with tree menus to offer a different user experience. Class diagram is shown in Figure 1.

WTNavTreeMenu has as its core element NavigationTabs control, but unlike the WTTreeMenu, it is not inherited from NavigationTabs, but instead is inherited from UserControl (through WTModule), and hosts a docked NavigationTabs control. Unlike WTTreeMenu, it has one new property -- IsCollapsable, which controls IsCollapsable property of embeded NavigationTabs control. For more reference on WTModule, check the previous article in the series at http://www.devx.com/dotnet/Article/42449/.



The actual composite structure of WTNavTreeMenu is shown in Figure 2. The root control is a UserControl, inherited from WTModule. It contains a docked NavigationTabs control. Then NavigationTabs has several pages, each of type WTNavTreeMenuItem, which are derived from NavigationTab (the class implementing default page for NavigationTabs component). Finally, each WTNavTreeMenuItem contains a WTTreeMenu component. The menu items on the first level are created as navigation tabs, and the menu items starting from second depth level are created inside the embeded WTTreeMenu menus.

Figure 1: Class diagrams for WTNavTreeMenu and WTNavTreeMenuItem.


Figure 2: Composite structure for WTNavTreeMenu.

InitMenu method creates the menu. NavigationTabs control must always have at least one page, otherwise it will throw exception. The code saves a reference to first page, remove all other pages, setup the new tabs and finally remove the initially save tab. Finally, binds to event handler to detect when active page is changed, and activates the first page:

public void InitMenu(List<IWTMenuEntryDefinition> menuDefinition){  if (m_initTab == null)    m_initTab =navigationTabs.TabPages[0];  CleanupTabs();  SetupTabs(menuDefinition);  navigationTabs.TabPages.Remove(m_initTab);  navigationTabs.SelectedIndex = navigationTabs.TabPages.Count-1;  navigationTabs.SelectedIndexChanged +=      new EventHandler(navigationTabs_SelectedIndexChanged);  navigationTabs.SelectedIndex = 0; }

SetupTabs method is the main "engine" for generating the menu. For each menu entry definition on the first level, a new menu item of type WTNavTreemenuItem is created, and added to TabPages collection. Event MenuItemAdded is fired. Then, if menu has submenus, a WTTreeMenu is created and populated with all menu items on current branch starting with depth level two. The MenuItemAdded and MenuItemClick events fired by embeded tree menu are intercepted and dispached.

private void SetupTabs(List<IWTMenuEntryDefinition> menuDefinition) {  foreach (IWTMenuEntryDefinition menuEntryDef in menuDefinition){    WTNavTreeMenuItem tab = new WTNavTreeMenuItem(menuEntryDef);    tab.Padding = new Padding(3, 13, 5, 13);    tab.Dock = Gizmox.WebGUI.Forms.DockStyle.Fill;    tab.Extra = false;    tab.Location = new Point(0, 0);    navigationTabs.TabPages.Add(tab);    OnMenuItemAdded(tab);        if (menuEntryDef.HasChildMenus) {      WTTreeMenu menu = new WTTreeMenu();      menu.BorderStyle = BorderStyle.None;      menu.Font = new Font(this.Font.Name, this.Font.Size);      menu.ExpandMode = ExpandMode;      menu.Location = new System.Drawing.Point(0, 0);      menu.Dock = DockStyle.Fill;      menu.ShowLines = false;      menu.MenuItemClick += new WTMenuEventHandler(menu_MenuItemClick);      menu.MenuItemAdded += new WTMenuEventHandler(menu_MenuItemAdded);      tab.Controls.Add(menu);      tab.ChildMenu = menu;      menu.InitMenu(menuEntryDef.ChildMenus);    }  } }

When a menu item which belongs to embeded tree menu is clicked, the whole interaction is handled by embeded tree menu. The event is intercepted by WTNavTreeMenu and rerouted to controller throgh own MenuItemClick event.

When user selects a menu on first level, it is actually a NavigationTab, and event is intercepted through handler for SelectedIndexChanged or Click events, which calls ActivateNavTab

void navigationTabs_SelectedIndexChanged(object sender, EventArgs e){  ActivateNavTab(); } private void navigationTabs_Click(object sender, EventArgs e){  ActivateNavTab(); }

ActivateNavTab method collapses all tree menus if the ExpandMode is MenuMode, and fires MenuItemClick.

private void ActivateNavTab(){  if (ExpandMode == WTTreeExpandMode.MenuMode){    for (int i = 0; i < navigationTabs.TabPages.Count; i++){      WTNavTreeMenuItem item = navigationTabs.TabPages[i] as WTNavTreeMenuItem;      if (item != null && item.ChildMenu != null) {        WTTreeMenu submenu = item.ChildMenu as WTTreeMenu;        if (submenu != null)          submenu.CollapseAll();      }    }  }  IWTMenuItem menuItem = navigationTabs.SelectedItem as IWTMenuItem;  OnMenuItemClick(menuItem); }

DetachLinkedModule unlink the module from the calling menu item. If the menu item is submenu, it delegates the detaching to the WTTreeMenus.

public bool DetachModule(IWTModule module){  bool moduleFound = false;  for ( int i=0; i<navigationTabs.TabPages.Count; i++){    WTNavTreeMenuItem menuItem = navigationTabs.TabPages[i] as WTNavTreeMenuItem;    if (menuItem != null){      if (menuItem.LinkedModule == module) {        menuItem.LinkedModule = null;        moduleFound = true;        break;      }      else if (menuItem.ChildMenu != null) {        moduleFound = menuItem.ChildMenu.DetachModule(module);        if (moduleFound)          break;      }    }  }  return moduleFound; }

The sample application included in attachment and shown in Figure 3 uses the WTNavTreeMenu component.

Figure 3: Sample application using WTNavTreeMenu component. Set the SampleNavTreeMenu.wgx as redirect page in Index.htm or as start page in Project Properties -> Web -> Start Action ->Specific Page to start the application with this menu

Using the menu system

Now we have all the pieces of the puzzle ready, so we can start playing and putting them together, to create a small application.

Start by creating a new Form in the solution: in solution explorer, right click on startup project, select Add New Item, and from Visual WebGui category select Visual WebGui form (fig 4). Name it MenuSample and build the project. Building is necessary to be able to register the new form as Visual WebGui Application (all forms must be registered as Visual WebGui Application in order to allow Visual WebGui engine to instantiate them at runtime). Set the startup object of the application to launch the new form. To do this, go to Project Properties, Web -> Start Action -> Specific Page and enter MenuSample.wgx. As a rule of thumb, default name for a form in Visual Webgui application is the form name with wgx extension.

Figure 4: Adding a new form to the application.

Then register the form as Visual WebGui Application in Project Properties, Registration, Applications (Figure 5). Right click on first button to open Choose Applications form, and select MenuSample form. If you don't see the form, you need to rebuild the project. Set the form's size to 800 x 500.

Add from the Toolbox the following components to the form and set the properties as in the table below below:

Figure 5: Registering the form as Visual WebGui Application.

Then add the following code in the Load event handler for the form:

private void MenuSample_Load(object sender, EventArgs e) {  wtMenuController.ActivateMenu(); }

Build and run and you'll see the application as shown in Figure 4.

The application uses the sample menu configuration XML already in download file. The configuration file contains both menu entries fully defined in the XML file, as well as menu items dynamically generated at runtime, through the help of menu creator classes, which implement IWTCustomMenuCreator interface. The menuitem definition in configuration file must define CustomMenuCreator attribute, by referencing a class implementing IWTCustomMenuCreator interface, as below:

<MenuItem Name="Favorites"            Type= "SampleModules.AdminModule, MiniCMS"            Icon="Favorites.png"            CustomMenuCreator="FavoritesMenu, MiniCMS.DocMenuCreator" />

The code for class FavoritesMenu is shown below (code simplified for clarity):

public class FavoritesMenu : IWTCustomMenuCreator{  public List<IWTMenuEntryDefinition> GenerateMenuDefinition(){    List<IWTMenuEntryDefinition> menu = new List<IWTMenuEntryDefinition>();    // create the type of module to be launched by the menus    // (embeded web browser)    Type browserClass = WTHelper.        CreateTypeByReflectionFromClassName("BrowserModule, MiniCMS.Browser");    // Create root menu entry definition for Search engines tree    MenuEntryDocumentDefinition md =        new MenuEntryDocumentDefinition("Search Engines", "groups.png");    // add three child submenus for three search engines    md.ChildMenus.AddRange(new IWTMenuEntryDefinition[]{           new MenuEntryDocumentDefinition("Google Search", browserClass ,            new string[] { "url", "www.google.com"}),        new MenuEntryDocumentDefinition("Bing", browserClass ,            new string[] { "url", "www.bing.com"}),        new MenuEntryDocumentDefinition("Ask", browserClass ,            new string[] { "url", "www.ask.com"})});    // Create root menu entry definition for social networks sites    MenuEntryDocumentDefinition db =        new MenuEntryDocumentDefinition("Social Networks", "img_2.png");    // add menu items for three social networks    db.ChildMenus.AddRange(new IWTMenuEntryDefinition[]{           new MenuEntryDocumentDefinition("LinkedIn",browserClass ,            new string[] { "url", "www.linkedin.com"}),        new MenuEntryDocumentDefinition("FaceBook", browserClass ,            new string[] { "url", "www.facebook.com"}),        new MenuEntryDocumentDefinition("Twitter", browserClass ,            new string[] { "url", "www.twitter.com"}) });    // add the two root branches to menu entry definition object    menu.Add(md);    menu.Add(db);    return menu;  } }

The code can also generate the menus in any way (e.g. read the entries from a database, web service, etc), this way allowing to build applications with customizable menu structure. Feel free to experiment with the configuration file or create new CustomMenuCreator handlers to generate menu items at runtime.


   
Bogdan Zamfir has a Master's degree in Computer Science and has been working as an independent consultant and software developer since 1993. He develops both web and desktop applications, using various languages and platforms including C#, VB.NET, Visual FoxPro, Microsoft Access, Visual Basic, and C/C++. Visit his web site for more information.
Comment and Contribute

 

 

 

 

 


(Maximum characters: 1200). You have 1200 characters left.

 

 

Sitemap