XAF Web Select Group in ListView

This was a small request coming from a friend but I had the same question asked a few more times in the past so I decided to do a quick post, that way if it comes up again I can just send them this link.

OK, to the point: How do we make a XAF Web ListView selectable by groups?

Here is the code:

public class SelectGroupInListViewController : ViewController
    {
        public SelectGroupInListViewController()
        {
            TargetObjectType = typeof(Invoice);
            // TargetViewNesting = Nesting.Root;
            TargetViewType = ViewType.ListView;
            
        }

        /// <summary>
        /// List view grid control
        /// </summary>
        private ASPxGridView Grid
        {
            get
            {
                ASPxGridListEditor editor = View != null ? (View as ListView).Editor as ASPxGridListEditor : null;
                if (editor != null)
                    return editor.Grid;

                return null;
            }
        }

        protected override void OnViewControlsCreated()
        {
            base.OnViewControlsCreated();

            if (Grid != null)
            {
                // Add checkboxes to group rows
                Grid.ClientInstanceName = gridId;
                Grid.Templates.GroupRowContent = new CheckboxGroupRowContentTemplate(gridId);
                Grid.CustomCallback += Grid_CustomCallback;
                Grid.HtmlRowPrepared += Grid_HtmlRowPrepared;
            }
        }

        #region Group checkboxes

        // http://www.devexpress.com/Support/Center/Example/Details/E1760

        /// <summary>
        /// SaleItem key field name
        /// </summary>
        //public const string keyFieldName = "ItemId";

        /// <summary>
        /// list view control name
        /// </summary>
        public const string gridId = "InvoiceListViewGrid";

        protected void Grid_CustomCallback(object sender, ASPxGridViewCustomCallbackEventArgs e)
        {
            string[] parameters = e.Parameters.Split(';');

            int index = int.Parse(parameters[0]);
            string fieldname = parameters[1];
            bool isGroupRowSelected = bool.Parse(parameters[2]);

            ReadOnlyCollection<GridViewDataColumn> groupedCols = Grid.GetGroupedColumns();
            if (groupedCols[groupedCols.Count - 1].FieldName == fieldname)
            {
                // Checked groupcolumn is the lowest level groupcolumn;
                // we can apply original recursive checking here
                Grid.ExpandRow(index, true); // Expand grouped column for consistent behaviour
                for (int childIndex = 0; childIndex < Grid.GetChildRowCount(index); childIndex++)
                {
                    var key = (Grid.GetChildRow(index, childIndex) as Invoice).Oid;
                    Grid.Selection.SetSelectionByKey(key, isGroupRowSelected);
                }
            }
            else
            {
                // checked row is not the lowest level groupcolumn:
                // we will find the Datarows that are to be checked recursively by iterating all rows
                // and compare the fieldvalues of the fields described by the checked groupcolumn
                // and all its parent groupcolumns. Rows that match these criteria are to the checked.
                // CAVEAT: only expanded rows can be iterated, so we will have to expand clicked row recursivly before iterating the grid

                // Get index of current grouped column
                int checkedGroupedColumnIndex = -1;
                foreach (GridViewDataColumn gcol in groupedCols)
                {
                    if (gcol.FieldName == fieldname)
                    {
                        checkedGroupedColumnIndex = groupedCols.IndexOf(gcol);
                        break;
                    }
                }

                //Build dictionary with checked groupcolumn and its parent groupcolumn fieldname and values
                Invoice checkedDataRow = Grid.GetRow(index) as Invoice;
                Dictionary<string, object> dictParentFieldNamesValues = new Dictionary<string, object>();
                string parentFieldName;
                object parentKeyValue;
                for (int i = checkedGroupedColumnIndex; i >= 0; i--)
                {
                    // find parent groupcols and parentkeyvalue
                    GridViewDataColumn pcol = groupedCols[i];
                    parentFieldName = pcol.FieldName;
                    parentKeyValue = checkedDataRow.Oid;
                    dictParentFieldNamesValues.Add(parentFieldName, parentKeyValue);
                }

                bool isChildDataRowOfClickedGroup;
                Grid.ExpandRow(index, true); // Expand grouped column for consistent behaviour
                for (int i = 0; i <= Grid.VisibleRowCount - 1; i++)
                {
                    Invoice row = Grid.GetRow(i) as Invoice;

                    // Check whether row does belong to checked group all the parent groups of the clicked group
                    isChildDataRowOfClickedGroup = true;
                    foreach (KeyValuePair<string, object> kvp in dictParentFieldNamesValues)
                    {
                        parentFieldName = kvp.Key;
                        parentKeyValue = kvp.Value;
                        if (row.Oid.Equals(parentKeyValue) == false)
                        {
                            isChildDataRowOfClickedGroup = false;
                            break;
                            //Iterated row does not belong to at least one parentgroup of the clicked groupbox; do not change selection state for this row
                        }
                    }

                    if (isChildDataRowOfClickedGroup == true)
                    {
                        // Row meets all the criteria for belonging to the clicked group and all parents of the clicked group:
                        // change selection state
                        Grid.Selection.SetSelectionByKey(row.Oid, isGroupRowSelected);
                    }
                }
            }
        }

        void Grid_HtmlRowPrepared(object sender, ASPxGridViewTableRowEventArgs e)
        {
            // Subscribe group checkboxes on CheckedChanged event
            if (e.RowType == GridViewRowType.Group)
            {
                ASPxCheckBox checkBox = Grid.FindGroupRowTemplateControl(e.VisibleIndex, "checkBox") as ASPxCheckBox;
                if (checkBox != null)
                {
                    checkBox.Checked = GetChecked(e.VisibleIndex);
                }
            }
        }

        /// <summary>
        /// Return checked state by selected childrens
        /// </summary>
        /// <param name="visibleIndex">Visible index of group row</param>
        /// <returns>Is checked</returns>
        protected bool GetChecked(int visibleIndex)
        {
            for (int childIndex = 0; childIndex < Grid.GetChildRowCount(visibleIndex); childIndex++)
            {
                //var key = Grid.GetChildRowValues(visibleIndex, childIndex, keyFieldName);
                var key = (Grid.GetChildRow(visibleIndex, childIndex) as Invoice).Oid;
                bool isRowSelected = Grid.Selection.IsRowSelectedByKey(key);
                if (!isRowSelected)
                    return false;
            }

            return true;
        }

        #endregion Group checkboxes
    }

    /// <summary>
    /// Group row with checkBox template
    /// </summary>
    public class CheckboxGroupRowContentTemplate : ITemplate
    {
        /// <summary>
        /// Grid id for usng in inner scripts
        /// </summary>
        string gridId = "";

        /// <summary>
        /// ctor
        /// </summary>
        /// <param name="gridId">Grid id for usng in inner scripts</param>
        public CheckboxGroupRowContentTemplate(string gridId)
        {
            this.gridId = gridId;
        }

        public void InstantiateIn(Control container)
        {
            // Add checkbox
            ASPxCheckBox checkBox = new ASPxCheckBox();
            checkBox.ID = "checkBox";
            checkBox.Init += checkBox_Init;
            container.Controls.Add(checkBox);

            // Add caption
            ASPxLabel label = new ASPxLabel();
            label.Text = GetCaptionText((GridViewGroupRowTemplateContainer)container);
            label.EncodeHtml = false;
            container.Controls.Add(label);
        }

        void checkBox_Init(object sender, System.EventArgs e)
        {
            ASPxCheckBox checkBox = sender as ASPxCheckBox;
            GridViewGroupRowTemplateContainer container = checkBox.NamingContainer as GridViewGroupRowTemplateContainer;
            checkBox.ClientSideEvents.CheckedChanged = string.Format(
                "function(s, e){{ {0}.PerformCallback('{1};{2};' + s.GetChecked()); }}",
                gridId,
                container.VisibleIndex,
                container.Column.FieldName);
        }

        /// <summary>
        /// Get caption text for container
        /// </summary>
        /// <param name="container">Container</param>
        /// <returns>Caption</returns>
        private string GetCaptionText(GridViewGroupRowTemplateContainer container)
        {
            string captionText = !string.IsNullOrEmpty(container.Column.Caption) ? container.Column.Caption : container.Column.FieldName;
            return string.Format("{0} : {1} {2}", captionText, container.GroupText, container.SummaryText);
        }
    }

Now let’s see it in action:

Source Code: https://github.com/jjcolumb/BlogDemo

Until next time, XAF out!

 

Posted in XAF

Leave a Reply

Your email address will not be published. Required fields are marked *