Search code examples
c#gridviewcomboboxdevexpresscascading

DevExpress Cascading Combobox GridView DataBind


I have a GridView which contains a TextField of BenefitName (string) and a ValueField of BenefitInfoKeySK (int).

Originally I directly bound a datasource to the combobox and had distinct TextField and ValueField values. (Code Sample 2)

I updated my code to create 'cascading' comboboxes and the TextField value no longer displays in my GridView but it does display within the comboBox in my edit form.

Why does the TextField render in my edit form combobox but not on my GridView?

PartialView CodeSample 1 (Cascading combobox)

    settings.Columns.Add(column =>
    {
        column.FieldName = "BenefitKey";
        column.Name = "BenefitKey";
        column.Caption = "Claim Type";
        column.Width = 200;
        column.Settings.AllowHeaderFilter = DefaultBoolean.False;
        column.EditFormSettings.Visible = DefaultBoolean.True;
        column.Settings.AllowSort = DefaultBoolean.False;
        column.EditorProperties().ComboBox(p =>
        {
            p.CallbackRouteValues = new { Controller = "BenefitClaimDetails", Action = "GetBenefitTypes", TextField = "BenefitName", ValueField = "BenefitInfoKeySK", headerEmployeeID = Model.BenefitHeaderEmployee };
            p.TextField = "BenefitName";
            p.ValueField = "BenefitInfoKeySK";
            p.ClientSideEvents.BeginCallback = "ClaimTypeComboBox_BeginCallback";
            p.EnableCallbackMode = true;
            p.Width = 200;
            p.ValidationSettings.RequiredField.IsRequired = true;
            p.ValidationSettings.RequiredField.ErrorText = "Claim Type cannot be blank";
            p.ValidationSettings.ErrorDisplayMode = ErrorDisplayMode.ImageWithText;
        });
    });

PartialView Code Sample 2 (basic combobox)

    settings.Columns.Add(column =>
    {
        column.FieldName = "BenefitKey";
        column.Name = "BenefitKey";
        column.Caption = "Claim Type";
        column.Width = 200;
        column.Settings.AllowHeaderFilter = DefaultBoolean.False;
        column.EditFormSettings.Visible = DefaultBoolean.True;
        column.Settings.AllowSort = DefaultBoolean.False;

        column.ColumnType = MVCxGridViewColumnType.ComboBox;
        var comboBoxProperties = column.PropertiesEdit as ComboBoxProperties;
        comboBoxProperties.Width = 200;
        comboBoxProperties.DataSource = repository.GetBenefitListByEmployee(Model.BenefitHeaderEmployee);
        comboBoxProperties.TextField = "BenefitName";
        comboBoxProperties.ValueField = "BenefitInfoKeySK";
        comboBoxProperties.DropDownRows = 15;
        comboBoxProperties.ValueType = typeof(int);
        comboBoxProperties.ValidationSettings.RequiredField.IsRequired = true;
        comboBoxProperties.ValidationSettings.RequiredField.ErrorText = "Claim Type cannot be blank";
        comboBoxProperties.ValidationSettings.ErrorDisplayMode = ErrorDisplayMode.ImageWithText;
    });

I have found that p.TextField and p.ValueField in code sample 1 do nothing. They can be removed with no effect on the code. However I must pass those fields in my CallBackRoute and assign them in the controller code:

Controller Code

    public ActionResult GetBenefitTypes(int claimantID, string textField, string valueField, string headerEmployeeID)
    {
        return GridViewExtension.GetComboBoxCallbackResult(p => {
            p.TextField = textField;
            p.ValueField = valueField;
            p.BindList(repository.GetBenefitListByEmployee(claimantID, headerEmployeeID));
        });
    }

As you can see, at the end both techniques call the same Repository method. Please let me know if I can elaborate on anything.

EDIT

I also attempted to modify Code Sample 1 by adding a columnType value and assigning a data source as shown below. This successfully displayed the TextField on my GridView but the null which I pass for claimantID prevented the Editor combobox from displaying any values. For that reason I am also including the JS code where I assign claimantID.

Modified PartialView Code Sample 1 (working grid view, no values in combobox)

    settings.Columns.Add(column =>
    {
        column.FieldName = "BenefitKey";
        column.Name = "BenefitKey";
        column.Caption = "Claim Type";
        column.Width = 200;
        column.Settings.AllowHeaderFilter = DefaultBoolean.False;
        column.EditFormSettings.Visible = DefaultBoolean.True;
        column.Settings.AllowSort = DefaultBoolean.False;
        column.EditorProperties().ComboBox(p =>
        {
            p.CallbackRouteValues = new { Controller = "BenefitClaimDetails", Action = "GetBenefitTypes", TextField = "BenefitName", ValueField = "BenefitInfoKeySK", headerEmployeeID = Model.BenefitHeaderEmployee };
            p.TextField = "BenefitName";
            p.ValueField = "BenefitInfoKeySK";
            p.ClientSideEvents.BeginCallback = "ClaimTypeComboBox_BeginCallback";
            p.EnableCallbackMode = true;
            p.Width = 200;
            p.ValidationSettings.RequiredField.IsRequired = true;
            p.ValidationSettings.RequiredField.ErrorText = "Claim Type cannot be blank";
            p.ValidationSettings.ErrorDisplayMode = ErrorDisplayMode.ImageWithText;
        });

        column.ColumnType = MVCxGridViewColumnType.ComboBox;
        var comboBoxProperties = column.PropertiesEdit as ComboBoxProperties;
        comboBoxProperties.DataSource = repository.GetBenefitListByEmployee(Model.BenefitHeaderEmployee, null);
        comboBoxProperties.TextField = "BenefitName";
        comboBoxProperties.ValueField = "BenefitInfoKeySK";
    });

JS Code

@*The follwing functions handle the Cascading Benefit Type combobox*@
function OnSelectedClaimantChanged() {
    BenefitClaimDetailsGridView.GetEditor("BenefitKey").PerformCallback();
}
function ClaimTypeComboBox_BeginCallback(s, e) {
    e.customArgs["claimantID"] = BenefitClaimDetailsGridView.GetEditor("DependentKey").GetValue();
}

I'm sorry to include so much code and not a working project, but the Solution is quite large. I hope this will do.


Solution

  • I found my solution.

    I started to comment out the comboBoxProperties section line by line to understand at what point the callback would begin failing and I found that it failed due to my declaration: column.ColumnType = MVCxGridViewColumnType.ComboBox;

    After looking into how the EditorProperties.Combobox() was working I realized that in that section of code, ComboBox was utilizing the standard MVCxColumnComboBoxProperties rather than the DevExpress GridView ComboBox.

    I think the issue was that in the callback I was declaring the column type one way but then overriding it later on with my comboBoxProperties code. The change which I made was changing var comboBoxProperties = column.PropertiesEdit as ComboBoxProperties; to

    var comboBoxProperties = column.PropertiesEdit as MVCxColumnComboBoxProperties;

    The finalized working code is here. For somebody trying to put this together themselves: In order to achieve cascading comboboxes which display text but are stored with a different value you will need the following.

    1. In your partial view give the 'initiator' column a SelectedIndexChanged event such as this:

      settings.Columns.Add(column =>
      {
          column.FieldName = "DependentKey";
          column.Name = "DependentKey";
          column.Caption = "Claimant";
          column.Width = 300;
          column.Settings.AllowHeaderFilter = DefaultBoolean.False;
          column.EditFormSettings.Visible = DefaultBoolean.True;
          column.Settings.AllowSort = DefaultBoolean.False;
      
          column.ColumnType = MVCxGridViewColumnType.ComboBox;
          var comboBoxProperties = column.PropertiesEdit as ComboBoxProperties;
          /*Get an employee model for the current employee and pass that to the Depoendents method to get their list of dependents*/
          comboBoxProperties.DataSource = repository.GetDependentDropdownList(repository.GetCurrentEmployee(employeeID: Model.BenefitHeaderEmployee).DimEmployee);
          comboBoxProperties.TextField = "DependentName";
          comboBoxProperties.ValueField = "DependentKeySK";
          comboBoxProperties.DropDownRows = 15;
          comboBoxProperties.ValueType = typeof(int);
          comboBoxProperties.ValidationSettings.RequiredField.IsRequired = true;
          comboBoxProperties.ValidationSettings.RequiredField.ErrorText = "Claimant cannot be blank";
          comboBoxProperties.ValidationSettings.ErrorDisplayMode = ErrorDisplayMode.ImageWithText;
          comboBoxProperties.ClientSideEvents.SelectedIndexChanged = "OnSelectedClaimantChanged";
      });
      
    2. In the column which is to be influenced you will need to declare it as such (this is the final version of my Code Sample 1 / Code Sample 2 from above):

      settings.Columns.Add(column =>
      {
          column.FieldName = "BenefitKey";
          column.Name = "BenefitKey";
          column.Caption = "Claim Type";
          column.Width = 200;
          column.Settings.AllowHeaderFilter = DefaultBoolean.False;
          column.EditFormSettings.Visible = DefaultBoolean.True;
          column.Settings.AllowSort = DefaultBoolean.False;
          column.EditorProperties().ComboBox(p =>
          {   /*Populate the combobox with valid values based on the selected dependentKey*/
              p.CallbackRouteValues = new { Controller = "BenefitClaimDetails", Action = "GetBenefitTypes", TextField = "BenefitName", ValueField = "BenefitInfoKeySK", headerEmployeeID = Model.BenefitHeaderEmployee };
              p.ClientSideEvents.BeginCallback = "ClaimTypeComboBox_BeginCallback";
              p.Width = 200;
              p.ValidationSettings.RequiredField.IsRequired = true;
              p.ValidationSettings.RequiredField.ErrorText = "Claim Type cannot be blank";
              p.ValidationSettings.ErrorDisplayMode = ErrorDisplayMode.ImageWithText;
          });
      
          /*Display the BenefitName in the gridView. The Callback method TextField and ValueField only influence the comboBox in the Editor window*/
          var comboBoxProperties = column.PropertiesEdit as MVCxColumnComboBoxProperties;
          comboBoxProperties.Width = 200;
          comboBoxProperties.DataSource = repository.GetBenefitListByEmployee(Model.BenefitHeaderEmployee);
          comboBoxProperties.TextField = "BenefitName";
          comboBoxProperties.ValueField = "BenefitInfoKeySK";
      });
      
    3. Create the JS code on your Index page - my code is above

    4. Create the ActionResult in your controller - my code is above

    With those 4 pieces in place you can cascade the comboBoxes and properly display them in the grid as well