Customize Controllers and Actions
- 6 minutes to read
To add custom functionality to your XAF-based application, create or modify a Controller. If new functionality requires user interaction, add Actions.
This article describes the following ways to add, customize, or disable controllers:
- Access a Controller and its Events and Properties. Use this method if a built-in or third-party Controller includes required customization API.
- Inherit from a Built-in Controller. Create a descendant and override virtual methods if existing properties and events are insufficient for your customization scenario.
- Disable a Built-in Controller. Turn off built-in functionality in specific contexts.
Access a Controller and its Events and Properties
This customization approach works best if an Action or a Controller requires different customizations in different contexts. For example, you may customize the New action and implement custom object initialization code depending on the module. In this case, event handlers are more appropriate than a descendant Controller.
Use the Frame.GetController<ControllerType> method to access any built-in Controller from your custom Controller.
Consider a scenario where you have to remove the Person
item from the New Action’s items list to prohibit users from creating a Person
object.
- Create a new Controller.
- In the Controller’s
OnActivated
method, access NewObjectViewController to subscribe to the NewObjectViewController.CollectDescendantTypes event. This event is fired when XAF generates the New Action’s items list. - In the
CollectDescendantTypes
event handler, remove thePerson
item from the list. Use the
OnDectivated
method to unsubscribe from theCollectDescendantTypes
event.using DevExpress.ExpressApp.SystemModule; using DevExpress.Persistent.BaseImpl; // ... public class CustomizeNewActionWindowController : ViewController { protected override void OnActivated(){ base.OnActivated(); NewObjectViewController controller = Frame.GetController<NewObjectViewController>(); if (controller != null) { controller.CollectDescendantTypes += NewObjectViewController_CollectDescendantTypes; } } private void NewObjectViewController_CollectDescendantTypes( object sender, CollectTypesEventArgs e) { foreach (Type type in e.Types) { if (type.Name == nameof(Person)) { e.Types.Remove(type); break; } } } protected override void OnDeactivated() { NewObjectViewController controller = Frame.GetController<NewObjectViewController>(); if (controller != null) { controller.CollectDescendantTypes -= NewObjectViewController_CollectDescendantTypes; } base.OnDeactivated(); } }
Important
To avoid possible null reference exceptions when accessing an existing Controller from your code, always ensure that the Frame.GetController<ControllerType> method result is not null when the XafApplication.OptimizedControllersCreation property is true.
Inherit From a Controller
When you create an XAF application, you need to slightly customize an existing Controller. The customization does not depend on any conditions, therefore you do not need to modify your customizations in another module. To customize an existing Controller, it is best to inherit from that Controller and override its virtual methods. To customize an Action, you also need to inherit from the Action’s Controller and override its virtual methods, because Actions are contained in Controllers.
Multiple descendants from the same Controller can conflict with each other. XAF activates all Controllers that don’t have inherited classes and raises an exception if it finds Actions with matching IDs. Aim to create only one descendant of a base Controller and implement all required customizations in this descendant. If you need several descendant Controllers, make sure to manually disable inherited Actions to avoid conflicts.
Important
Select an appropriate built-in Controller type when you create a descendant. In most cases, to create a descendant of a built-in Controller, you need to select the last descendant in the inheritance chain. For example, a Controller may have WinForms, ASP.NET Web Forms, and ASP.NET Core Blazor-specific descendants: WinModificationsController, WebModificationsController and BlazorModificationsController. Use one of these classes as your base class. Otherwise, XAF activates both the built-in Controller and your descendant. This situation may lead to conflicts.
However, remember that ViewController and WindowController are basic Controllers. They do not provide any particular features and are designed for developing new Controllers. You can inherit from these parent Controllers to create new Controllers in your UI-specific module projects. If your solution does not contain these projects, add a Controller to an application project. Your newly implemented items don’t conflict with other descendants of
ViewController
andWindowController
.- Do not change the ViewController.TargetObjectType, ViewController.TargetViewType, ViewController.TargetViewId or ViewController.TargetViewNesting property values when you inherit from a built-in Controller. If you do make changes, your Controller may no longer apply to previously associated Views.
When you inherit from a Controller, the Application Model contains information on both the base and inherited Controllers. In the ActionDesign | Controllers node, the new Controller contains Actions declared in it. The inherited Actions are displayed under the base Controller’s node.
Consider WebModificationsController
. It contains the following Actions: Cancel, Save, SaveAndClose, and Edit. Each Action has a virtual method: Cancel
, Save
, SaveAndClose
and ExecuteEdit
respectively. XAF invokes the method (which one?) when a user performs an operation with the Action. To customize the default execution process, you should inherit from WebModificationsController
and override the required virtual method. In this case, all of the UI settings (Category, Caption, Image, Shortcut, Tooltip, etc.) and the management state (Active and Enabled) of the Actions are reused.
After executing the SaveAndClose Action in an ASP.NET Web Forms application, XAF displays a Detail View in a view mode. The following code snippet demonstrates how to close the Incident Detail View after you execute the SaveAndClose Action:
using DevExpress.ExpressApp.Web.SystemModule;
// ...
public class MyWebModificationsController : WebModificationsController
{
protected override void SaveAndClose(SimpleActionExecuteEventArgs e) {
View view = View;
base.SaveAndClose(e);
if ((view != null) && (view.ObjectTypeInfo.Type == typeof(Incident))) {
view.Close();
}
}
}
In this example, you use a single method to customize Action behavior. However, Controllers and Actions can be more complex, so, you may need to override several virtual methods.
Deactivate a Built-In Controller
You may want to disable a built-in controller. Do this to disable features that you do not wish to use or if the controller’s logic conflicts with your business logic. You can choose one of the following techniques to achieve this.
Create a new Controller
In the new controller’s overridden
OnFrameAssigned
method, find the controller that you want to disable and use its Active property to deactivate the controller:using DevExpress.ExpressApp; using DevExpress.ExpressApp.SystemModule; public class DisableRecordsNavigationController : ViewController { protected override void OnFrameAssigned() { base.OnFrameAssigned(); var recordsNavigationController = Frame.GetController<RecordsNavigationController>(); if (recordsNavigationController != null) { recordsNavigationController.Active["Explicitly disabled"] = false; } } }
When you deactivate a controller this way, you can target the controller’s base type even if it has platform-specific descendants (RecordsNavigationController has an ASP.NET Core Blazor-specific
BlazorRecordsNavigationController
descendant). However, this method is not suitable if the controller has some undesirable logic in its ownOnFrameAssigned
override. In that case, use the following method.Inherit from the built-in Controller
Create a descendant of the built-in controller and deactivate it in the
OnFrameAssigned
method:using DevExpress.ExpressApp.Blazor.SystemModule; public class CustomRecordsNavigationController : BlazorRecordsNavigationController { protected override void OnFrameAssigned() { // keep the line below commented out to disable the base controller's OnFrameAssigned logic //base.OnFrameAssigned(); Active["Explicitly disabled"] = false; } }
In this case, you need to specify the platform-specific descendant of the controller you want to deactivate as your custom controller’s base class. This technique gives you more control over specifying when the controller should be active.
Note
If you deactivate a controller in the OnFrameAssigned
method, it disables the controller entirely. If you want to deactivate this controller in specific views, refer to the following topic: Define the Scope of Controllers and Actions - Activate or Deactivate a Controller.