This blog post will demonstrate how to use XAF, along with DXperience controls, to create an MDI application, the image below shows what the finished application will look like:
As you can see from the image, the child forms are represented by tabs within the main form. However, you will be able to customize the location and appearance of the child forms via the XtraTabbedMdiManager component. You can read more on the XtraTabbedMdiManager class in the documentation.
There are 7 short steps to creating this MDI application and they are as follows:
Step 1 - Create a new XAF Windows solution and give it a name (MDIDemo):

Step 2 - Create the MDIMainForm And MDIChildForm Templates
To build an MDI application, you should develop two Frame Templates:
- MDIMainForm - a Frame Template for the Main Window (the Window that is first displayed when starting an application);
- MDIChildForm - a Frame Template for child Windows (all the Windows except the Main Window).
You won't have to create these Templates from scratch. The eXpressApp Framework installation provides the sources of the main Templates. You can use the MainForm and DetailViewForm Templates as prototypes for your new Templates:
- In the Solution Explorer, create a new folder called "Templates" within the MDIDemo.Win application project.
- Invoke the context menu for the new "Templates" folder and select the Add | Existing Item... menu item. In the invoked dialog, navigate to the [ProgramFilesFolder]\Developer Express Inc\eXpressApp Framework\Sources\FrameTemplates\ folder. Select the MainForm.cs and DetailViewForm.cs files from the "CS" folder if your project is in C#, or the MainForm.vb and DetailViewForm.vb files from the "VB" folder if your project is in Visual Basic. Press the Add button. Visual Studio will create local copies of these two files.
- Rename the added MainForm.cs (MainForm.vb) file to MDIMainForm.cs (MDIMainForm.vb), and the DetailViewForm.cs (DetailViewForm.vb) file to MDIChildForm.cs (MDIChildForms.vb). Rename the MainForm and DetailViewForm classes and all the references to them inside these files in the following manner:
MainForm to MDIMainForm and DetailViewForm to MDIChildForm.
Then, you should modify the MDIMainForm Template as defined below. The MDIChildForm Template doesn't need to be modified.
Step 3 - Modify the MDIMainForm Template
You have to make some changes in the MDIMainForm Template. For this purpose, open the MDIMainForm file in the Visual Studio Form Designer. There, do the following:
- Via the Properties window, set the MainForm's IsMdiContainer property to true.
- Remove the viewSitePanel from the form. Since this form is just a container for child forms, it must not contain Views. In the MDIMainForm.cs, modify the Initialize method call in the following manner:
Initialize(mainBarManager, containers, new IActionContainer[] { cObjectsCreation,
cRecordEdit, cView, cPrint, cExport }, null, navigation);
- From the DX: Navigation & Layout page of the Toolbox, drag and drop an XtraTabbedMdiManager component to the form.
Now, create the menu items that will manage MDI functions:
- Select the barManager component on the form. This will allow you to perform toolbar design-time customizations.
- Click the small [Add] link on the MainMenu toolbar (the toolbar at the top, by default).
- Select the Menu (BarSubItem) item in the invoked context menu. This will create a new toolbar item. Give it the Window name (for instance).
- Click the new toolbar item twice (two clicks with a short pause in between, not a double click), to expand it. Via the [Add] link inside the invoked menu, add a Button (BarButtonItem) toolbar item. Set the new item's name to, for instance, Close All Windows .
-
Select the Close All Windows toolbar item, and add an ItemClick event handler via the Properties window. In this event handler, implement the code that closes all open child Windows, as shown below:
private void barButtonItemCloseAll_ItemClick(object sender, ItemClickEventArgs e) {
foreach(Form child in this.MdiChildren) {
child.Close();
if(child.Visible)
break;
}
- Using the same technique as above, add an MDI children list (BarMdiChildrenListItem) item to the Window toolbar item. This is a special item type, which will be populated with open child Windows automatically. It will also allow navigating to the selected Window.
A child form will be shown as a tab within the main form. Both the main and child forms will have their own toolbars. This will look unattractive because of the nested MainMenu toolbars inside each tab. So, you should merge the items of the main and child MainMenu toolbars. The BarManager does this automatically, but you should hide a child Window's MainMenu toolbar in code. For this purpose, add a handler to the barManager's Merge event, via the Properties window:
using DevExpress.XtraBars;
//...
private void barManager_Merge(object sender, DevExpress.XtraBars.BarManagerMergeEventArgs e) {
foreach (Bar childBar in e.ChildManager.Bars) {
if (childBar == e.ChildManager.MainMenu || childBar == e.ChildManager.StatusBar)
continue;
childBar.Visible = false;
}
}
Step 4 - Add the Filters Action Container to the Detail View Form Template
There are Actions that are enabled when there is a View on the current frame. The MDIMainForm Template doesn't contain a View. So, the Actions that are mapped to the Action Containers contained in this Template are displayed disabled. For instance, the FilterController.SetFilterAction is displayed by the Filters Action Container contained in the MDIMainForm Template. You can make this Action and other similar Actions available, if you add the Filters Action Container and other required Action Containers to the MDIChildForm Template. This Template's Action Containers are displayed in the main form, because of the merge operation performed under the main and detail form's Action Containers (see above).
The code below demonstrates the code snippets that you should add to the MDIChildForm.Designer.cs (MDIChildForm.Designer.vb) and MDIChildForm.cs (MDIChildForm.vb) files, to add the Filters Action Container:
In the MDIChildForm.Designer.cs (MDIChildForm.Designer.vb) file:
using DevExpress.ExpressApp.Utils;
//...
partial class MDIChildForm {
private void InitializeComponent() {
//...
this.cFilters = new DevExpress.ExpressApp.Win.Templates.ActionContainers.ActionContainerBarItem();
//...
//
// mainBarManager
//
//...
this.barManager.Items.AddRange(new DevExpress.XtraBars.BarItem[] {
//...
this.cFilters,
//...
});
//
// barSubItemEdit
//
this.barSubItemEdit.LinksPersistInfo.AddRange(new DevExpress.XtraBars.LinkPersistInfo[] {
//...
new DevExpress.XtraBars.LinkPersistInfo(this.cFilters, true)
});
//
// StandardToolBar
//
//...
this.StandardToolBar.LinksPersistInfo.AddRange(new DevExpress.XtraBars.LinkPersistInfo[] {
//...
new DevExpress.XtraBars.LinkPersistInfo(this.cFilters, true),
//...
});
//
// cFilters
//
this.cFilters.Caption = CaptionHelper.GetLocalizedText(FrameTemplatesDetailViewForm, "Filters","Filters");
this.cFilters.ContainerId = "Filters";
this.cFilters.Id = 26;
this.cFilters.MergeType = DevExpress.XtraBars.BarMenuMerge.MergeItems;
this.cFilters.Name = "cFilters";
//...
private DevExpress.ExpressApp.Win.Templates.ActionContainers.ActionContainerBarItem cFilters;
}
Step 5 - Replace the Default Templates with the MDIMainForm and MDIChildForm
By default, an application uses the MainForm and DetailViewForm Templates to display the Main Window and Windows with Detail Views, respectively. You should change this default behavior, and make the application use your MDIMainForm and MDIChildForm Templates instead. For this purpose, open the Program.cs file, and add a handler to the XafApplication.CreateCustomTemplate. This event is raised each time a Template is required.
static class Program {
static void Main() {
MDIDemoWindowsFormsApplication application = new MDIDemoWindowsFormsApplication();
application.CreateCustomTemplate +=
new EventHandler<CreateCustomTemplateEventArgs>(application_CreateCustomTemplate);
// ...
}
}
Implement the CreateCustomTemplate event handler, as shown below:
using DevExpress.ExpressApp.Win.CustomTemplates;
//...
static void application_CreateCustomTemplate(object sender, CreateCustomTemplateEventArgs e) {
if(e.Context == TemplateContext.ApplicationWindow) {
e.Template = new MDIMainForm();
} else
if(e.Context == TemplateContext.View) {
e.Template = new MDIChildForm();
} else {
e.Template = null;
}
}
Step 6 - Implement the MDIStrategy
A Show View Strategy is a special abstraction which determines how and where to show Views (see ShowViewStrategyBase). The eXpressApp Framework comes with two Windows Forms Show View Strategies:
- ShowInSingleWindowStrategy
Using this strategy, child windows are displayed within the Main Window.
- ShowInMultipleWindowsStrategy
Using this strategy, child windows are displayed individually.
In your MDI application, you should implement a new strategy by inheriting from the ShowViewStrategy class:
using DevExpress.ExpressApp.Win;
using System.Collections.Generic;
using DevExpress.ExpressApp;
//...
class MDIStrategy : DevExpress.ExpressApp.Win.WinShowViewStrategyBase {
private List<WinWindow> delayedToShow = new List<WinWindow>();
private List<WinWindow> childWindows = new List<WinWindow>();
private void window_Closing(object sender, System.ComponentModel.CancelEventArgs e) {
e.Cancel = true;
}
private void window_Closed(object sender, EventArgs e) {
WinWindow window = sender as WinWindow;
window.Closing -= new System.ComponentModel.CancelEventHandler(window_Closing);
window.Closed -= new EventHandler(window_Closed);
childWindows.Remove(window);
}
private void Form_FormClosing(object sender, System.Windows.Forms.FormClosingEventArgs e) {
Form form = sender as Form;
WinWindow window = FindChildWindowByForm(form);
e.Cancel = false;
if (e.CloseReason == System.Windows.Forms.CloseReason.MdiFormClosing) {
if (window.Form.MdiParent.MdiChildren[0] == window.Form) {
e.Cancel = !CanCloseMDIParentWindow();
}
if (!e.Cancel) {
window.Form.FormClosing -= new FormClosingEventHandler(Form_FormClosing);
window.View.SynchronizeInfo();
window.View.Close(false);
}
}
else {
e.Cancel = !window.CanClose();
}
}
private WinWindow FindChildWindowByForm(Form form) {
foreach (WinWindow window in childWindows) {
if (window.Form == form) {
return window;
}
}
return null;
}
private bool CanCloseMDIParentWindow() {
foreach (WinWindow window in childWindows) {
if (window.View != null && !window.View.CanClose()) {
return false;
}
}
return true;
}
protected override void ShowViewFromCommonView(ShowViewParameters parameters,
ShowViewSource showViewSource) {
WinWindow existWindow = FindWindowByView(parameters.CreatedView);
if (existWindow != null) {
parameters.CreatedView.Dispose();
parameters.CreatedView = existWindow.View;
existWindow.Show();
}
else {
ShowViewInNewWindow(showViewSource.SourceFrame,
parameters.CreatedView, TemplateContext.View, parameters.Controllers);
}
}
protected override void ShowViewCore(ShowViewParameters parameters,
ShowViewSource showViewSource) {
if (parameters.TargetWindow == TargetWindow.Current &&
showViewSource.SourceFrame == MainWindow) {
parameters.TargetWindow = TargetWindow.Default;
}
base.ShowViewCore(parameters, showViewSource);
}
protected override void ShowViewFromLookupView(ShowViewParameters parameters,
ShowViewSource showViewSource) {
ShowInModalWindow(parameters, showViewSource.SourceFrame);
}
protected override void BeforeShowWindow(WinWindow window) {
if (window != MainWindow) {
if (window.Form.MdiParent != MainWindow.Form) {
window.Form.MdiParent = MainWindow.Form;
window.Closing += new System.ComponentModel.CancelEventHandler(window_Closing);
window.Form.FormClosing += new System.Windows.Forms.FormClosingEventHandler(Form_FormClosing);
window.Closed += new EventHandler(window_Closed);
childWindows.Add(window);
}
}
}
public MDIStrategy(XafApplication application) : base(application) { }
public override void ShowStartupWindow() {
delayedToShow.Clear();
try {
base.ShowStartupWindow();
}
finally {
System.Windows.Forms.Application.DoEvents();
foreach (WinWindow child in delayedToShow) {
ShowWindow(child);
}
}
}
public override void ShowWindow(WinWindow window) {
if (!window.IsMain && MainWindow == null) {
delayedToShow.Add(window);
}
else {
base.ShowWindow(window);
}
}
}
The MDIStrategy class overrides the following methods to perform the MDI specific functionality:
- ShowViewFromCommonView
Called when a View requests another View to be shown (for instance, when the New action is executed within a List View). According to the MDIStrategy, the requested View is shown within a new Window, i.e. a new MDI child form. In addition, if this View is already being shown, a new Window isn't created.
In a case when a View within a lookup Property Editor or within another View is requested, the ShowViewFromLookupView method is called.
- ShowViewFromLookupView
Called when a View in a lookup Property Editor requests another View to be shown. According to the MDIStrategy, the requested View is shown in a modal window.
- BeforeShowWindow
Called before showing a Window. Sets the parent form to the current child form.
- ShowStartUpWindow and ShowWindow
The ShowStartUpWindow method is called when an application starts. It creates the Main Window, activates its Controllers and calls the ShowWindow method. Some of the activated Controllers can be required to show a child Window by calling the ShowWindow method. Since the Main Window isn't shown yet, problems can occur. To avoid this, the ShowWindow method is overridden. If the Main Window isn't shown, this method adds the Window passed as a parameter to the delayed Window list. The ShowStartUpWindow method shows the Windows from this list after showing the Main Window.
Step 7 - Replace the Default Strategy with the MDIStrategy
By default, the WinApplication's Manager uses the ShowInSingleWindowStrategy or ShowInMultipleWindowsStrategy strategy, to show Windows in a UI. You should change this default behavior, and make the Manager use your MDIStrategy instead. To accomplish this, open the Program.cs file and specify the ShowViewStrategy property of the WinApplication's Manager:
static class Program {
public static void Main() {
MDIDemoWindowsFormsApplication application = new MDIDemoWindowsFormsApplication();
application.ShowViewStrategy = new MDIStrategy(application);
// ...
}
}
And that’s all there is to creating an MDI application with XAF :-)