0

In a Dotvvm GridView is it possible to have one header for 2 or more columns? Can you give an example?

demonplus
  • 5,613
  • 12
  • 49
  • 68
renanj
  • 21
  • 2

1 Answers1

0

The GridView contron in DotVVM currently doesn't support spanned header columns. However, you can simply extend the control to support this functionality.

public class CustomGridView : GridView
{

    [MarkupOptions(AllowBinding = false, MappingMode = MappingMode.InnerElement)]
    public List<GridViewHeaderSpan> HeaderColumnSpans
    {
        get { return (List<GridViewHeaderSpan>)GetValue(HeaderColumnSpansProperty); }
        set { SetValue(HeaderColumnSpansProperty, value); }
    }
    public static readonly DotvvmProperty HeaderColumnSpansProperty
        = DotvvmProperty.Register<List<GridViewHeaderSpan>, CustomGridView>(c => c.HeaderColumnSpans, null);


    protected override void OnPreRender(IDotvvmRequestContext context)
    {
        // let the GridView build the cells as it always do
        base.OnPreRender(context);

        if (HeaderColumnSpans != null)
        {
            // check that spans don't collide with each other
            var spans = HeaderColumnSpans.OrderByDescending(s => s.ColumnIndex).ToList();
            foreach (var span in spans)
            {
                if (spans.Any(s => s.CollidesWith(span)))
                {
                    throw new DotvvmControlException(this, "Collisions found in the HeaderColumnSpans collection!");
                }
                if (span.ColumnIndex < 0 || span.SpannedColumns <= 1 || span.ColumnIndex + span.SpannedColumns > Columns.Count)
                {
                    throw new DotvvmControlException(this, "The HeaderColumnSpans contains indexes that are out of range!");
                }
            }

            // go through all spans, remove the columns, and set the colspan attribute to the <th> elements
            // we have sorted them by the column index descending, so we don't change indexes of the <th> elements
            foreach (var span in spans)
            {
                var headerRow = Children[0].Children[0];

                // set the colspan attribute
                var firstCell = (HtmlGenericControl)headerRow.Children[span.ColumnIndex];
                firstCell.Attributes["colspan"] = span.SpannedColumns.ToString();

                // remove the next columns
                for (var i = span.SpannedColumns - 1; i >= 1; i--)
                {
                    headerRow.Children.RemoveAt(span.ColumnIndex + i);
                }
            }
        }
    }
}

public class GridViewHeaderSpan : DotvvmBindableObject
{

    public int ColumnIndex
    {
        get { return (int)GetValue(ColumnIndexProperty); }
        set { SetValue(ColumnIndexProperty, value); }
    }
    public static readonly DotvvmProperty ColumnIndexProperty
        = DotvvmProperty.Register<int, GridViewHeaderSpan>(c => c.ColumnIndex, 0);

    public int SpannedColumns
    {
        get { return (int)GetValue(SpannedColumnsProperty); }
        set { SetValue(SpannedColumnsProperty, value); }
    }
    public static readonly DotvvmProperty SpannedColumnsProperty
        = DotvvmProperty.Register<int, GridViewHeaderSpan>(c => c.SpannedColumns, 2);


    public bool CollidesWith(GridViewHeaderSpan span)
    {
        if (span == this)
        {
            // cannot colide with self
            return false;
        }

        // the whole span must be before this one or after this one
        return (ColumnIndex < span.ColumnIndex + span.SpannedColumns) && (ColumnIndex + SpannedColumns > span.ColumnIndex);
    }
}

You can configure it using the collection in the markup – you simply tell the GridView index (zero-based) of the column where the merged area should start (ColumnIndex), and number of columns that is merged together (SpannedColumns).

<cc:CustomGridView DataSource="{value: Customers}">
    <Columns>
        <dot:GridViewTextColumn HeaderText="ID" ValueBinding="{value: Id}" AllowSorting="true" />
        <dot:GridViewTextColumn HeaderText="Name" ValueBinding="{value: FirstName}" AllowSorting="true" SortExpression="LastName" />
        <dot:GridViewTextColumn HeaderText="" ValueBinding="{value: LastName}" />
        <dot:GridViewTemplateColumn HeaderText="Controls">
            Edit
        </dot:GridViewTemplateColumn>
        <dot:GridViewTemplateColumn>
            Delete
        </dot:GridViewTemplateColumn>
    </Columns>
    <HeaderColumnSpans>
        <cc:GridViewHeaderSpan ColumnIndex="1" SpannedColumns="2" />
        <cc:GridViewHeaderSpan ColumnIndex="3" SpannedColumns="2" />
    </HeaderColumnSpans>
</cc:CustomGridView>
Tomáš Herceg
  • 1,595
  • 1
  • 13
  • 18