FlexGrid Refresh of Headers when Source is Refreshed

Posted by: rkane on 27 November 2018, 4:10 am EST

  • Posted 27 November 2018, 4:10 am EST

    When the data source is updated in a way that results in the collection firing a CollectionChanged event with a argument of Reset the entire grid is reloaded instead of just the data portion. We have added a filter row to the header section that needs to maintain state while the data is refreshed and wiping the entire grid clears the state. We have been able to work around most issues that this presents but one that is very problematic is the state of IME input. I used a decompiler to see what the problem was and think it is because the LoadRows method call this.Invalidate instead of this._gCells.Invalidate.


    Version 4.0.20182.595

  • Replied 27 November 2018, 11:52 pm EST

    Hello,

    I tried to replicate the structure of your application and to replicate the issue, however the filter state does not clear upon clearing the grid. Please see attached video.
    Also, I attached a sample for your reference.

    Thanks
    Ruchir

    FlexGrid_CollectionChanged.zip
  • Replied 28 November 2018, 8:33 am EST

    I updated the project to show the issue and included a video. In the video you can see each time I click on the reset button (which now clears the ObservableCollection) the Reset message is shown as is the debug statement in the constructor of the filter cell. There is no need to redraw the Header panel when the data is cleared or reloaded. Most of the time it is not a problem but in a few cases keeping the state of the text box is important.


    Reset causing reload.zipFlexGrid_CollectionChanged Rev1.zip
  • Replied 28 November 2018, 4:22 pm EST

    Hello,

    Thank you for modifying the sample and including a video. I am able to observe that the header cells are re-rendered as soon as the collection is modified (add/delete both). And as I understand, this is by design behavior with no possible workaround.
    I can ask the developer to confirm this but before it, could you please elaborate a bit more about your issue with re-rendering the header cells. Since even after re-rendering the header cells, the text entered in them (filter row) persist, so I request you to kindly share your use-cases where you are facing problem due to this behavior.

    Thanks & regards,
  • Replied 1 December 2018, 6:35 am EST

    When you are using IME for a foreign language, in our case Korean, the state of the input sequence is maintained by the textbox. In the attached video I am typing the sequence 'abca', which produces the first character and begins the second character. Filtering should occur on the final 'a' but the textbox keeps the state of the edit sequence and know the final 'a' is part of the next character. If the textbox is reloaded that state is lost. The other issue is the placement of the cursor within the textbox after the refresh. It is possible to do this but code is not very clean.

    If this is by design please recommend a way to solve the problem.

    Filter Input.zip
  • Replied 2 December 2018, 10:22 pm EST

    Hi,

    Thank you for elaborating further over this. I am in discussion with the developers [ID: 356082] regarding the same.

    Regards,
    Ruchir
  • Replied 6 December 2018, 2:55 pm EST

    Hello,
    Following is the response received from the development team:
    FlexGrid filter inputs are MS TextBox or base on MS TextBox so there is no solution to fix this problem. There is related problem in msdn (https://social.msdn.microsoft.com/Forums/vstudio/en-US/d2e155a2-e333-458c-ab0d-1a6ef9318892/why-the-uncommitted-text-in-the-ime-is-passed-to-textbox?forum=wpf)
    This is an intent design by MS so users must remove last unfinished character and enter the new one after re-focus.

    Apologies for the inconvenience caused due to the issue.

    ~Ruchir
  • Replied 7 December 2018, 3:34 am EST

    The response you gave is not relevant. Please read my original post and see the fix I suggested. There is no reason to refresh the header panel when reloading data.

    The Fix
    Change this.Invalidate() to this._gCells.Invalidate() in the LoadRows routine.

    If there is a reason this cannot be done please let me know.

  • Replied 9 December 2018, 5:33 pm EST

    Hello,
    I am re-discussing your concern with the developer and will get back to you with the information he shares.
    ~Ruchir
  • Replied 17 December 2018, 3:30 am EST

    Any update?
  • Replied 17 December 2018, 3:08 pm EST

    Hi,
    I have asked the developer for an ETA. I will share the same with you as soon as I have one.
    Thanks,
  • Replied 21 December 2018, 5:46 am EST

    Can I get an update? It has been over three weeks since I submitted the request.
  • Replied 23 December 2018, 8:49 pm EST

    Hello,
    I am really sorry for the delay in getting you a response. I have enquired the developers about the ETA and has also increased this issue's Priority, so that it is on the top of their To-Do list.
    I will get back to you soon.
    ~Ruchir
  • Replied 26 December 2018, 9:43 pm EST

    Hello,

    Following is the response I received from the developer:
    When we reset or replace a new item source of FlexGrid, all the panels must be invalidated.
    With your use case, because of reset a new data source so there is no way to control compare the changes between the new and old data source.

    The solution for your use case is when you're filtering the data, you should pending the update interval. In the FilterEditor, the update interval should be pending when Textbox filter GotFocus, combobox GotFocus and the update interval should be resuming when Textbox filter LostFocus, combobox LostFocus.

    ~Ruchir
  • Replied 8 January 2019, 1:59 am EST

    That will not work for the keystroke filtering that we need to implement.

    I would like to talk to the developer. It should be a short conversation.
  • Replied 10 January 2019, 2:05 am EST

    Hello

    My name is Kelley Ricker and I'm the Product Manager for WPF. I saw your issue here and I can confirm it is the intended behavior that all panels are invalidated when the ItemsSource is either replaced or reset. You may be able to update the data source instead though.

    I think what Ruchir suggested (deferring updates while the filter has focus) is one possibility for handling it. Also, another alternative would be to remove items one by one rather than clear it (so in this case you're updating rather than replacing or resetting). So the button click event to reset would look like:

                
    private void Button_Click(object sender, RoutedEventArgs e)
    {
    //EmployeeList.Clear();
    //Clear will reset the Datasource, but you can remove items one by one as an update
    while (EmployeeList.Count > 0)
    {
    EmployeeList.RemoveAt(0);
    }
    }


    In your sample you'd also need to make a similar change where Clear is called in the PopulateList method. That will prevent the headers from being updated in both instances.

    I hope that helps.

    Thanks

    Kelley
  • Replied 10 January 2019, 7:36 am EST

    Kelly,
    Thanks for the reply. Adding and removing items is very slow and, in the large sets we are working with, is not an option.

    I understand why you need to redraw the header section when you change the source but don't understand why it is needed when a reset occurs. If I can do singleton adds or deletes and the header panel is not redrawn then why does it need to redrawn when a bulk operation is done?

    If there is need to reset the header panel when a reset occurs could you provide some means to disable it?

    Our product has the requirement of filtering on each keystroke, supporting IME and handling large collections. Making this work with the current issue is very difficult.
  • Replied 14 January 2019, 2:44 am EST

    We'll look into it some more, but there isn't a direct way around it. The issue in this specific case is that using the ObservableCollection's Clear method will raise NotifyCollectionChanged.Reset which means that the underlying data has dramatically changed. Items obviously could be added, removed, or moved in this scenario. When this happens there could be any number of underlying changes that effect things like grouping, sorting, and filtering. Because of this, the whole grid needs to be refreshed under this particular event, otherwise it breaks a lot of scenarios for the other behaviors I've mentioned. It might be possible to do something like use a CollectionView with deferred updates, and we'll try to look into that aspect. I'll talk to the developers about it some more.

    Kelley
  • Replied 14 January 2019, 8:27 am EST

    After more discussion I think you'd need inherit from the existing ObservableCollection and change/override the notification type for clearing items. There's a StackOverflow article that discusses a few different possible solutions that you can take a look at here (and see what could be suitable):

    https://stackoverflow.com/questions/224155/when-clearing-an-observablecollection-there-are-no-items-in-e-olditems

    Unfortunately I don't think there's a more simple way of handling this scenario other than what we've outlined so far.
  • Replied 14 January 2019, 10:15 am EST

    Thanks for looking into it. Clear is only half the issue; when doing bulk adds we use a sub classed ObservableCollection for speed. The workaround that I came up with is to key off the AddRange and Clear methods to determine when a header refresh is going to happen; I then remove the text boxes from the visual tree, wait for the Reset to occur and put them back in the visual tree. It works but seems like a hack. If you were to supply a sub classed ObservableCollection that would work with the grid, the grid would know definitively the intent.


    public void AddRange(IEnumerable<T> list)
    {
    if (list == null)
    return;

    if (PreviewReset != null)
    PreviewReset.Invoke(this, new EventArgs());

    _suppressNotification = true;

    foreach (T item in list)
    {
    Add(item);
    }
    _suppressNotification = false;
    OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
    }

    protected override void ClearItems()
    {
    if (PreviewReset != null)
    PreviewReset.Invoke(this, new EventArgs());

    base.ClearItems();
    }
  • Marked as Answer

    Replied 19 March 2019, 1:34 am EST

    Hello,

    Another workaround is to implement a custom ObservableCollection to prevent raising Clear event. One can add more code to the implementation if he wants case-specific preventing of the “Clear” event.
    public class SilentClearObservableCollection<T> : ObservableCollection<T>
    {
    public event Action<object, NotifyCollectionChangedEventArgs> ItemClear;

    protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
    {
    if (e.Action != NotifyCollectionChangedAction.Reset)
    base.OnCollectionChanged(e);
    else
    ItemClear?.Invoke(this, e);
    }
    }
    Please refer attached sample application for the workaround.
    Thanks,
    Ruchir
    FlexGrid_Workaround.zip
Need extra support?

Upgrade your support plan and get personal unlimited phone support with our customer engagement team

Learn More

Forum Channels