Stick three UI designers in a room and ask them to come up with a UI design solution for a particular problem and you are going to get three different designs? Why’s that? Well because, although there are good guidelines on UI design in general, UI design is more art than science and so personal taste comes into play. The bottom line is that there is no, correct, general UI solution for a particular problem. Now that kind of sucks for us because, in case you hadn’t noticed, XAF is an application framework that ships with a UI. By definition, this UI must be a general one and that means that most of our customers are going to want to change the UI most of the time.
Well good news! As of 2010.2 that’s going to be a lot easier! Up until now, when you’ve customised your templates, all’s been well until we’ve released a new version of XAF, in which we’ve updated the templates, and now look, you’ve got to merge your changes in with ours, and that’s no fun. Well we’re looking to make things a lot simpler in 2010.2. So, how have we done this? Well, firstly we’ve moved all of the residual logic out of the templates and into base classes or helper classes or into new components entirely. This means that, in future, when we improve our templates, these improvements will actually be added to these classes and will not affect your customisations. Secondly, we’ve improved design time support and ensured that all of our templates can be customised, for example you can easily add a new custom action controller:

And thirdly, we’ve introduced a special CustomizeTemplateViewControllerBase controller that will make your life easier if you are adding panels to your customised template.
Okay, so let’s take a look at all of this in action by customising the main form template to include a custom panel. First thing we have to do is to get a hold of the template and include it in our project, then we are going to add the panel by opening the designer, changing the view site’s Dock property from Fill to None and dropping a SplitContainerControl nearby. Next, we’ll drag and drop the view site onto the SplitContainerControl’s first panel and set the view site Dock property to Fill:

Then we’ll set the SplitContainerControl’s Dock property to Fill and set the SplitContainerControl’s FixedPanel to Panel2 (to ensure that it is the left panel that can be resized), before setting the SplitContainerControl’s PanelVisibility to Panel1, to make the right panel invisible by default.
Having done that, we need to put some info on the SplitContainerControl’s panel. Let’s display a hint, based on the current View, and store the hint in the Application Model, so that it can be edited via the Model Editor. So, firstly, we extend the Application Model, to add a new Description property to View nodes:
public interface IModelViewDescription {
string Description { get; set; }
}
public override void ExtendModelInterfaces(ModelInterfaceExtenders extenders) {
base.ExtendModelInterfaces(extenders);
extenders.Add<IModelView, IModelViewDescription>();
} Secondly, we need to display this information on a panel. To do this, we are going to implement a platform-agnostic controller and platform-specific descendants. The platform-agnostic Controller will be derived from the built-in CustomizeTemplateViewControllerBase<TemplateType> Controller. Where TemplateType can be either a template type or an interface implemented by templates. The CustomizeTemplateViewControllerBase already provides template access logic, so our code wont be that complicated:
public abstract class InfoPanelsViewControllerBase<TemplateType> : CustomizeTemplateViewControllerBase<TemplateType> where TemplateType : IFrameTemplate {
private string GetInfoText() {
return ((IModelViewDemoDescription)View.Model).DemoDescription;
}
protected abstract void SetInfoTextToControls(string text);
protected override void UpdateControls(View view) {
SetInfoTextToControls(GetInfoText());
}
protected override void UpdateControls(object currentObject) {
SetInfoTextToControls(GetInfoText());
}
public InfoPanelsViewControllerBase() {
TargetObjectType = typeof(InfoPanelsObject);
}
} Now we can derive a Windows Forms specific descendant. Before we do that though, let’s define an interface, through which we’ll access the template’s panel, allowing us to decouple the controllers from the templates.
DevExpress.XtraEditors.SplitContainerControl SplitContainer { get; }
}
Implement this in the template:
#region IInfoPanelTemplate Members
DevExpress.XtraEditors.SplitContainerControl IInfoPanelTemplate.SplitContainer {
get { return splitContainerControl; }
}
#endregion
Finally, let’s write the Windows Form specific controller that interacts with the template’s panel:
public class InfoPanelsViewController : InfoPanelsViewControllerBase<IInfoPanelTemplate> {
private GroupControl panel;
private LabelControl literal;
private void UpdateInfoPanelVisibility(SplitContainerControl splitContainer) {
splitContainer.PanelVisibility = splitContainer.Panel2.Controls.Count > 0 ?
SplitPanelVisibility.Both : SplitPanelVisibility.Panel1;
}
private void EnsureControls() {
if(panel == null) {
panel = new GroupControl();
panel.AutoSize = true;
panel.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
panel.Dock = System.Windows.Forms.DockStyle.Top;
literal = new LabelControl();
literal.Dock = System.Windows.Forms.DockStyle.Top;
literal.AutoSizeMode = LabelAutoSizeMode.Vertical;
panel.Controls.Add(literal);
panel.Text = "Contextual Panel";
}
}
protected override void SetInfoTextToControls(string text) {
if(literal != null) {
literal.Text = text;
}
}
protected override void AddControlsToTemplateCore(IInfoPanelTemplate template) {
EnsureControls();
template.SplitContainer.Panel2.Controls.Add(panel);
UpdateInfoPanelVisibility(template.SplitContainer);
}
protected override void RemoveControlsFromTemplateCore(IInfoPanelTemplate template) {
template.SplitContainer.Panel2.Controls.Remove(panel);
UpdateInfoPanelVisibility(template.SplitContainer);
panel = null;
literal = null;
}
}
Now, if we launch the application, go to the model editor and add a description for a View, we should see:

Of course we can do exactly the same thing in a Web Form View, by following the same steps as above (omitted here):

I hope you can see how this this new feature, coming in 2010.2, will increase your productivity by making it much easier for you to customise our templates, and to maintain those customisations in the face of us releasing improved templates in the future.
That’s all for this post, until next time, happy XAF’ing! 