Overview

  • 8 min to read

The CardView supports custom data binding that allows you to provide the extension with data manually. In this custom data binding mode, you have full control of and responsibility for populating the CardView with the required data each time the extension requests it.

Note

The Custom Data Binding mode was specially designed for working with a large data volume and handling all data shaping operations manually (e.g., it is impossible to use the built-in Linq/Entity Framework capabilities for some reason).

If there are no strict requirements to use Custom Data Binding, using one of the following data binding modes is recommended.

In brief, this mode requires that you manually populate a CardView-specific view model with the portion of data that needs to be currently displayed within the extension. You can accomplish this by performing the following steps.

  • Define Handling Actions

    In addition to a standard handling Action defined using the GridSettingsBase.CallbackRouteValues, use the CardViewSettings.CustomBindingRouteValuesCollection to specify additional Actions to handle all or particular data operations (pageing, filtering, sorting), depending on which functionality is used in your CardView. Data operations are identified by the CardViewOperationType enumeration values.

  • Obtain/Update CardView State

    In Actions, use the static CardViewExtension.GetViewModel method to obtain the extension's view model, which is a specific CardViewModel object designed to maintain the CardView state and provide a means to populate itself with data. By default, the view model obtained within Actions contains the previous state of the CardView. If required, update this state with the state information that relates to the processed operation and is automatically passed to an Action as a parameter. (For instance, this state can be a column state (CardViewColumnState) that contains information on which column is sorted and in which direction/order, or a pager state (CardViewPagerState) that determines which page is selected.)

  • Delegate Model Population

    Within a Controller, use the CardView view model that contains the up-to-date CardView state and call its CardViewModel.ProcessCustomBinding method to delegate the implementation of a model population to certain Model-layer functions specified with method parameters.

  • Implement Model Population

    At the Model layer, implement certain delegates, which are pointed to by the CardViewModel.ProcessCustomBinding method's parameters to populate the CardView view model with a portion of the currently required data.

  • Bind the CardView to the Populated Model

    In a Controller, pass the populated view model object as a Model to the CardView's Partial View. In the Partial View, bind the extension to the passed Model by using the CardViewExtension.BindToCustomData method.

Note

When implementing the CardView's custom data binding, the DevExpressEditorsBinder must be used instead of the default model binder to correctly transfer values from DevExpress editors back to the corresponding data model fields. See the Binding to Model section in the Binding Data Editors to Data topic for more details on how to specify DevExpressEditorsBinder as a model binder.

These steps are described in greater detail below. You can refer to the corresponding online demo to see this scenario in action.

Define Handling Actions

To associate a CardView data operation type with a handling Action method, populate the extension's specific route values collection available using the CardViewSettings.CustomBindingRouteValuesCollection property. Register each data operation that you need to handle by adding a specific item to the collection. Within an item, identify a data operation and specify a handling method by providing its Controller and Action names.

Partial View Code:


@Html.DevExpress().CardView(
    settings => {
        settings.Name = "CardView";

        //Specify an Action to handle standard callback requests (including population of the CardView with data on the first load)
        settings.CallbackRouteValues = new { Controller = "MyController", Action = "MyCardViewPartial" }; 

        //Specify Actions to handle certain data operations - in this sample, these operations are paging, sorting and filtering
        settings.CustomBindingRouteValuesCollection.Add(
            CardViewOperationType.Paging,
            new { Controller = "MyController", Action = "CustomBindingPagingAction" }
        );
        settings.CustomBindingRouteValuesCollection.Add(
            CardViewOperationType.Sorting,
            new { Controller = "MyController", Action = "CustomBindingSortingAction" }
        );
        settings.CustomBindingRouteValuesCollection.Add(
            CardViewOperationType.Filtering,
            new { Controller = "MyController", Action = "CustomBindingFilteringAction" }
        );
        ...

Refer to the Action Types and Passed Parameters topic to learn more about available data operation types.

Obtain/Update CardView State

In each specified Action, you can obtain both the previous state of the CardView (the state the CardView had before an end-user performed the currently processed data operation) and the information (state) specific to the currently processed operation (for instance, information about the applied paging or sorted column). The CardView's previous state can be obtained as a CardView view model object (CardViewModel) by making a call to the static CardViewExtension.GetViewModel method. The currently processed data operation's state is automatically passed to each Action as its parameter(s). You can update the CardView view model object, maintaining the previous CardView state with the state of the currently processed operation to update the CardView state. Then, the updated CardView view model should be processed to populate it with the required data (see the next item for details).

Controller Code:


public partial class MyController: Controller {
        public ActionResult MyCardView() {
            return View("MyCardView");
        }
        //Standard CardView callback requests
        public ActionResult MyCardViewPartial() {
            var viewModel = CardViewExtension.GetViewModel("cardView");
            if(viewModel == null)
                //Initialize the CardView state on the first load
                viewModel = CreateCardViewModel();
            return MyCustomBindingCore(viewModel);
        }
        //Paging operation requests
        public ActionResult MyPagingAction(CardViewPagerState pager) {
            var viewModel = CardViewExtension.GetViewModel("cardView");
            viewModel.ApplyPagingState(pager); //Update the CardView state with the processed paging state
            return MyCustomBindingCore(viewModel);
        }
        //Sorting operation requests
        public ActionResult MySortingAction(CardViewColumnState column, bool reset) {
            var viewModel = CardViewExtension.GetViewModel("cardView");
            viewModel.ApplySortingState(column, reset); //Update the CardView state with the processed sorting state
            return MyCustomBindingCore(viewModel);
        }

        PartialViewResult MyCustomBindingCore(CardViewModel viewModel) {
            ... //Some code to delegate model population to Model-layer functions (see the next item)
            return PartialView("MyCardViewPartial", viewModel);
        }

        static CardViewModel CreateCardViewModel() {
            var viewModel = new CardViewModel();
            viewModel.KeyFieldName = "ID";
            viewModel.Columns.Add("From");
            viewModel.Columns.Add("Subject");
            viewModel.Columns.Add("Sent");
            viewModel.Columns.Add("Size");
            viewModel.Columns.Add("HasAttachment");
            return viewModel;
        }
    }

Refer to the Action Types and Passed Parameters topic to learn more on operation-specific parameters passed to handling Actions automatically.

See the Methods to Update the CardView State in Actions topic for a list of methods the CardViewModel exposes to update the CardView state.

Delegate Model Population

Given the CardView view model, call its CardViewModel.ProcessCustomBinding method to delegate the implementation of model population to certain Model-layer functions specified with method parameters. The CardViewModel.ProcessCustomBinding method has several overloads. You can select one depending on which data operations you need to handle.

Controller Code:


public partial class MyController: Controller {
        ...
        PartialViewResult MyCustomBindingCore(CardViewModel cardViewModel) {
            //Delegate implementation of the functions that are always required for custom binding
            cardViewModel.ProcessCustomBinding(
                MyBindingHandlers.MyGetDataCardCount, //Function to return the total number of data cards in a model
                MyBindingHandlers.MyGetData //Function to return data cards requested by the CardView
            );
            return PartialView("MyCardViewPartial", cardViewModel);
        }
    }

Sample Model Code:


...
    public static class MyBindingHandlers {
        static IQueryable Model { get { return LargeDatabaseDataProvider.Emails; } }

        public static void MyGetDataCardCount(CardViewCustomBindingGetDataCardCountArgs e) {
            ... //Code for the delegate method implementation
        }

        public static void MyGetData(CardViewCustomBindingGetDataArgs e) {
            ... //Code for the delegate method implementation
        }

        ...
    }
    ...

Refer to the ProcessCustomBinding - Available Binding Delegates topic to learn more about the CardViewModel.ProcessCustomBinding method.

Implement Model Population

At the Model layer, provide implementation for the specified method delegates according to your application needs and domain layer logic. Use arguments available through the e parameter of method delegates to obtain operation-related information and to supply the model with the required data.

Model Code:


...
    public static class MyBindingHandlers {
        static IQueryable Model { get { return LargeDatabaseDataProvider.Emails; } }

        public static void MyGetDataCardCount(CardViewCustomBindingGetDataCardCountArgs e) {
            e.DataCardCount = Model.Count(); //Return the total count of data cards in your model (taking into account the applied filtering, if any)
        }

        public static void MyGetData(CardViewCustomBindingGetDataArgs e) {
            e.Data = Model
                .ApplySorting(e.State.SortedColumns) //Your function to implement sorting of a model
                .Skip(e.StartDataCardIndex) //Position on the first required data card
                .Take(e.DataCardCount); //Get the number of currently required data cards (cards of the current page)
        }
        ...
    }
    ...

Refer to the Implementation of Typed Method Delegates topic to learn more about the available arguments and the implementation of method delegates.

Bind the CardView to the Populated Model

The resulting populated CardView view model object should be returned as a Model to a Partial View that contains the CardView markup. To bind the CardView to this view model, use the CardViewExtension.BindToCustomData method within the extension's Partial View.

Controller Code:


public partial class MyController: Controller {
        ...
        public ActionResult MyCardViewPartial() {
            ...
            return MyCustomBindingCore(viewModel);
        }
        public ActionResult MyPagingAction(CardViewPagerState pager) {
            ...
            return MyCustomBindingCore(viewModel);
        }
        public ActionResult MySortingAction(CardViewColumnState column, bool reset) {
            ...
            return MyCustomBindingCore(viewModel);
        }

        PartialViewResult MyCustomBindingCore(CardViewModel viewModel) {
            ...
            return PartialView("MyCardViewPartial", viewModel);
        }
        ...
    }

Partial View Code:


@Html.DevExpress().CardView(
    settings => {
        settings.Name = "cardView";
        ...
    }).BindToCustomData(Model).GetHtml()
See Also