This is a migrated thread and some comments may be shown as answers.

BestFit method not working as expected

13 Answers 424 Views
GridView
This is a migrated thread and some comments may be shown as answers.
Leonardo
Top achievements
Rank 1
Leonardo asked on 26 Aug 2011, 01:31 PM
Hi,

I'm trying to use the BestFit method dynamically to resize the column width property as the largest word in my DataSet in one column specific. But when I use this method, the application fit the column as the largest word that I can see, and if I slide the scroll down, some words in the grid column is truncated. 

How can I do to display all my grid data with no truncate words?

Regards

13 Answers, 1 is accepted

Sort by
0
erwin
Top achievements
Rank 1
Veteran
Iron
answered on 29 Aug 2011, 08:18 AM
Hi Leonardo

The function works as documented for all visible rows. I guess that telerik chose to limit the measuring to visible rows because of performance reasons.

Limiting sizing to visible rows has other advantages, when a non-visible cell is considerably wider than all visible cells of the same column that would otherwise lead to waste of space for the displayed portion of the grid.

If you know your data (and formatting rules) you can always set the width of a column manually, by measuring a typical string with the current font of the grid.

Erwin
0
Alexander
Telerik team
answered on 31 Aug 2011, 01:57 PM
Hello guys,

Thank you, Erwin, for the provided explanations and comments. BestFit for all cells of a particular column could be implemented by creating cell element for each data cell of the column and measuring this element. The following code snippet demonstrated one approach to achieve it:
private void BestFitAllCells(GridViewColumn column)
{
    IVirtualizedElementProvider<GridViewRowInfo> rowProvider = this.radGridView1.TableElement.RowElementProvider;
    IVirtualizedElementProvider<GridViewColumn> cellProvider = this.radGridView1.TableElement.ColumnScroller.ElementProvider;
 
    float width = 0;
    foreach (GridViewRowInfo dataRow in this.radGridView1.Rows)
    {
        GridRowElement row = rowProvider.GetElement(dataRow, null) as GridRowElement;
        row.InitializeRowView(this.radGridView1.TableElement);
        row.Initialize(dataRow);
        row.UpdateInfo();
 
        this.radGridView1.TableElement.Children.Add(row);
        row.ResetLayout(true);
 
        GridVirtualizedCellElement cell = cellProvider.GetElement(column, row) as GridVirtualizedCellElement;
        cell.Attach(column, row);
 
        cell.SetContent();
        cell.UpdateInfo();
 
        row.Children.Add(cell);
 
        GridHeaderCellElement headerCell = cell as GridHeaderCellElement;
        if (headerCell != null)
        {
            headerCell.UpdateArrowState();
        }
 
        cell.ResetLayout(true);
        width = Math.Max(width, this.GetCellDesiredWidth(cell));
 
        row.Children.Remove(cell);
        this.radGridView1.TableElement.Children.Remove(row);
 
        this.Detach(cellProvider, cell);
        this.Detach(rowProvider, row);
    }
 
    column.Width = (int)width;
}
 
private float GetCellDesiredWidth(GridCellElement cell)
{
    cell.Measure(new SizeF(float.PositiveInfinity, float.PositiveInfinity));
    return cell.DesiredSize.Width;
}
 
private void Detach(IVirtualizedElementProvider<GridViewColumn> elementProvider, GridCellElement cell)
{
    GridVirtualizedCellElement virtualizedCell = cell as GridVirtualizedCellElement;
 
    if (virtualizedCell != null)
    {
        elementProvider.CacheElement(virtualizedCell);
        virtualizedCell.Detach();
        return;
    }
 
    cell.Dispose();
}
 
private void Detach(IVirtualizedElementProvider<GridViewRowInfo> elementProvider, GridRowElement row)
{
    GridVirtualizedRowElement virtualizedRоw = row as GridVirtualizedRowElement;
 
    if (virtualizedRоw != null)
    {
        elementProvider.CacheElement(virtualizedRоw);
        virtualizedRоw.Detach();
        return;
    }
 
    row.Dispose();
}

I hope it helps.

Best regards,
Alexander
the Telerik team

Thank you for being the most amazing .NET community! Your unfailing support is what helps us charge forward! We'd appreciate your vote for Telerik in this year's DevProConnections Awards. We are competing in mind-blowing 20 categories and every vote counts! VOTE for Telerik NOW >>

0
Abba
Top achievements
Rank 1
answered on 24 Sep 2016, 06:05 PM

This is not true, or something is not working correct.
I do not find that it is only using the visible columns.  Or there is issue with this.

Here is simple test:
I load 1000 rows of data from a SQL table without bestfit.  It is fast.
I load same 1000 rows with bestfit.  It takes awhile.
I load from same table, but with 5000 rows and with bestfit.  It takes much longer!

If the bottle next is the bestfit (which it clearly is), and if it only uses the visible rows, the time difference between 1000 and 5000 rows should be very little if any.  But it is huge.

Either this is a bug that needs to be addressed, or it is not correctly documented/supported.   I have tried with 2013 Q3 and well as 2015 Q3 (awhile back).

Also, I will state again, this bestfit is VERY needed, yet VERY VERY slow (unuseable). 
This has been an ongoing issue with RadGridView that just doesn't seem to get resolved.  I think it is false advertising because in your demos it shows loading millions of rows, yet if you put bestfit on it'll take over 10 seconds just to load 10k.  You could never get near 1million and still have a usable application.
I really don't see what is so hard about getting the visible rows calculating the widths and applying them.  It should not take as long as it does.

0
erwin
Top achievements
Rank 1
Veteran
Iron
answered on 24 Sep 2016, 08:52 PM

 

Did you load the grid using deferred refresh? To my experience that can make a huge difference.

I usually let the user choose the width (and other aspects of the layout like column order) they like and use the Load/Save Layout. Another approach I sometimes use is to calculate the width of the probably largest column myself. Since the application knows the data it displays, it can make a guess that works most of the time.

Regards Erwin

 

 

0
Hristo
Telerik team
answered on 26 Sep 2016, 03:21 PM
Hi guys,

Thank you for writing.

When your grid is data bound to a great number of items, calling the BestFit method with the BestFitColumnMode.AllCells parameter may indeed cost an additional time. The reason for this is that all of the cells need to be iterated, measured and sized. I can suggest best fitting the columns once you have set the data source.

You can also check the following forum threads discussing a similar question:  Additionally, you may consider displaying a waiting bar indicating that the grid is performing a long running operation: http://www.telerik.com/support/kb/winforms/track-and-status/details/indicating-progress-in-applications-that-have-their-primary-ui-thread-busy.

I hope this helps. Should you have further questions please do not hesitate to write back.

Regards,
Hristo Merdjanov
Telerik by Progress
Check out the Windows Forms project converter, which aids the conversion process from standard Windows Forms applications written in C# or VB to Telerik UI for WinForms. For more information check out this blog post and share your thoughts.
0
Abba
Top achievements
Rank 1
answered on 27 Sep 2016, 04:09 AM
Thank you both for quick reply.

1-Erwin: what do you mean by deferred refresh?  If you mean begin/end update, yes it is wrapped.  The problem with the layout is i am running queries with different data in the same grid.  for example, sometimes it is from one table, sometimes from another.
I will also mention that the begin/end update seems to have issue as i have used it in other parts of code and it was noticable slower with it on.  seems strange, but i confirmed it.  But in this part, it is fine

2-Hristo: why does all cells need to be iterated, measured, sized instead of just the visible ones?  This is also contrary to what was stated earlier in the thread.  Can someone get to the bottom of this and determine which way it works?  It seems like it would be faster to only check the visible ones, but it doesn't seem to work like that.  As I mentioned I am running the same query in the same size results window (same amount of visible rows: 25) with a big difference in time between 1000 and 5000 rows.
I also only bestfit after the datasource is set (as you mentioned).

I have also tested just showing 1 row visible and it is taking longer for 1000 vs 3000.  Again confirming my theory that it is not running only on the visible and indeed is running all rows.  A waste.  If anything there should be a setting for each, but default to only visible.

SSMS is my comparison and it is MUCH faster and also bestfits.  Why does this take so much more time?  It should be able to match that performance at least.  and it is close to same speed without the bestfit.  

I hesitate to return anything over 500 rows because of the delay.  But when I need to query a lot, this becomes a bottle neck.

My only last test (which means i now have to be your developer) is to run a calc on the first x cols and set the width each time to see if that is faster.  I feel it will be, especially noticeable on rows over 1000.
Again, showing that this is an ongoing issue (your links show since 2008) with performance.

Here is my code.  I hope someone could look into and finially resolve so i don't have todo each work arounds.

'set the datasource
GridView.DataSource = Nothing
GridView.DataSource = GetDataTable()

'set bestfit
GridView.TableElement.BeginUpdate()
GridView.MasterTemplate.BestFitColumns(BestFitColumnMode.AllCells)
GridView.TableElement.EndUpdate()
0
erwin
Top achievements
Rank 1
Veteran
Iron
answered on 27 Sep 2016, 04:43 AM

 

I'm assuming you are using auto-generated columns. I usually use code like this (pseudocode): 

To my experience, when using AutoGenerated columns, it's better not to re-assign a new DataTable with every Refresh, but to just reload the existing DataTable. The grid gets notified by the change in the DataTable and updates itself accordingly.

Also it's key to have the assignment of the datasource within the Begin/EndUpdate Block.

With this construct I routinely load 10'000 rows with dozens of columns in under 1 sec, including the sql server database query execution time.

 

using (gridView.DeferRefresh())  // (calls BeginUpdate())
{
    if (dataTable = null)
    {
        // initial load
        dataTable = datalogic.GetDataTable();
         
        gridView.DataSource = dataTable;
 
        if(layoutpersistence.HasSavedLayout(gridView))
        {
            gridView.LoadLayout(layoutpersistence.GetSavedLayout(gridView));
        }
        else
        {
            gridView.BestFitColumns();
        }
    }
    else
    {
        // refresh, do not change grid layout and do not re-assign the datasource to the grid
        datalogic.UpdateDataTable(dataTable);
    }
} // implicitely calls EndUpdate() even if the datalogic errs out 

 

regards

Erwin

 

 

0
Abba
Top achievements
Rank 1
answered on 27 Sep 2016, 05:32 AM

Thank you again for getting back to me so quick.

First, sorry for being cranky.

Second, I did miss something...apparently there is an option in the bestfit for displayed versus all.  When I set this, it is faster, and on the larger data sets as well.

Third, I will have to try the update method.  I have used it before on another grid in the application with poor results (and from what i recall issues. do i have to reset the master template?  and/or the gains weren't seen noticed).  It was a self referencing (hierarchy) grid though.

Fourth, can you send the update method code or pseudo so i can make sure it is correct.  or if it is in another thread/documentation.  or if it is just updating the datasource (meaning, setting the query result into the underlying datatable is all).

 

Regards,

0
Hristo
Telerik team
answered on 27 Sep 2016, 08:25 AM
Hi guys,

Thank you for writing.

The RadGridView.BestFitColumns method receives a BestFitColumnMode parameter determining which cells will be measured. The available options are DisplayedDataCellsHeaderCellsFilterCellsSummaryRowCellsSystemCellsDisplayedCells, and AllCells. The DisplayedCells is indeed a good option when you have a large dataset.

Abba, the provided by Erwin approach is valid. Please also note that it is not necessary to reset the data source of the grid as long as the object to which it is bound notifies it for the update. The various ways to data bind RadGridView are discussed in the following section of the documentation: Populating with Data.

I hope this helps. Should you have further questions please do not hesitate to write back.

Regards,
Hristo Merdjanov
Telerik by Progress
Check out the Windows Forms project converter, which aids the conversion process from standard Windows Forms applications written in C# or VB to Telerik UI for WinForms. For more information check out this blog post and share your thoughts.
0
Abba
Top achievements
Rank 1
answered on 28 Sep 2016, 05:42 AM
Thanks.  I looked in that documentation section and i see ways to bind, but that is not the issue.  Is there specific section with specific code showing this update and notify?  Am I missing it?  Is it just a matter of changing the datatable?  What about:
Gridview.MasterTemplate.Reset()
Gridview.MasterTemplate.Refresh()
?

I remember seeing such technique in a thread on here.  And I believe i tried, but ran into issues.  whether updating or something. That is why i had to resort to the reapplying.  
It is possible that it was the version I am on and it was improved later on.
If you have a sample project already built, that would be great.  No need to create if not.
If you feel there is a performance gain to it (or it is the more correct way), I can try again.

0
Abba
Top achievements
Rank 1
answered on 28 Sep 2016, 05:46 AM

By the way,

This seems outdated.  At the bottom it is setting bestfit on each column (not mastertemplate) and without begin/end update would be working each loop (if i'm not mistaken).

0
Abba
Top achievements
Rank 1
answered on 28 Sep 2016, 05:47 AM
0
Hristo
Telerik team
answered on 29 Sep 2016, 03:12 PM
Hello Abba,

Thank you for writing.

Starting with your last post, calling the BestFit method for each of the columns individually basically achieves the same result as calling the BestFitColumns of the grid. Please note that the BestFit method can be called for a single column if needed and that it also respects the AutoSizeMode property set on the column.

Considering your other question regarding the binding mechanism, as I said previously the grid will automatically reflect any changes in your data object as long as it is notified about the changes. The Reset method called on the master template sets the data source to null and additionally clears all settings like templates or relations. Please check the following documentation article discussing how changes in the data objects can be reflected in the grid: http://docs.telerik.com/devtools/winforms/gridview/populating-with-data/reflecting-custom-object-changes-in-rgv.

I hope this information is useful. Should you have further questions please do not hesitate to write back.

Regards,
Hristo Merdjanov
Telerik by Progress
Check out the Windows Forms project converter, which aids the conversion process from standard Windows Forms applications written in C# or VB to Telerik UI for WinForms. For more information check out this blog post and share your thoughts.
Tags
GridView
Asked by
Leonardo
Top achievements
Rank 1
Answers by
erwin
Top achievements
Rank 1
Veteran
Iron
Alexander
Telerik team
Abba
Top achievements
Rank 1
Hristo
Telerik team
Share this question
or