How to implement skin selection via Navigation panel?

28 October 2008

 

 This article describes how we introduced skin items into the Navigation panel in the FeatureCenter application.

The FeatureCenter demo application is installed by the XAF installation and you can review all its sources as well as to see how it works.

One of the famous features of DXperience controls is their skins, see "Skins Overview" at http://documentation.devexpress.com/#WindowsForms/CustomDocument2399 and "Application Wide Skins" video at http://tv.devexpress.com/SkinsPromo1.movie

		public ReportsController()
			: base() {
			InitializeComponent();

		}

How to demonstrate skins in XAF? The Navigation panel seems to be the best place to introduce a new group of setting, which will switch skins for a whole application.

By default, a Windows Forms XAF application has a standard action "Choose Skin", which is placed in the "Tools" menu. However, the "Tools" menu is not a place, which will be expected by a developer in a Demo application and we decided to duplicate its items into the Navigation panel as a new group.

This is how it will look:

 


It is used in XAF to realize additional behavior scenarios via controllers. Which type of controller should we choose for our task – WindowController or ViewController?

Certainly, we need to create the descendant of the WindowController, 'cause our task doesn't concern exact View, but the whole main Window.

 

So, we need to create Window controller that will realize expected behavior, and customize it in order to implement our task.

 

 

Step One. Add new WindowController to our solution.

 

At first we have to select project, to which we will add new controller, in Solution Explorer, and then add new item to it.

We can do it via Microsoft Visual Studio main menu “Project\Add new item...” or

vai project context menu - cick right mouse button on the project with suffix “Module.Win” in our solution and select “Add\NewItem...” in the menu, as shown below:

After that Microsoft Visual Studio will offer you dialog to select new item type and specify it's name:

Let's the mane of our new coltroller be “SkinsWindowController” and press Add button.

 

After that we will have a new file in our project with code shown below:

 

		using System;

using System.ComponentModel;

using System.Collections.Generic;

using System.Diagnostics;

using System.Text;

 

using DevExpress.ExpressApp;

using DevExpress.ExpressApp.Actions;

using DevExpress.Persistent.Base;

 

namespace FeatureCenter.Module.Win {

public partial class SkinsWindowController : WindowController {

public SkinsWindowController() {

InitializeComponent();

RegisterActions(components);

}

}

}

 

 

 

And all our work will occur inside this file, in the SkinsWindowController class.

 

Step Two. Customize automatically generated code.

 

Microsoft Visual Studio automatically generate file SkinsWindowController.Designer.cs, so we can place some visaul components into controller. But we don't need it in our case – and we can delete file SkinsWindowController.Designer.cs from our project, and modify SkinsWindowController constructor this way:

<code>

public SkinsWindowController() {

}

 

</code>

 

Our controller will operate main application window only, and we can specify this by setting TargetWindowType property of our controller:

 

<code>

public SkinsWindowController() {

this.TargetWindowType = WindowType.Main;

}

</code>

 

Note. Deleting SkinsWindowController.Designer.cs file from the project deprive us possibility of viewing our controller in design mode. So we should use View Code command to edit file contents.

 

Step Three. Adding links to skins into Navigation panel.

 

The best way to adjust navigation panel from our controller is override OnActivated method, as shown in code sample below:

 

<code>

public partial class SkinsWindowController : WindowController {

private ChoiceActionItem skinsGroup = null;

private const string skinsGroupCaption = "Skins";

protected override void OnActivated() {

base.OnActivated();

skinsGroup = new ChoiceActionItem(skinsGroupCaption, skinsGroupCaption, null);

NavigationController.ShowNavigationItemAction.Items.Add(skinsGroup);

foreach(ChoiceActionItem item in ChooseSkinController.ChooseSkinAction.Items) {

ChoiceActionItem newItem = new ChoiceActionItem(item.Caption, item.Caption, null);

newItem.ImageName = ChooseSkinController.ChooseSkinAction.ImageName;

skinsGroup.Items.Add(newItem);

}

}

protected ChooseSkinController ChooseSkinController {

get { return Frame.GetController<ChooseSkinController>(); }

}

protected ShowNavigationItemController NavigationController {

get { return Frame.GetController<ShowNavigationItemController>(); }

}

public SkinsWindowController() {

this.TargetWindowType = WindowType.Main;

}

}

</code>

 

In our OnActivated method we called base.OnActivated() first, then done our additions:

  • create new navigation item for new group with name “Skins”:

<code>

skinsGroup = new ChoiceActionItem(skinsGroupCaption, skinsGroupCaption, null);

</code>

  • add this group to navigation panel with the help of the corresponding system controller:

<code>

NavigationController.ShowNavigationItemAction.Items.Add(skinsGroup);

</code>

  • add items corresponding available skins from the chose skin system controller:

<code>

foreach(ChoiceActionItem item in ChooseSkinController.ChooseSkinAction.Items) {

ChoiceActionItem newItem = new ChoiceActionItem(item.Caption, item.Caption, null);

newItem.ImageName = ChooseSkinController.ChooseSkinAction.ImageName;

skinsGroup.Items.Add(newItem);

}

</code>

 

As you have noticed, we also write some code:

  • for storing a link to our “Skin” group:

<code>

private ChoiceActionItem skinsGroup = null;

</code>

  • for storing the caption of our group:

<code>

private const string skinsGroupCaption = "Skins";

</code>

  • properties, that simplify for us access to the system controllers:

<code>

protected ChooseSkinController ChooseSkinController {

get { return Frame.GetController<ChooseSkinController>(); }

}

protected ShowNavigationItemController NavigationController {

get { return Frame.GetController<ShowNavigationItemController>(); }

}

</code>

 

And after this step we can run our windows project and see a group “Skins” in the navigation panel, which contains a list of available skins.

 

But choosing this actions doesn't result on current skin selection. Why?

That's why we don't link our items in the navigation panel with real ChooseSkin action. And we will do in in the next step.

 

Step Four. Providing links to ChooseSkin action.

 

By default, each item in Navigation panel contains a ViewShortcut to corresponding view, and than user select the item, process ViewShortcut, that linked with item.Data property – third parameter of the ChoiceActionItem constructor (that was null at previous step).

 

So, for each item added to Navigation panel we create a ViewShortcut to SkinDemoObject (simple business object we created to have something else empty space in application window). The ViewShortcut class is a list of [Key, Value] pairs, and we added a reference to the skin item as a new item:

<code>

viewShortcut.Add("SkinID", (string)item.Data);

</code>

 

As result we have following:

 

<code>

public partial class SkinsWindowController : WindowController {

private ChoiceActionItem skinsGroup = null;

private const string skinsGroupCaption = "Skins";

protected override void OnActivated() {

base.OnActivated();

skinsGroup = new ChoiceActionItem(skinsGroupCaption, skinsGroupCaption, null);

NavigationController.ShowNavigationItemAction.Items.Add(skinsGroup);

foreach(ChoiceActionItem item in ChooseSkinController.ChooseSkinAction.Items) {

ViewShortcut viewShortcut = new ViewShortcut(typeof(SkinDemoObject), null, Application.GetListViewId(typeof(SkinDemoObject)));

viewShortcut.Add("SkinID", (string)item.Data);

ChoiceActionItem newItem = new ChoiceActionItem(item.Caption, item.Caption, viewShortcut);

newItem.ImageName = ChooseSkinController.ChooseSkinAction.ImageName;

skinsGroup.Items.Add(newItem);

}

}

protected ChooseSkinController ChooseSkinController {

get { return Frame.GetController<ChooseSkinController>(); }

}

protected ShowNavigationItemController NavigationController {

get { return Frame.GetController<ShowNavigationItemController>(); }

}

public SkinsWindowController() {

this.TargetWindowType = WindowType.Main;

}

}

</code>

 

In platform-independent module we added also a simple business object

<code>

using System;

using DevExpress.Persistent.BaseImpl;

using DevExpress.Xpo;

using DevExpress.ExpressApp.Demos;

using DevExpress.Persistent.Base;

 

namespace FeatureCenter.Module.Skins {

public class SkinDemoObject : BaseObject {

private string name;

public SkinDemoObject(Session session) : base(session) { }

public string Name {

get { return name; }

set { name = value; }

}

}

}

</code>

 

Step Five. Executing ChooseSkin action then Navigation panel item selected .

 

In step four we add an information about skin to Navigation panel items. In order to change skin when user select appropriate item in navigation panel we should process just a pair of events – CustomShowNavigationItem (to execute ChooseSkin action) and CustomUpdateSelectNavigationItem (to say that we proceeed selected Navigation panel item):

 

<code>

public partial class SkinsWindowController : WindowController {

private ChoiceActionItem skinsGroup = null;

private const string skinsGroupCaption = "Skins";

 

private void NavigationController_CustomShowNavigationItem(object sender, CustomShowNavigationItemEventArgs e) {

ViewShortcut shortcut = e.ActionArguments.SelectedChoiceActionItem.Data as ViewShortcut;

if(shortcut != null) {

ChoiceActionItem skinItem =

ChooseSkinController.ChooseSkinAction.Items.Find(shortcut["SkinID"]);

if(skinItem != null) {

ChooseSkinController.ChooseSkinAction.DoExecute(skinItem);

}

}

}

 

private void NavigationController_CustomUpdateSelectNavigationItem(object sender, CustomUpdateSelectedItemEventArgs e) {

if(e.ProposedSelectedItem != null) {

if(e.ProposedSelectedItem.ParentItem == skinsGroup) {

e.Handled = true;

}

}

}

 

public override void UpdateModel(Dictionary dictionary) {

base.UpdateModel(dictionary);

new DefaultSkinListGenerator(dictionary).StorePredefinedLookAndFeelStyle("Black");

}

 

protected override void OnActivated() {

base.OnActivated();

skinsGroup = new ChoiceActionItem(skinsGroupCaption, skinsGroupCaption, null);

NavigationController.ShowNavigationItemAction.Items.Add(skinsGroup);

foreach(ChoiceActionItem item in ChooseSkinController.ChooseSkinAction.Items) {

ViewShortcut viewShortcut = new ViewShortcut(typeof(SkinDemoObject), null, Application.GetListViewId(typeof(SkinDemoObject)));

viewShortcut.Add("SkinID", (string)item.Data);

ChoiceActionItem newItem = new ChoiceActionItem(item.Caption, item.Caption, viewShortcut);

newItem.ImageName = ChooseSkinController.ChooseSkinAction.ImageName;

skinsGroup.Items.Add(newItem);

}

NavigationController.CustomShowNavigationItem += new

EventHandler<CustomShowNavigationItemEventArgs>(NavigationController_CustomShowNavigationItem);

NavigationController.CustomUpdateSelectedItem += new

EventHandler<CustomUpdateSelectedItemEventArgs>(NavigationController_CustomUpdateSelectNavigationItem);

}

 

protected override void OnDeactivating() {

base.OnDeactivating();

NavigationController.CustomUpdateSelectedItem -= new EventHandler<CustomUpdateSelectedItemEventArgs>(NavigationController_CustomUpdateSelectNavigationItem);

NavigationController.CustomShowNavigationItem -= new EventHandler<CustomShowNavigationItemEventArgs>(NavigationController_CustomShowNavigationItem);

}

 

protected ChooseSkinController ChooseSkinController {

get { return Frame.GetController<ChooseSkinController>(); }

}

 

protected ShowNavigationItemController NavigationController {

get { return Frame.GetController<ShowNavigationItemController>(); }

}

 

public SkinsWindowController() {

this.TargetWindowType = WindowType.Main;

}

 

}

</code>

 

We add appropriate handlers in OnActivated() method and delete them in OnDeactivating() method.

 

NavigationController_CustomShowNavigationItem handler takes ViewShortcut, associated with selected Navigation panel item, finds in this sortcut link to corresponding skin action item, and executes ChooseSkin action to select choosed skin.

 

NavigationController_CustomUpdateSelectNavigationItem sets e.Handled to true if selected item is in “skinsGroup”.

 

 

Step Six. Compiling, executing and testing.

 

After all we can compile and run our solution, where we can see now new group with available skins in Navigation panel, moreover, when user select one of these items, appearance of our application is changed in accordance to selected skin.

 

 

Note. This approach can be used for adding links to another actions into Navigation panel.

Free DevExpress Products - Get Your Copy Today

The following free DevExpress product offers remain available. Should you have any questions about the free offers below, please submit a ticket via the DevExpress Support Center at your convenience. We'll be happy to follow-up.