Skip to main content

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 own OnFrameAssigned 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:

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.

See Also