Define the Scope of Controllers and Actions
- 9 minutes to read
This topic describes how to set conditions for activating Controllers and their Actions.
Specify the Scope of Controllers
Specify the Scope of Controllers
Activate or Deactivate a Controller
If you implement a Controller that executes code in the Controller.Activated event handler or in the OnActivated
and OnViewControlsCreated
methods, you may have to define the conditions when this code is executed. For example, you may need to specify that a Controller that customizes a grid editor should be active for List Views only. To do this, change the Controller.Active property’s value directly or use one of Controller’s properties listed in this topic.
The Controller.Active
property affects the visibility of all Actions declared in this Controller (if a Controller is inactive, all its Actions are also inactive). To hide a single Action, you can use the ActionBase class’s properties (see Change the Scope of Actions).
The following members help you specify the required conditions for Controller activation:
Member | Description |
---|---|
Controller.Active | Provides access to a collection of reason/value pairs used to activate or deactivate a Controller or determine its active state. The Controller is active when all values in this collection are true . You can add an item with a value that is a conditional expression. The Controller deactivates when this expression returns false . |
ViewController.TargetObjectType | Specifies the type of objects a View should display to activate a View Controller. |
ViewController.TargetViewId | Specifies the ID of the View for which a View Controller is intended. |
ViewController.TargetViewType | Specifies the type of the View for which a View Controller is intended. |
ViewController.TargetViewNesting | Specifies whether the View for which a View Controller is intended is root, nested, or any. |
WindowController.TargetWindowType | Specifies the kind of Window for which a Window Controller is intended. |
Activate a Controller for Particular Views and Objects
You can inherit your Controller from ViewController<ViewType> or ObjectViewController<ViewType, ObjectType>, and use the generic parameters to control views and types where a View Controller should become active.
The example below demonstrates how to activate a Controller only for the Person
Detail View.
public class ViewController1 : ObjectViewController<DetailView, Person> {
protected override void OnActivated() {
base.OnActivated();
Person person = this.ViewCurrentObject;
DetailView detailView = this.View;
// ....
}
}
The ObjectViewController`2.ViewCurrentObject and ObjectViewController`2.View property types change based on the types passed as generic parameters. This may be useful when you want to avoid casting the View Controller’s View to ListView or DetailView.
Visual Studio Designer does not work for Controllers inherited from generic types.
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.
Change the Scope of Actions
When you implement an Action, you may want to display it in a particular form. For example, a CancelAppointment Action should be displayed for Views that show Appointment
object(s) only. There are two approaches to deactivating an Action: deactivating its Controller and deactivating the Action itself.
Deactivate an Action’s Controller
In most cases, you can turn off (deactivate) a Controller and hide all its Actions. Refer to the Specify the Scope of Controllers section to learn how to do this.
Deactivate an Action Itself
You can define the target Views and Windows for each Action individually. To do this, use the following properties:
Member | Description |
---|---|
ActionBase.Active | Provides access to a collection of key/value pairs that determine or change the Action’s active state. The resulting state determines Action visibility. |
ActionBase.Enabled | Provides access to a collection of key/value pairs that determine an Action’s enabled/disabled state. A disabled Action is grayed out in the UI and a user cannot execute it. |
ActionBase.TargetObjectsCriteria | Specifies a criteria to enable an Action. |
ActionBase.TargetObjectsCriteriaMode | Specifies whether all the currently selected objects meet the TargetObjectsCriteria criteria to enable an Action. |
ActionBase.TargetObjectType | Specifies the type of the object(s) that the current View should display to activate an Action. |
ActionBase.TargetViewId | Specifies the ID of the targeted View where an Action should be active. |
ActionBase.TargetViewNesting | Specifies whether the View where an Action should be active is root, nested, or any. |
ActionBase.TargetViewType | Specifies the type of the targeted View where an Action should be active. |
ActionBase.SelectionDependencyType | Specifies a context to enable an Action. |
These properties control whether an Action is visible in certain Views and Windows. Refer to the ActionBase.Active topic to learn other options used to control the visibility state (for example, hide an Action depending on a Business Object’s property value).
Activate a Controller or Action for Multiple Business Objects or Views
To make an individual ViewController
or Action
available in Views of different business object types, consider one of the following solutions:
- Set the ViewController.TargetObjectType property in code to an interface or their base class type that is implemented or inherited by all these business types respectively.
- Specify several View identifiers (separated by semicolon
;
) in the ViewController.TargetViewId property. - Manage the Controller.Active or ActionBase.Active properties manually depending on required conditions.
Examples of Controller Scope Configuration
The following controller activates in nested Paycheck
List Views:
using DevExpress.ExpressApp;
public class ViewController1 : ViewController {
public ViewController1() {
TargetObjectType = typeof(Paycheck);
TargetViewType = ViewType.ListView;
TargetViewNesting = Nesting.Nested;
}
}
// Alternatively, you can write the controller above as:
public class ViewControllerAlternative : ObjectViewController<ListView, Paycheck> {
public ViewControllerAlternative() {
TargetViewNesting = Nesting.Nested;
}
}
The next controller activates in Root Views for a set of Business Object types:
using DevExpress.ExpressApp;
public class ViewController1 : ViewController {
public ViewController1() {
TargetViewNesting = Nesting.Root;
}
protected override void OnViewChanged() {
base.OnViewChanged();
var viewObjectType = View?.ObjectTypeInfo?.Type;
Active["Only for Employee and Paycheck objects"] =
viewObjectType == typeof(Employee) || viewObjectType == typeof(Paycheck);
}
}
You can add multiple activation criteria of arbitrary complexity:
using DevExpress.ExpressApp;
public class ViewController1 : ViewController {
protected override void OnViewChanged() {
base.OnViewChanged();
if(View is null)
return;
Active["Only for nested views"] = !View.IsRoot;
Active["Not for lookup list views"] =
Frame.Context != TemplateContext.LookupControl && Frame.Context != TemplateContext.LookupWindow;
Active["Not for inline edit or split views"] = !(View is ListView listView && (listView.AllowEdit || listView.Model.MasterDetailMode == MasterDetailMode.ListViewAndDetailView));
Active["Only for specific types"] = CanActivateForType(View.ObjectTypeInfo?.Type);
Active["Active in specific views only"] = CanActivateForViewId(View.Id);
}
private bool CanActivateForType(Type businessObjectType) { /*...*/ }
private bool CanActivateForViewId(string viewId) { /*...*/ }
}
Examples of Action Scope Configuration
To define Action scope, you can use simple static rules or dynamic rules based on the properties of the selected objects. The following example demonstrates an Action that can deactivate users. This Action is enabled if the following conditions are met:
- The current View is a root View for
ApplicationUser
. - At least one of the selected objects in a List View or the current object in a Detail View must be an active user (
CriteriaOperator.FromLambda<ApplicationUser>(user => user.IsActive).ToString()
). - The selection cannot include Administrator users.
using DevExpress.ExpressApp;
using DevExpress.ExpressApp.Actions;
using DevExpress.Data.Filtering;
public class DeactivateUsersController : ViewController {
SimpleAction deactivateUserAction;
public DeactivateUsersController() {
deactivateUserAction = new SimpleAction(this, "DeactivateUser", DevExpress.Persistent.Base.PredefinedCategory.Edit) {
SelectionDependencyType = SelectionDependencyType.RequireMultipleObjects,
TargetObjectsCriteria = CriteriaOperator.FromLambda<ApplicationUser>(user => user.IsActive).ToString(),
TargetObjectsCriteriaMode = TargetObjectsCriteriaMode.TrueAtLeastForOne,
TargetViewNesting = Nesting.Root,
TargetObjectType = typeof(ApplicationUser),
TargetViewType = ViewType.Any,
};
deactivateUserAction.Execute += DeactivateUserAction_Execute;
}
private void UpdateActionState() {
// Update action visibility dynamically based on the currently selected objects
var selectedUsers = deactivateUserAction.SelectionContext?.SelectedObjects?.OfType<ApplicationUser>()
?? Array.Empty<ApplicationUser>();
bool adminUsersSelected = selectedUsers.Any(user => user.Roles.Any(role => role.IsAdministrative));
deactivateUserAction.Enabled["Cannot deactivate Administrator users"] = !adminUsersSelected;
}
private void DeactivateUserAction_Execute(object sender, SimpleActionExecuteEventArgs e) {
var selectedUsers = deactivateUserAction.SelectionContext?.SelectedObjects?.OfType<ApplicationUser>()
?? Array.Empty<ApplicationUser>();
foreach (var user in selectedUsers) {
user.IsActive = false;
}
ObjectSpace.CommitChanges();
}
protected override void OnActivated() {
base.OnActivated();
UpdateActionState();
View.CurrentObjectChanged += View_CurrentObjectChanged;
if (View is ListView listView) {
listView.SelectionChanged += ListView_SelectionChanged;
}
}
protected override void OnDeactivated() {
base.OnDeactivated();
if (View is not null) {
View.CurrentObjectChanged -= View_CurrentObjectChanged;
if (View is ListView listView) {
listView.SelectionChanged -= ListView_SelectionChanged;
}
}
}
private void ListView_SelectionChanged(object sender, EventArgs e) => UpdateActionState();
private void View_CurrentObjectChanged(object sender, EventArgs e) => UpdateActionState();
}
Tip
For more information on how to determine a user’s permissions, refer to the following topic: Determine if the Current User Has Specific Permissions.