GrapeCity / ComponentOne-MAUI-Samples

3 stars 4 forks source link

flexGrid subtotal (or subsummary) row sticked at the bottom #1

Closed enkaradag closed 1 year ago

enkaradag commented 1 year ago

There is no aggregate sum row (like a subtotal) for flexgrid that is sticked at the bottom.

I tried sticking a panel at the bottom of flexgrid and scroll it horizontally with the move of grid. But flexgrid does not call ScrollPositionChanged event with horizontal scroll (tried for windows)

Then i tried to put flexgrid inside a horizontal scrollview with a sub stacklayout (so the scroll of grid is lost and the external hor. scrollview moves grid together with the subbar) this time vertical scroll performance of grid decreased dramatically.

I tried to add a summary row at the bottommost of my data, then user has to scroll to the bottom to see the summaries (ugly for 100+ rows of data)

I tried to fix the frozenrow (can fix it at the top) to the bottom, set the summary in that cells with code. but there's no option for fixing at the bottom.

Is there a workaround for my need?

Thank you so much

arivoir commented 1 year ago

Hi @enkaradag,

Firstly I want to comment we're already working on this exact feature, for all platforms but WinUI, since the grid uses the native WinUI ScrollViewer and this control lacks for a way of sticking an element to the bottom. What platforms are you working on?

Secondly, the issue you saw about the scroll performance is probably caused because the parent is measuring with infinite size, and the control is creating all the cells. This is a typical issue with ui-virtualizing controls. You can solve it either giving a fixed height to the control, or using a Grid panel whose bottom row is Auto, instead of StackPanel.

Hope it helps.

enkaradag commented 1 year ago

Hi Alvaro,

Currently i'm testing flexGrid on both WinUI, Android and IOS. Load and scroll performance is really good for all platforms comparing to similiar products.

For my need (subtotal on some columns); if i stick a stacklayout bottom of a flexgrid and put them together into a stacklayout and set its width to sum of all column widths then put that outer panel into a horizontal scrollview, it works as expected. But all horizontal cells are created immidiately and causes bad scroll performance.

I test the flexgrid with constant height and width. If grid sticks some rows at top (with FrozenRows=2), i think it can easily stick some rows at bottom also (some like FrozenRowsEnd=1)

Still couldn't find a way to place column summaries at the bottom. Trying some other approaches but whatever i tried decreases performance up to now.

flexGrid for maui also has some other small problems (such as column sort is not working when it is used in unbound mode or assigned to a dataview instead of loader class) but i have solved all with some coding except that subtotal feature.

Thank you @arivoir

arivoir commented 1 year ago

Performance is one of our pillars, together with simplicity and flexibility. I'm glad to hear you noticed it and benefit from it.

I think the issue you're seeing can be related to the usage of StackLayout as parent of FlexGrid. Lately we've been using Grid instead since it's more reliable for these scenarios involving a ui-virtualizing control. Something like below should let you add another element on top(or bottom) of the FlexGrid keeping the ui adaptive

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition />
    </Grid.RowDefinitions>
    <SomeElement />
    <c1:FlexGrid Grid.Row="1"/>
</Grid>

Also I'm seeing some samples in out suite (Coming from Xamarin days) that use StackLayout are wrapping FlexGrid with a Grid like this

<StackLayout>
    <SomeElement />
    <Grid VerticalOptions="FillAndExpand">
        <c1:FlexGrid />
    </Grid>
</StackLayout>

Maybe that helps.

Regarding the lack of sort in unbound mode is made intentionally, because of performance. When an ItemsSource is assigned to the grid not all the rows are created, and the sort operation ends up going directly to the data (through a special collection IDataCollection). Only the data is sorted, and the rows don't need to be created at all. The same happens with filter and group operations. All of them are delegated to the IDataCollection.

I didn't get the issue with "dataview". Maybe you can give more details about it.

enkaradag commented 1 year ago

Thank you @arivoir

Yes i get the reason for unbound mode. For my case i have to use a datatable bound to the grid (i dont always know what columns will be there in data at design time) so i use an approach like;

DataTable dt=new DataTable();
dt.Columns.Add("COL1",typeof(string));
dt.Columns.Add("COL2",typeof(string));
dt.Rows.Add("COL1 DATA","COL2 DATA");

DataView dv=dt.DefaultView;

grid.Columns.Add(new C1.Maui.Grid.GridColumn() { Binding = "[" + "COL1" + "]"};
grid.Columns.Add(new C1.Maui.Grid.GridColumn() { Binding = "[" + "COL2" + "]"};

grid.ItemSource=dv;

this works like a charm (even with 200+ columns and 10000+ rows, both for winUI, android, ios, maccatalyst). But grid's internal sorting doesnt work with this approach. So i catched tap column header myself and sort data in dataview. this solved my case. Solved search and filter issues using the same approach with no performance lose also

flexgridsort


For column subtotal issue;

I plan to put labels below grid and will try to align those labels with the corresponding columns on layout changes (such as horizontal scroll and column width changes). But grid does not seem fire its "ScrollPositionChanged" event (tested for WinUI).

        grid.ScrollPositionChanged += async (s, e) =>
        {
//never reaching here
            Debug.WriteLine("I AM SCROLLED!");
        };

For another issue;

I want to change font attributes of some columns (column data has to be bold font in some cases) I create such columns like;

        GridColumn colx = new GridColumn() { };
        colx.CellTemplate = new DataTemplate(() =>
        {
            Label l = new Label() { FontAttributes = FontAttributes.Bold,VerticalOptions=LayoutOptions.Center };
            l.SetBinding(Label.TextProperty, "[" + "COL1" + "]", BindingMode.Default, null, null);
            return l;
        });
        datagrid.Columns.Add(colx);

But using celltemplates decreases performance. Another technique is CellFactory. But i couldn't make it work for my case. Let's say, we want to make the column "KOD" shown in bold font, I create a CellFactory for that;

    public class MyCellFactory : GridCellFactory
    {
        public override void PrepareCell(GridCellType cellType, GridCellRange range, GridCellView cell, Thickness internalBorders)
        {
            base.PrepareCell(cellType, range, cell, internalBorders);
        }

        public override void BindCellContent(GridCellType cellType, GridCellRange range, View cellContent)
        {
            base.BindCellContent(cellType, range, cellContent);

            if (cellType == GridCellType.Cell)
            {
                (cellContent as Label).VerticalTextAlignment = TextAlignment.Center;
                if (Grid.Columns[range.Column].ColumnName == "KOD")
                {
                    (cellContent as Label).FontFamily = "fnt_bold";
                }
            }
        }

        public override void UnbindCellContent(GridCellType cellType, GridCellRange range, View cellContent)
        {
            base.UnbindCellContent(cellType, range, cellContent);
            var label = cellContent as Label;
            if (label != null)
            {
                label.FontAttributes = FontAttributes.None;
            }
        }
    }

The result has some problems (as seen, some unexpected cells are changing font family also); (actually using label.FontAttributes=FontAttributes.Bold works, but changing font family results like this)

flexgridsort2


And "a row auto-height feature based on the cell data" would be good for flexgrid;

As a responsive mode implementation, i plan to create a one column grid with a complex celltemplate for mobile phone mode instead of scrolling horizontally.


It would be better, i would create seperate issues for each of those

As a summary, flexGrid works best among all, in terms of load & scroll performance, features like column drag sizing and reordering. My only need here is; the frozen row at bottom for column summaries.

Thank you for your help @arivoir

enkaradag commented 1 year ago

flexGrid is not firing its ResizingColumn or ResizedColumn events (tested for WinUI and Android)

    private async void datagrid_ResizingColumn(object sender, GridCellRangeEventArgs e)
    {
        //never reaches here
        _SummaryAlign();

    }
    private async void datagrid_ResizedColumn(object sender, GridCellRangeEventArgs e)
    {
        //never reaches here
        _SummaryAlign();

    }
arivoir commented 1 year ago

@enkaradag Regarding sort issue. There is a simple way to fix it. By default, Maui FlexGrid will not make any special handling for DataTable (we didn't expect Maui customers to use DataTable), so it will treat it as an IList, but there is a package C1.DataCollection.BindingList you can use to wrap the DataView and this will handle sort and filter to be applied to DataView as expected.

You'll need a reference to the package

<PackageReference Include="C1.DataCollection.BindingList" Version="1.0.20232.164" />

and then wrap the DataTable before setting it to the grid

using C1.DataCollection.BindingList;
grid.ItemSource = new C1BindingListDataCollection(dv);

I'll look into the rest of the issues.

arivoir commented 1 year ago

Regarding CellFactory issue, what you're seeing is a recycling issue. The cell recycling is one of the main features contributing to have good performance, but it can be tricky to customize. The idea is everything is changed in the BindCellContent method must be undone in the UnbindCellContent method, and also change as little as possible. The less is changed the faster the layout will compute.

In your case I see you're changing "FontFamily" in BindCellContent, but then in the UnbindCellContent method you're changing FontAttributes property. My guess it's it should change FontFamily back to its default value instead.

arivoir commented 1 year ago

Regarding row auto-height, there is the property

DefaultRowHeight="Auto"

By default is set to a fixed size, because using fixed sizes has better performance and that's suitable for most customers, but you can change it so the row-height is calculated from the content of the cells.

Not sure if it fits in your scenario, but for mobile case you might use row-detail feature, this allows to show a couple columns (so it doesn't scroll horizontally) and have more information in the details row, which can have a complex template.

arivoir commented 1 year ago

@enkaradag the issues with the events were fixed and will be released in the next release.

enkaradag commented 1 year ago

Hi @arivoir, thank your for your replies,


I already solved "tap at column header sorting" and "filter at all data" at the dataview side, it's working perfect now


Regarding CellFactory; one should define the "else" condition for the correct result. I solved it already. And yes, as expected, less things customized, better performance gained.

i was using without "else" condition thinking it gets the default value for else. but its ok, now its working good

            if (cellType == GridCellType.Cell)
            {
                (cellContent as Label).VerticalTextAlignment = TextAlignment.Center;
                if (Grid.Columns[range.Column].ColumnName == "KOD")
                {
                    (cellContent as Label).FontFamily = "fnt_bold";
                }
                else     ///i was using without this "else" condition
                {
                    (cellContent as Label).FontFamily = "fnt_regular";
                }
            }

I just couldn't figure out how to set label properties for column headers with CellFactory, because it doesn't have a (cellContent as Label) cast there


For the subtotal issue, i solved it. Seems abit weird. but works good:)

Those labels follow the columns to align below them. After GridScrolled event working, i can get a better performance. Now i am using a timer.

flexgridsubtotal


I will try row auto height option with a complex template soon. I think it won't lose performance with just 1 column even it has a template inside.

And i have afew tests more (multi-select with checkboxes, contextPopup options for winUI and macOS etc.) to use it here in my current project.

LongPress, DoubleClick functions are really handy. As a summary flexgrid works really good.

Thank you for your help.

Regards