Show Hourglass Pointer on lengthy operations

18 posts, 1 answers
  1. erwin
    erwin avatar
    358 posts
    Member since:
    Dec 2006

    Posted 16 Sep 2010 Link to this post

    With large grids (>100'000 rows) sorting columns can take a lot of time, would be nice if the Grid would show the HourGlass Pointer during such internal operations.
    Is there a way I can enable this?

    Regards
    Erwin
  2. Richard Slade
    Richard Slade avatar
    3000 posts
    Member since:
    May 2009

    Posted 17 Sep 2010 Link to this post

    Hi, 

    I think this should work for you. 
    Private Sub RadGridView1_SortChanged(ByVal sender As System.Object, ByVal e As Telerik.WinControls.UI.GridViewCollectionChangedEventArgs) Handles RadGridView1.SortChanged
        Me.RadGridView1.Cursor = Cursors.Default
    End Sub
     
    Private Sub RadGridView1_SortChanging(ByVal sender As System.Object, ByVal e As Telerik.WinControls.UI.GridViewCollectionChangingEventArgs) Handles RadGridView1.SortChanging
        Me.RadGridView1.Cursor = Cursors.WaitCursor
    End Sub
    Richard
  3. UI for WinForms is Visual Studio 2017 Ready
  4. erwin
    erwin avatar
    358 posts
    Member since:
    Dec 2006

    Posted 17 Sep 2010 Link to this post

    Thanks Richard!

    However, I have 2 problems

    - Changing the Cursor in two places can leave the app with the Wait Cursor if for some reason the SortChanged Event handler does not fire.
     
    - The SortChanging event does not seem to fire when only the direction of the sort changes but not the column (?)

    Erwin
  5. Richard Slade
    Richard Slade avatar
    3000 posts
    Member since:
    May 2009

    Posted 17 Sep 2010 Link to this post

    Hi Erwin,

    Yes, it's a good point about changing the cursor in two places, but I don't think it would be up to the grid internally to manage the cursor either.
    Don't have access to a dev environment at the moment, but from memory, I think you're right in that it doesn't fire when changing the direction of the coloumn. Just the change of column to sort on fires the event (which in my opinion is incorrect behaviour).

    But even if the event fired at all the correct times, I'm not sure if there would be another way around it. Be interested to see what you come up with. If I think of another way, I'll be sure to post it.
    Richard
  6. Martin Vasilev
    Admin
    Martin Vasilev avatar
    1061 posts

    Posted 21 Sep 2010 Link to this post

    Hello guys,

    Using SortChanging and SortChanged events is the right way to change the cursor to hourglass. However, it is true that SortChanging event does not fire if only the sort direction is changed. This is an issue and we are going to address it in the next release. Until the issue is addressed, however, there is no workaround that will allow you to implement the desired scenarion. 

    Best wishes,
    Martin Vasilev
    the Telerik team
    Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Public Issue Tracking system and vote to affect the priority of the items
  7. Answer
    Emanuel Varga
    Emanuel Varga avatar
    1336 posts
    Member since:
    May 2010

    Posted 24 Sep 2010 Link to this post

    Hello guys,

    I would suggest a workaround for the time being, it's not very clean and nice but it does what it should do, and until the SortChanging event will be fixed you don't need to use that method anymore.

    First, in the form, register for the SortChanged event, and for the ContextMenuOpening event:
    radGridView1.SortChanged += radGridView1_SortChanged;
    radGridView1.GridBehavior = new MyBehavior();
    radGridView1.ContextMenuOpening += radGridView1_ContextMenuOpening;

    Second, we will create a custom behavior that checks if the left mouse click is in the cell header element and if so, set the Cursor to waiting state:
    public class MyBehavior : BaseGridBehavior
        {
            protected override bool OnMouseDownLeft(MouseEventArgs e)
            {
                var element = this.GridControl.ElementTree.GetElementAtPoint(e.Location) as GridHeaderCellElement;
                if (element != null)
                {
                    this.GridControl.Cursor = Cursors.WaitCursor;
                }
     
                return base.OnMouseDownLeft(e);
            }
        }

    After this we will create the SortChanged event where we can set the cursor to the default state:
    private void radGridView1_SortChanged(object sender, Telerik.WinControls.UI.GridViewCollectionChangedEventArgs e)
    {
        this.radGridView1.Cursor = Cursors.Default;
    }

    With this everything will work fine if the user click on the table header element, but there is still on thing which we have to take care of if we are slowing also the header context menu, because sort operations can also be accessed from there,
    void radGridView1_ContextMenuOpening(object sender, ContextMenuOpeningEventArgs e)
    {
        if (e.ContextMenuProvider == null)
        {
            return;
        }
     
        foreach (var item in e.ContextMenu.Items)
        {
            if (item.Text.ToLower().Contains("sort") && ((RadMenuItem)item).ToggleState != ToggleState.On)
            {
                item.MouseUp += item_MouseUp;
            }
        }
    }
     
    void item_MouseUp(object sender, MouseEventArgs e)
    {
        this.radGridView1.Cursor = Cursors.WaitCursor;
    }

    Here we are checking if the ContextMenuProvider is not null, and after that going trough all of the items in the menu, and those of which contain the sort keyword and are not currently selected we will register them for the MouseUp event to be able to set the cursor to waiting state before performing the operation.

    I hope this helps, please let me know if there is something I've missed here.

    By the way there is still one problem, when grouping not even the SortChanged event is fired after sorting...., if it were to fire then the custom behavior would change like this:
    public class MyBehavior : BaseGridBehavior
    {
        protected override bool OnMouseDownLeft(MouseEventArgs e)
        {
            var element = this.GridControl.ElementTree.GetElementAtPoint(e.Location) as GridHeaderCellElement;
            if (element != null)
            {
                this.GridControl.Cursor = Cursors.WaitCursor;
            }
            else if (this.GridControl.EnableGrouping)
            {
                var groupFieldElement = this.GridControl.ElementTree.GetElementAtPoint(e.Location) as GroupFieldElement;
                if (groupFieldElement != null)
                {
                    this.GridControl.Cursor = Cursors.WaitCursor;
                }
            }
     
            return base.OnMouseDownLeft(e);
        }
    }

    BUT, because that event doesn't fire either you will just have always a waiting cursor, so for the time being the previous version should be used...

    Best regards,
    Emanuel Varga
  8. erwin
    erwin avatar
    358 posts
    Member since:
    Dec 2006

    Posted 27 Sep 2010 Link to this post

    Thanks, Emanuel for you in-depth analysis and suggestion.

    Regards
    Erwin
  9. Wayne
    Wayne avatar
    36 posts
    Member since:
    May 2013

    Posted 16 May 2013 Link to this post

    Hi all,

    I'm looking for an update to this topic. I just now realized that a particular grid has many rows, and sorting does indeed take a bit of time.  Long enough to warrant an hourglass.

    Martin mentioned that the next release would contain a fix.  While I am new to utilizing the Telerik controls, I would assume that since 2010 an update has been released.  :)

    So my question is, how can we do this and in a clean, non-convoluted way?

    Best regards,

    Wayne
  10. Martin Vasilev
    Admin
    Martin Vasilev avatar
    1061 posts

    Posted 21 May 2013 Link to this post

    Hi Wayne,

    We introduced some performance improvements for the sorting operation in our latest release. The new implementation uses hybrid internal data structure that holds the sorted rows of RadGridView. The new implementation uses the quick sort algorithm when the Rows collection contains up to 10000 rows. When the Rows collection contains more than that the sorting operation uses a red-black binary tree to perform the operation.

    However, this optimization might cause issue when there is repetitive data in the grid and in this situation the new algorithm works slower, so you will have to switch back to the old one. To do that we added new property to control this behavior. However, the new API will be available with next our release.

    Currently, as a workaround you can change the sorting behavior using this code snippet based on reflection:

    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
      
        private void Form1_Load(object sender, EventArgs e)
        {
      
            IList<Book> bookList = new List<Book>();
      
            for (int i = 0; i < 10000; i++)
            {
                bookList.Add(new Book { Price = new Random(i).NextDouble() * 100, HardCover = i % 2 == 0 ? "Y" : "N" });
            }
      
            grdBooks.DataSource = bookList;
      
            grdBooks.SortChanging += new Telerik.WinControls.UI.GridViewCollectionChangingEventHandler(grdBooks_SortChanging);
        }
      
        bool initIndex = false;
        void grdBooks_SortChanging(object sender, Telerik.WinControls.UI.GridViewCollectionChangingEventArgs e)
        {
            if (!initIndex)
            {
                AvlIndex<GridViewRowInfo> index = new AvlIndex<GridViewRowInfo>(grdBooks.MasterTemplate.DataView);
                FieldInfo fieldInfo = typeof(RadDataView<GridViewRowInfo>).GetField("indexer", BindingFlags.GetField | BindingFlags.Instance | BindingFlags.NonPublic);
                if (fieldInfo != null)
                {
                    fieldInfo.SetValue(grdBooks.MasterTemplate.DataView, index);
      
                }
      
                fieldInfo = typeof(RadDataView<GridViewRowInfo>).GetField("groupBuilder", BindingFlags.GetField | BindingFlags.Instance | BindingFlags.NonPublic);
                if (fieldInfo != null)
                {
                    fieldInfo.SetValue(grdBooks.MasterTemplate.DataView, new GroupBuilder<GridViewRowInfo>(index));
                }
      
                initIndex = true;
            }
        }
    }

    If this does not help, please provide us with an example demonstrating the issue, so we can investigate it and help you resolve it. If you want, you can open a new support ticket where you can attach a whole project.

    Looking forward to your reply.

    Best wishes,
    Martin Vasilev
    the Telerik team
    Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Public Issue Tracking system and vote to affect the priority of the items
  11. Wayne
    Wayne avatar
    36 posts
    Member since:
    May 2013

    Posted 31 May 2013 Link to this post

    I'm just looking to show an hourglass while sorting, all my sorting ops are pretty fast, save for one or two items...I decided to go with Richard Slade's solution, since it meets the KISS principle, and I have other fish to fry with my time. :)

    Still, love the controls!  I've waited for over 10 years to play with them and there was no way I was going to pay for them myself.

    Thanks!

    Wayne
  12. Peter Stanford
    Peter Stanford avatar
    7 posts
    Member since:
    Mar 2010

    Posted 17 Oct 2013 Link to this post

    Hi,

    I've been following this to see why my code doesn't work. I'm only after the ability to change the cursor to an hourglass while I'm loading up my grid. I have a RadGridView on a windows form. I have a set of radio buttons containing 8 options for showing a list of products, of which there are about 5000 active at any one time. When the user selects one of the radio buttons and then clicks a "Search" button, I run the following code which does not change the cursor to an hourglass. Is the change of cursor only fired on a RadGridView event and if so, which one would I use in my case?

    Thanks and best regards

    Peter
    With RadGridView2
        .BeginUpdate()
        .Cursor = Cursors.WaitCursor
        .DataSource = Collection
        .MasterTemplate.AutoSizeColumnsMode = GridViewAutoSizeColumnsMode.Fill
        .Dock = DockStyle.Fill
        .MasterTemplate.AllowAddNewRow = False
        .MasterTemplate.EnableSorting = True
     
        Dim descriptor As New SortDescriptor()
        descriptor.PropertyName = "Invoice_Description"
        descriptor.Direction = ListSortDirection.Ascending
        .MasterTemplate.SortDescriptors.Add(descriptor)
     
        If .Rows.Count > 0 Then
            .AllowEditRow = False
            .AllowDeleteRow = False
            .AllowAddNewRow = False
            .Columns("EntityKey").IsVisible = False
            .Columns("EntityDisplayName").IsVisible = False
            .Columns("RowVersion").IsVisible = False
            .Columns("ID").IsVisible = False
            .Columns("CreationTime").IsVisible = False
            .Columns("CreationUser").IsVisible = False
            .Columns("LastWriteTime").IsVisible = False
            .Columns("LastWriteUser").IsVisible = False
            .Columns("Exclusive_CustomerID").IsVisible = False
            .Columns("Exclusive_Customer").IsVisible = False
            .Columns("BrandID").IsVisible = False
            .Columns("UOM_UnitID").IsVisible = False
            .Columns("UOM_ItemID").IsVisible = False
            .Columns("Unit_Barcode").IsVisible = False
            .Columns("Item_Barcode").IsVisible = False
            .Columns("Unit_Weight").IsVisible = False
            .Columns("Item_Weight").IsVisible = False
            .Columns("Product_GroupID").IsVisible = False
            .Columns("Unit_Actual_Cost").IsVisible = False
            .Columns("Pallet_Quantity").IsVisible = False
            .Columns("Pallet_Weight").IsVisible = False
            .Columns("GST_Percentage").IsVisible = False
            .Columns("Pallet_Layer_Quantity").IsVisible = False
            .Columns("Supplier_Discount_Type_1").IsVisible = False
            .Columns("Supplier_Discount_type_2").IsVisible = False
            .Columns("Supplier_Discount_Amount_1").IsVisible = False
            .Columns("Supplier_Discount_Amount_2").IsVisible = False
            .Columns("Unit_Average_Cost").IsVisible = False
            .Columns("Appropriation_Code").IsVisible = False
            .Columns("Appropriation_CodeID").IsVisible = False
            .Columns("Include_In_StockTake").IsVisible = False
            .Columns("Is_Stocked_Product").IsVisible = False
            .Columns("Is_BuyIn_Product").IsVisible = False
            .Columns("Is_Specialty_Product").IsVisible = False
            .Columns("Is_Quantity_Buy").IsVisible = False
            .Columns("Is_Seasonal_Product").IsVisible = False
            .Columns("Is_Halal_Product").IsVisible = False
            .Columns("Is_Gluten_Free_Product").IsVisible = False
            .Columns("Is_Vegetarian_Product").IsVisible = False
            .Columns("Classification").IsVisible = False
            .Columns("Sub_Classification").IsVisible = False
            .Columns("ClassificationID").IsVisible = False
            .Columns("Sub_ClassificationID").IsVisible = False
            .Columns("School_Canteen_Colour").IsVisible = False
            .Columns("Alternate_Code").IsVisible = False
            .Columns("Alternate_Description").IsVisible = False
            .Columns("Is_Discount_Allowed").IsVisible = False
            .Columns("Freight_Category").IsVisible = False
            .Columns("Freight_Cost").IsVisible = False
            .Columns("Description_Brand_Size").IsVisible = False
            .Columns("System_ComponentModel_IDataErrorInfo_Error").IsVisible = False
            .Columns("CodeFluent_Runtime_Utilities_IKeyable_System_Int32__Key").IsVisible = False
            .Columns("EntityState").IsVisible = False
            For Each column As GridViewDataColumn In .Columns
                column.BestFit()
                column.ReadOnly = True
            Next
            RadGridView2.EndUpdate()
            .Visible = True
        Else
            MsgBox("There are no Products matching your search criteria")
        End If
        RadGridView2.Cursor = Cursors.Default
     
    End With
    'Catch ex As Exception
    '    MsgBox("What's going on?")
    'End Try
    RadGridView2.Cursor = Cursors.Default

  13. George
    Admin
    George avatar
    500 posts

    Posted 22 Oct 2013 Link to this post

    Hello Peter,

    Thank you for writing.

    If you set the cursor of the grid and immediately start a heavy operation the grid has no time to process the Cursor request as the UI thread becomes busy. There are two solutions in this case:
    1. Set the cursor and create a timer with some interval. In the timer's tick event start the heavy operations. This will give enough time for the grid to update its cursor.
    2. Use the Cursor property of the Form. This will set the form's cursor while the grid processes the data:
      void btn_Click(object sender, EventArgs e)
      {
          this.Cursor = Cursors.WaitCursor;
          List<string> list = new List<string>();
          for (int i = 0; i < 100000; i++)
          {
              list.Add("STR");
          }
       
          this.grid.DataSource = list;
          this.Cursor = Cursors.Default;
      }

    I hope this will be suitable for your case. Let me know if there is anything else I can help you with.

    Regards,
    George
    Telerik
    TRY TELERIK'S NEWEST PRODUCT - EQATEC APPLICATION ANALYTICS for WINFORMS.
    Learn what features your users use (or don't use) in your application. Know your audience. Target it better. Develop wisely.
    Sign up for Free application insights >>
  14. Peter Stanford
    Peter Stanford avatar
    7 posts
    Member since:
    Mar 2010

    Posted 22 Oct 2013 Link to this post

    Hi George,

    Thanks, I tried a few different options and ultimately the simplest was to use the form-based approach.

    Thanks and best regards

    Peter
  15. George
    Admin
    George avatar
    500 posts

    Posted 25 Oct 2013 Link to this post

    Hi Peter,

    I am glad that this worked out for you.

    Do not hesitate to contact us if you need any further assistance.

    Regards,
    George
    Telerik
    TRY TELERIK'S NEWEST PRODUCT - EQATEC APPLICATION ANALYTICS for WINFORMS.
    Learn what features your users use (or don't use) in your application. Know your audience. Target it better. Develop wisely.
    Sign up for Free application insights >>
  16. Kurt
    Kurt avatar
    11 posts
    Member since:
    Dec 2007

    Posted 03 Nov 2013 Link to this post

    Hi Martin,

    I notice in your reply that the Q3 release might now have the option to change the sorting style by column to the old sort method?

    I have 7 columns of which 6 sort great. One is a string column with a selection of string values which can repeat a lot and the sorting on this column takes upwards of 10 seconds with up to 8000 values. Is there now a way to change the sorting behaviour on just this column? I couldn't figure out an elegant way to do it, I was trying to create a numeric column based on the strings, have it hidden and then do a sort on that, but it caused issues with the normal sorting on other columns.
  17. George
    Admin
    George avatar
    500 posts

    Posted 06 Nov 2013 Link to this post

    Hello Kurt,

    Thank you for contacting us.

    The following code will change the Threshold which defines what algorithm to be used when sorting:
    RadDataView<GridViewRowInfo> dataView =this.grid.GridViewElement.Template.ListSource.CollectionView as RadDataView<GridViewRowInfo>;
    FieldInfo indexerField =typeof(RadDataView<GridViewRowInfo>).GetField("indexer", BindingFlags.NonPublic | BindingFlags.Instance);
    HybridIndex<GridViewRowInfo> indexer = indexerField.GetValue(dataView) as HybridIndex<GridViewRowInfo>;
    indexer.Threshold = 500;

    With Threshold of 500 if the count of the data items is more than 500 the sorting operation will be performed with a BinaryTree.

    I hope this helps.

    Regards,
    George
    Telerik
    TRY TELERIK'S NEWEST PRODUCT - EQATEC APPLICATION ANALYTICS for WINFORMS.
    Learn what features your users use (or don't use) in your application. Know your audience. Target it better. Develop wisely.
    Sign up for Free application insights >>
  18. Kurt
    Kurt avatar
    11 posts
    Member since:
    Dec 2007

    Posted 07 Nov 2013 Link to this post

    Thanks that did the trick, sorting is back to full speed.
  19. George
    Admin
    George avatar
    500 posts

    Posted 12 Nov 2013 Link to this post

    Hello Kurt,

    I am glad that the problem is solved.

    Do not hesitate to contact us if you have any further questions.

    Regards,
    George
    Telerik
    TRY TELERIK'S NEWEST PRODUCT - EQATEC APPLICATION ANALYTICS for WINFORMS.
    Learn what features your users use (or don't use) in your application. Know your audience. Target it better. Develop wisely.
    Sign up for Free application insights >>
Back to Top
UI for WinForms is Visual Studio 2017 Ready