Re-arranging rows of datagrid while another thread modified the underlying collection
Hello,
The use case is that the user starts to drag a row in the datagrid (to sort it) with the mouse, and before the user drops the row - the underlying collection had an update with rows removal or addition. This produces the following exception:
An unhandled exception of type 'System.ArgumentOutOfRangeException' occurred in PresentationCore.dll
Index was out of range. Must be non-negative and less than the size of the collection.
Index was out of range. Must be non-negative and less than the size of the collection.
Could you please advise is there a way to suspend any updates or throttle updates until the grid row re-order is processed, or a way to make this serial to the data update with some blocking.
Thank you in advance, Alex
SIGN IN To post a reply.
7 Replies
SS
Susmitha Sundar
Syncfusion Team
August 5, 2019 05:06 PM UTC
Hi Alexandre,
Thank you for using Syncfusion controls.
Based on the provided information, we have checked the mentioned issue “System.ArgumentOutOfRangeException throws while drag and drop in DataGrid” and we unable to replicate the issue from end. We have prepared sample for the same,
Sample link: https://www.syncfusion.com/downloads/support/forum/146420/ze/SfDataGridDemo_DragDropException-1129744973
Please check the sample and let us know if you still facing the same issue? If not, please modify the sample based on your scenario.
It will be helpful for us to check on it and provide you the solution at the earliest.
Regards,
Susmitha S
AL
Alexandre
August 6, 2019 01:13 PM UTC
I've attached the modified project source code.
Main changes are as follows:
- using reactive observables streams through DynamicData nugget
- sfdatagrid is still bound to EmployeeDetails, a dynamically generated ReadOnlyObservableCollection
//dynamicdata binding
_SubscriptionToModelChanges = _readonlyEmployeeDetailsCacheChangeSet
.Sort(SortExpressionComparer.Ascending(i => i.EmployeeId)) //some operation
.ObserveOnDispatcher() //make sure binding is on GUI thread
.Bind(out _EmployeeDetails) //bind to ReadOnlyObservableCollection bound to UI
//.DisposeMany() //necessary if Model is disposable
.Subscribe();
- updates moved from the dispatcher timer to an observable stream inside the view model
//actual updates timer
_SubscriptionToUpdatesPollingTimer = Observable.Timer(TimeSpan.Zero, TimeSpan.FromSeconds(5))
.SubscribeOn(NewThreadScheduler.Default)
.ObserveOn(NewThreadScheduler.Default) // default test case
//.ObserveOnDispatcher() // <--------------------has no effect if the subscribe() is executed on UI thread
.Subscribe(_ =>
{
//executes every five seconds - fetch 20 items with max delay before update cancelled of 3 seconds
var updates = _RemoteService.FetchRemoteSimple(20)
//.ObserveOn(NewThreadScheduler.Default) //this can be observed on a separate thread or
//.ObserveOnDispatcher() // on GUI thread (if the parent observable is observed on dispatcher too
// the simpest case is no new thread
.Subscribe( newSnapshot =>
{
lock (_EmployeeDetailsCache)
{
var expiredItems = _EmployeeDetailsCache.Items.Where(old => !newSnapshot.Any(upd => upd.EmployeeId==old.EmployeeId));
_EmployeeDetailsCache.Edit(f =>
{
//remove expired and update items
f.Remove(expiredItems);
f.AddOrUpdate(newSnapshot);
});
/*
//tried the following one by one and all together
//1)
_EmployeeDetailsCache.Refresh();
//2)
Application.Current.Dispatcher.BeginInvoke(
DispatcherPriority.Render,
new Action(() =>
{
OnPropertyChanged("EmployeeDetails");
})
);
*/
}
});
});
which works with the underlying data source.
To see the exception, start dragging the row and dont let go (dont drop) until the grid has a row added or removed.
Running updates on dispatcher, including raising onpropertychanged event for the bound readonlycollection does not resolve this problem.
Thank you very much for looking into this issue.
Kind regards, AM
Attachment: WPF_SfDataGridDemo_AMA1_79b6f82.zip
PS: This is the stack trace to show that the error happens in syncfusion and not in the dynamicdata:
> mscorlib.dll!System.ThrowHelper.ThrowArgumentOutOfRangeException(System.ExceptionArgument argument, System.ExceptionResource resource) Line 93 C#
mscorlib.dll!System.Collections.Generic.List.this[int].get(int index) Line 178 C#
Syncfusion.Data.WPF.dll!Syncfusion.Data.RecordsListBase.GetRecord(int index) Unknown
Syncfusion.Data.WPF.dll!Syncfusion.Data.RecordsListBase.this[int].get(int index) Unknown
Syncfusion.SfGrid.WPF.dll!Syncfusion.UI.Xaml.Grid.GridRowDragDropController.DroppingItemsInDataGrid(Syncfusion.UI.Xaml.ScrollAxis.RowColumnIndex rowColumnIndex, Syncfusion.UI.Xaml.Grid.SfDataGrid sourceDataGrid, System.Windows.DragEventArgs args) Unknown
Syncfusion.SfGrid.WPF.dll!Syncfusion.UI.Xaml.Grid.GridRowDragDropController.ProcessOnDrop(System.Windows.DragEventArgs args, Syncfusion.UI.Xaml.ScrollAxis.RowColumnIndex rowColumnIndex) Unknown
Syncfusion.SfGrid.WPF.dll!Syncfusion.UI.Xaml.Grid.GridCell.GridCell_Drop(object sender, System.Windows.DragEventArgs e) Unknown
PresentationCore.dll!System.Windows.DragEventArgs.InvokeEventHandler(System.Delegate genericHandler, object genericTarget) Unknown
PresentationCore.dll!System.Windows.RoutedEventArgs.InvokeHandler(System.Delegate handler, object target) Unknown
PresentationCore.dll!System.Windows.RoutedEventHandlerInfo.InvokeHandler(object target, System.Windows.RoutedEventArgs routedEventArgs) Unknown
PresentationCore.dll!System.Windows.EventRoute.InvokeHandlersImpl(object source, System.Windows.RoutedEventArgs args, bool reRaised) Unknown
PresentationCore.dll!System.Windows.UIElement.RaiseEventImpl(System.Windows.DependencyObject sender, System.Windows.RoutedEventArgs args) Unknown
PresentationCore.dll!System.Windows.UIElement.RaiseEvent(System.Windows.RoutedEventArgs e) Unknown
PresentationCore.dll!System.Windows.OleDropTarget.RaiseDragEvent(System.Windows.RoutedEvent dragEvent, int dragDropKeyStates, ref int effects, System.Windows.DependencyObject target, System.Windows.Point targetPoint) Unknown
PresentationCore.dll!System.Windows.OleDropTarget.MS.Win32.UnsafeNativeMethods.IOleDropTarget.OleDrop(object data, int dragDropKeyStates, long point, ref int effects) Unknown
[Native to Managed Transition]
[Managed to Native Transition]
PresentationCore.dll!System.Windows.RoutedEventArgs.InvokeHandler(System.Delegate handler, object target) Unknown
PresentationCore.dll!System.Windows.RoutedEventHandlerInfo.InvokeHandler(object target, System.Windows.RoutedEventArgs routedEventArgs) Unknown
PresentationCore.dll!System.Windows.EventRoute.InvokeHandlersImpl(object source, System.Windows.RoutedEventArgs args, bool reRaised) Unknown
PresentationCore.dll!System.Windows.UIElement.RaiseEventImpl(System.Windows.DependencyObject sender, System.Windows.RoutedEventArgs args) Unknown
PresentationCore.dll!System.Windows.UIElement.RaiseEvent(System.Windows.RoutedEventArgs e) Unknown
PresentationCore.dll!System.Windows.OleDropTarget.RaiseDragEvent(System.Windows.RoutedEvent dragEvent, int dragDropKeyStates, ref int effects, System.Windows.DependencyObject target, System.Windows.Point targetPoint) Unknown
PresentationCore.dll!System.Windows.OleDropTarget.MS.Win32.UnsafeNativeMethods.IOleDropTarget.OleDrop(object data, int dragDropKeyStates, long point, ref int effects) Unknown
[Native to Managed Transition]
[Managed to Native Transition]
...
AL
Alexandre
August 6, 2019 03:58 PM UTC
I tried a quick "fix" - to fire an observable within the ViewModel just before handling and binding the update.
Meanwhile, in the view, mouse grid row ondrag event handler starts watching for update signal and mouse grid row drop is cancelled if update signalled inbetween.
This works if the drag and drop is within the grid and not into another control or system window (which throws OLE exception).
If the drop happens off the application, the mouse grid OnDrop event handler is never reached and various exceptions happen.
Would appreciate your feedback how to resolve this issue in the most portable way (WPF and Xamarin Forms syncfusion controls).
//the ViewModel has this Subject as a signal mechanism
public Subject updatesObservable;
//actual updates timer
_SubscriptionToUpdatesPollingTimer = Observable.Timer(TimeSpan.Zero, TimeSpan.FromSeconds(5))
.SubscribeOn(NewThreadScheduler.Default)
.ObserveOn(NewThreadScheduler.Default) // default test case
.Subscribe(_ =>
{
var updates = _RemoteService.FetchRemoteSimple(20)
.Do(i =>
{
//signal pending update
updatesObservable.OnNext(true);
})
.Subscribe( newSnapshot =>
{
var expiredItems = _EmployeeDetailsCache.Items.Where(old => !newSnapshot.Any(upd => upd.EmployeeId==old.EmployeeId));
_EmployeeDetailsCache.Edit(f =>
{
//remove expired and update items
f.Remove(expiredItems);
f.AddOrUpdate(newSnapshot);
});
});
});
and within view
ViewModel vm = new ViewModel();
...
readonly object _lock = new object();
bool updateSignaled = false;
IDisposable updateStreamSubscription;
...
this.sfdatagrid.RowDragDropController.DragStart += RowDragDropController_DragStart;
this.sfdatagrid.RowDragDropController.Drop += RowDragDropController_Drop;
this.sfdatagrid.RowDragDropController.DragOver += RowDragDropController_DragOver;
...
//subscribe to the observable when mouse drag starts
private void RowDragDropController_DragStart(object sender, GridRowDragStartEventArgs e)
{
updateStreamSubscription = vm.updatesObservable
.Subscribe(flag =>
{
lock(_lock)
{
updateSignaled = true;
}
}
);
}
//disable drop if update was signaled
private void RowDragDropController_Drop(object sender, GridRowDropEventArgs e)
{
lock (_lock)
{
if (updateSignaled)
{
e.Handled = true;
updateSignaled = false;
}
updateStreamSubscription?.Dispose();
}
}
Call stack on drag&drop outside the application window after update notification received:
System.Runtime.Serialization.SerializationException: 'Type 'SfDataGridDemo.Models.Model' in Assembly … is not marked as serializable
mscorlib.dll!System.Runtime.Serialization.FormatterServices.InternalGetSerializableMembers()
mscorlib.dll!System.Collections.Concurrent.ConcurrentDictionary<>.GetOrAdd()
mscorlib.dll!System.Runtime.Serialization.Formatters.Binary.WriteObjectInfo.InitMemberInfo()
…
PresentationCore.dll!System.Windows.DataObject.System.Runtime.InteropServices.ComTypes.IDataObject.GetData()
AL
Alexandre
August 6, 2019 04:43 PM UTC
Alternative I tried was to expose dynamic data binding through simple IObservableCollection<Model>
The View datagrid has binding to the ViewModel EmployeeDetails. However, EmployeeDetails is now IObservableCollection.
Changes as follows:
//underlying datasource stays the same
private readonly SourceCache<Model, int> _EmployeeDetailsCache = new SourceCache<Model, int>(r => r.EmployeeId);
//datagrid now bound to this
public IObservableCollection<Model> EmployeeDetails { get; } = new ObservableCollectionExtended<Model>();
and in the ViewModel constructor:
//dynamicdata binding
_SubscriptionToModelChanges = _EmployeeDetailsCache
.Connect()
.Sort(SortExpressionComparer<Model>.Ascending(i => i.EmployeeId)) //some operation
.ObserveOnDispatcher()
.Bind(EmployeeDetails)
//.DisposeMany() //necessary if Model is disposable
.Subscribe();
_SubscriptionToModelChanges = _EmployeeDetailsCache
.Connect()
.Sort(SortExpressionComparer<Model>.Ascending(i => i.EmployeeId)) //some operation
.ObserveOnDispatcher()
.Bind(EmployeeDetails)
//.DisposeMany() //necessary if Model is disposable
.Subscribe();
While the timer thread stays the same:
//actual updates timer
_SubscriptionToUpdatesPollingTimer = Observable.Timer(TimeSpan.Zero, TimeSpan.FromSeconds(5))
.SubscribeOn(NewThreadScheduler.Default)
.ObserveOn(NewThreadScheduler.Default) // default test case
.Subscribe(_ =>
{
var updates = _RemoteService.FetchRemoteSimple(20)
.Subscribe(newSnapshot =>
{
var expiredItems = _EmployeeDetailsCache.Items.Where(old => !newSnapshot.Any(upd => upd.EmployeeId == old.EmployeeId));
_EmployeeDetailsCache.Edit(f =>
{
//remove expired and update items
f.Remove(expiredItems);
f.AddOrUpdate(newSnapshot);
});
});
_SubscriptionToUpdatesPollingTimer = Observable.Timer(TimeSpan.Zero, TimeSpan.FromSeconds(5))
.SubscribeOn(NewThreadScheduler.Default)
.ObserveOn(NewThreadScheduler.Default) // default test case
.Subscribe(_ =>
{
var updates = _RemoteService.FetchRemoteSimple(20)
.Subscribe(newSnapshot =>
{
var expiredItems = _EmployeeDetailsCache.Items.Where(old => !newSnapshot.Any(upd => upd.EmployeeId == old.EmployeeId));
_EmployeeDetailsCache.Edit(f =>
{
//remove expired and update items
f.Remove(expiredItems);
f.AddOrUpdate(newSnapshot);
});
});
});
Call Stack Trace:
> mscorlib.dll!System.ThrowHelper.ThrowArgumentOutOfRangeException(System.ExceptionArgument argument, System.ExceptionResource resource) Line 93 C#
mscorlib.dll!System.Collections.Generic.List<System.__Canon>.this[int].get(int index) Line 178 C#
Syncfusion.Data.WPF.dll!Syncfusion.Data.RecordsListBase.GetRecord(int index) Unknown
Syncfusion.SfGrid.WPF.dll!Syncfusion.UI.Xaml.Grid.GridRowDragDropController.DroppingItemsInDataGrid(Syncfusion.UI.Xaml.ScrollAxis.RowColumnIndex rowColumnIndex, Syncfusion.UI.Xaml.Grid.SfDataGrid sourceDataGrid, System.Windows.DragEventArgs args) Unknown
Syncfusion.SfGrid.WPF.dll!Syncfusion.UI.Xaml.Grid.GridRowDragDropController.ProcessOnDrop(System.Windows.DragEventArgs args, Syncfusion.UI.Xaml.ScrollAxis.RowColumnIndex rowColumnIndex) Unknown
PresentationCore.dll!System.Windows.RoutedEventArgs.InvokeHandler(System.Delegate handler, object target) Unknown
PresentationCore.dll!System.Windows.RoutedEventHandlerInfo.InvokeHandler(object target, System.Windows.RoutedEventArgs routedEventArgs) Unknown
PresentationCore.dll!System.Windows.EventRoute.InvokeHandlersImpl(object source, System.Windows.RoutedEventArgs args, bool reRaised) Unknown
PresentationCore.dll!System.Windows.UIElement.RaiseEventImpl(System.Windows.DependencyObject sender, System.Windows.RoutedEventArgs args) Unknown
PresentationCore.dll!System.Windows.OleDropTarget.RaiseDragEvent(System.Windows.RoutedEvent dragEvent, int dragDropKeyStates, ref int effects, System.Windows.DependencyObject target, System.Windows.Point targetPoint) Unknown
PresentationCore.dll!System.Windows.OleDropTarget.MS.Win32.UnsafeNativeMethods.IOleDropTarget.OleDrop(object data, int dragDropKeyStates, long point, ref int effects) Unknown
[Native to Managed Transition]
[Managed to Native Transition]
mscorlib.dll!System.Collections.Generic.List<System.__Canon>.this[int].get(int index) Line 178 C#
Syncfusion.Data.WPF.dll!Syncfusion.Data.RecordsListBase.GetRecord(int index) Unknown
Syncfusion.SfGrid.WPF.dll!Syncfusion.UI.Xaml.Grid.GridRowDragDropController.DroppingItemsInDataGrid(Syncfusion.UI.Xaml.ScrollAxis.RowColumnIndex rowColumnIndex, Syncfusion.UI.Xaml.Grid.SfDataGrid sourceDataGrid, System.Windows.DragEventArgs args) Unknown
Syncfusion.SfGrid.WPF.dll!Syncfusion.UI.Xaml.Grid.GridRowDragDropController.ProcessOnDrop(System.Windows.DragEventArgs args, Syncfusion.UI.Xaml.ScrollAxis.RowColumnIndex rowColumnIndex) Unknown
PresentationCore.dll!System.Windows.RoutedEventArgs.InvokeHandler(System.Delegate handler, object target) Unknown
PresentationCore.dll!System.Windows.RoutedEventHandlerInfo.InvokeHandler(object target, System.Windows.RoutedEventArgs routedEventArgs) Unknown
PresentationCore.dll!System.Windows.EventRoute.InvokeHandlersImpl(object source, System.Windows.RoutedEventArgs args, bool reRaised) Unknown
PresentationCore.dll!System.Windows.UIElement.RaiseEventImpl(System.Windows.DependencyObject sender, System.Windows.RoutedEventArgs args) Unknown
PresentationCore.dll!System.Windows.OleDropTarget.RaiseDragEvent(System.Windows.RoutedEvent dragEvent, int dragDropKeyStates, ref int effects, System.Windows.DependencyObject target, System.Windows.Point targetPoint) Unknown
PresentationCore.dll!System.Windows.OleDropTarget.MS.Win32.UnsafeNativeMethods.IOleDropTarget.OleDrop(object data, int dragDropKeyStates, long point, ref int effects) Unknown
[Native to Managed Transition]
[Managed to Native Transition]
PresentationCore.dll!System.Windows.OleServicesContext.OleDoDragDrop(System.Runtime.InteropServices.ComTypes.IDataObject dataObject, MS.Win32.UnsafeNativeMethods.IOleDropSource dropSource, int allowedEffects, int[] finalEffect) Unknown
PresentationCore.dll!System.Windows.DragDrop.OleDoDragDrop(System.Windows.DependencyObject dragSource, System.Windows.DataObject dataObject, System.Windows.DragDropEffects allowedEffects) Unknown
PresentationCore.dll!System.Windows.DragDrop.DoDragDrop(System.Windows.DependencyObject dragSource, object data, System.Windows.DragDropEffects allowedEffects) Unknown
Syncfusion.SfGrid.WPF.dll!Syncfusion.UI.Xaml.Grid.GridRowDragDropController.ProcessOnDragStarting(System.Windows.Input.MouseEventArgs args, Syncfusion.UI.Xaml.ScrollAxis.RowColumnIndex rowColumnIndex) Unknown
Syncfusion.SfGrid.WPF.dll!Syncfusion.UI.Xaml.Grid.GridCell.GridCell_PreviewMouseMove(object sender, System.Windows.Input.MouseEventArgs e) Unknown
PresentationCore.dll!System.Windows.RoutedEventArgs.InvokeHandler(System.Delegate handler, object target) Unknown
PresentationCore.dll!System.Windows.RoutedEventHandlerInfo.InvokeHandler(object target, System.Windows.RoutedEventArgs routedEventArgs) Unknown
PresentationCore.dll!System.Windows.EventRoute.InvokeHandlersImpl(object source, System.Windows.RoutedEventArgs args, bool reRaised) Unknown
PresentationCore.dll!System.Windows.UIElement.RaiseEventImpl(System.Windows.DependencyObject sender, System.Windows.RoutedEventArgs args) Unknown
PresentationCore.dll!System.Windows.UIElement.RaiseTrustedEvent(System.Windows.RoutedEventArgs args) Unknown
PresentationCore.dll!System.Windows.Input.InputManager.ProcessStagingArea() Unknown
PresentationCore.dll!System.Windows.Input.InputManager.ProcessInput(System.Windows.Input.InputEventArgs input) Unknown
PresentationCore.dll!System.Windows.Input.InputProviderSite.ReportInput(System.Windows.Input.InputReport inputReport) Unknown
PresentationCore.dll!System.Windows.Interop.HwndMouseInputProvider.ReportInput(System.IntPtr hwnd, System.Windows.Input.InputMode mode, int timestamp, System.Windows.Input.RawMouseActions actions, int x, int y, int wheel) Unknown
PresentationCore.dll!System.Windows.Interop.HwndMouseInputProvider.FilterMessage(System.IntPtr hwnd, MS.Internal.Interop.WindowMessage msg, System.IntPtr wParam, System.IntPtr lParam, ref bool handled) Unknown
PresentationCore.dll!System.Windows.Interop.HwndSource.InputFilterMessage(System.IntPtr hwnd, int msg, System.IntPtr wParam, System.IntPtr lParam, ref bool handled) Unknown
WindowsBase.dll!MS.Win32.HwndWrapper.WndProc(System.IntPtr hwnd, int msg, System.IntPtr wParam, System.IntPtr lParam, ref bool handled) Unknown
…
PresentationCore.dll!System.Windows.DragDrop.OleDoDragDrop(System.Windows.DependencyObject dragSource, System.Windows.DataObject dataObject, System.Windows.DragDropEffects allowedEffects) Unknown
PresentationCore.dll!System.Windows.DragDrop.DoDragDrop(System.Windows.DependencyObject dragSource, object data, System.Windows.DragDropEffects allowedEffects) Unknown
Syncfusion.SfGrid.WPF.dll!Syncfusion.UI.Xaml.Grid.GridRowDragDropController.ProcessOnDragStarting(System.Windows.Input.MouseEventArgs args, Syncfusion.UI.Xaml.ScrollAxis.RowColumnIndex rowColumnIndex) Unknown
Syncfusion.SfGrid.WPF.dll!Syncfusion.UI.Xaml.Grid.GridCell.GridCell_PreviewMouseMove(object sender, System.Windows.Input.MouseEventArgs e) Unknown
PresentationCore.dll!System.Windows.RoutedEventArgs.InvokeHandler(System.Delegate handler, object target) Unknown
PresentationCore.dll!System.Windows.RoutedEventHandlerInfo.InvokeHandler(object target, System.Windows.RoutedEventArgs routedEventArgs) Unknown
PresentationCore.dll!System.Windows.EventRoute.InvokeHandlersImpl(object source, System.Windows.RoutedEventArgs args, bool reRaised) Unknown
PresentationCore.dll!System.Windows.UIElement.RaiseEventImpl(System.Windows.DependencyObject sender, System.Windows.RoutedEventArgs args) Unknown
PresentationCore.dll!System.Windows.UIElement.RaiseTrustedEvent(System.Windows.RoutedEventArgs args) Unknown
PresentationCore.dll!System.Windows.Input.InputManager.ProcessStagingArea() Unknown
PresentationCore.dll!System.Windows.Input.InputManager.ProcessInput(System.Windows.Input.InputEventArgs input) Unknown
PresentationCore.dll!System.Windows.Input.InputProviderSite.ReportInput(System.Windows.Input.InputReport inputReport) Unknown
PresentationCore.dll!System.Windows.Interop.HwndMouseInputProvider.ReportInput(System.IntPtr hwnd, System.Windows.Input.InputMode mode, int timestamp, System.Windows.Input.RawMouseActions actions, int x, int y, int wheel) Unknown
PresentationCore.dll!System.Windows.Interop.HwndMouseInputProvider.FilterMessage(System.IntPtr hwnd, MS.Internal.Interop.WindowMessage msg, System.IntPtr wParam, System.IntPtr lParam, ref bool handled) Unknown
PresentationCore.dll!System.Windows.Interop.HwndSource.InputFilterMessage(System.IntPtr hwnd, int msg, System.IntPtr wParam, System.IntPtr lParam, ref bool handled) Unknown
WindowsBase.dll!MS.Win32.HwndWrapper.WndProc(System.IntPtr hwnd, int msg, System.IntPtr wParam, System.IntPtr lParam, ref bool handled) Unknown
…
This version of source code attached too.
Attachment: WPF_SfDataGridDemo_AMA2_95626947.zip
FP
Farjana Parveen Ayubb
Syncfusion Team
August 7, 2019 01:00 PM UTC
Hi Alexandre,
Thank you for your update.
We could able to reproduce your reported issue from our end. You are added and removed the records from an underlying collection of DataGrid while drag and drop. Exception throws only if an underlying collection does not contain that dragging record. So Can you please confirm whether you need to add that record as a new record or just handle that exception?
Please confirm on this, it will be helpful for us to provide you solution at earliest.
Regards,
Farjana Parveen A
AL
Alexandre
August 7, 2019 02:08 PM UTC
Hello!
>Exception throws only if an underlying collection does not contain that dragging record.
This is what my example illustrates. I can not confirm the exception is only raised if the item was removed from bound collection while the grid is doing drag and drop.
I can not exclude a more general case of drag and drop failing while collection is updated.
Are you confirming that the bug is limited only to the use case of removed source record?
>Can you please confirm whether you need to add that record as a new record or just handle that exception?
It is my understanding that sfdatagrid caches and maintains it's own copy of the data source bound to the control for all things like filtering, ordering etc..
I do not understand what logic, if any would, requires the cached record "to be added back".
The data source update clearly invalidates any cached copy sfdatagrid maintains.
What would be the use case not to invalidate any GUI control operations and calculations in progress - such as totals/sums/groups and drag-and-drop?
In terms of my specific use case (always reflect the latest data in the grid, and disallow layout changes to interfere with updates), I can identify 2 potential solutions:
Solution 1
- The SfDataGrid drag and drop operation should be cancelled if the control detects underlying collection update, while in drag and drop, unless it can handle the resulting updates conflict
- Conflict cases are the drag-drop target OR source records had been removed from the bound data source.
- Drag-and-drop is allowed to be happen if the conflict can be resolved and after the drag and drop the grid reflects the latest changes in the data source.
- Otherwise the drag and drop should be cancelled. Drag and drop cancellation should be reflected visually by the "dragging" popup disappearing - ie as if Drop was denied or Dragging cancelled
Solution 2
- Suspend the SfGridData update (including the cached copy it uses behind) while the grid layout is changing. Resume data source bind after grid layout changed.
Summary
Whichever solution you can help me and other users with, please make it portable across SfDataGrid implementations across all supported platforms.
The solution should be universal across all SfDataGrid implementations - including WPF and Xamarin Forms/UWP, and should rely on a mechanism (ie. events or properties/methods) exposed across these targets
As a side note, I have tried to implement 1) and could not do it with my knowledge of the SfDataGrid.
Implementing 2) is easier - the work-around is to disallow any drag-and-drop by default on the grid. Allow the user to toggle into the "Layout" mode which re-binds the SfDataGrid to the last good copy of the update stream and allows the drag-and-drop. The user then can toggle back into "Live" mode where the control is re-binded to the dynamic data source.
ViewModel changes:
private IObservableCollection<Model> _EmployeeDetailsSnapshot;
public IObservableCollection<Model> EmployeeDetailsSnapshot { get { return _EmployeeDetailsSnapshot; } }
public void TakeSnapshot()
{
lock(_locker)
{
_EmployeeDetailsSnapshot = new ObservableCollectionExtended<Model>(EmployeeDetails.ToList());
}
{
lock(_locker)
{
_EmployeeDetailsSnapshot = new ObservableCollectionExtended<Model>(EmployeeDetails.ToList());
}
}
View Changes:
<ToggleButton x:Name="tb" Content="Switch To Layout Mode" Width="150" Height="50" Grid.Row="1"
Checked ="ChangeLayout_Mode"
Unchecked = "Live_Mode"/>
Checked ="ChangeLayout_Mode"
Unchecked = "Live_Mode"/>
private void ChangeLayout_Mode(object sender, RoutedEventArgs e)
{
vm.TakeSnapshot();
sfdatagrid.ItemsSource = vm.EmployeeDetailsSnapshot;
{
vm.TakeSnapshot();
sfdatagrid.ItemsSource = vm.EmployeeDetailsSnapshot;
sfdatagrid.AllowDraggingRows = true;
sfdatagrid.AllowDrop = true;
sfdatagrid.AllowDrop = true;
tb.Content = "Switch to Live Update";
}
}
private void Live_Mode(object sender, RoutedEventArgs e)
{
sfdatagrid.AllowDraggingRows = false;
sfdatagrid.AllowDrop = false;
{
sfdatagrid.AllowDraggingRows = false;
sfdatagrid.AllowDrop = false;
tb.Content = "Switch To Layout Mode";
sfdatagrid.ItemsSource = vm.EmployeeDetails;
}
}
I attach the updated source code.
Attachment: WPF_SfDataGridDemo_AMA3_3cf952e2.zip
FP
Farjana Parveen Ayubb
Syncfusion Team
August 8, 2019 12:03 PM UTC
Hi Alexandre,
Thank you for your update.
Please find the details for your queries,
Query1: It is my understanding that sfdatagrid caches and maintains it's own copy of the data source bound to the control for all things like filtering, ordering etc
Based on our architecture, we did not maintained any copy of our bounded collection of DataGrid. DataGrid UI updates instantly while performing Grouping, Sorting etc.
Query2: What would be the use case not to invalidate any GUI control operations and calculations in progress - such as totals/sums/groups and drag-and-drop?
You can use the following code for Suspend and resume the DataGrid UI. BeginInit() method suspend the all updates including drag and drop. When calling the EndInit(), all the actions updates on DataGrid UI.
C#:
|
//Suspend the Grid UI
this.sfdatagrid.View.BeginInit();
//Resume the Grid UI
this.sfdatagrid.View.EndInit(); |
Query 3: The SfDataGrid drag and drop operation should be cancelled if the control detects underlying collection update, while in drag and drop, unless it can handle the resulting updates conflict
- Conflict cases are the drag-drop target OR source records had been removed from the bound data source.
We have logged issue report for this query. We will fix this issue on our Volume 3, 2019 release which is expected on end of the September. Can you please confirm whether you will wait for our official release or need patch for this issue?
You can track the status of this report through the following feedback link,
Note: The provided feedback link is private and you need to login to view this feedback.
Query 4: Suspend the SfDataGrid update (including the cached copy it uses behind) while the grid layout is changing. Resume data source bind after grid layout changed.
We recommend to use your own work around (Layout Mode and Live mode) for this query.
Please contact us for further assistance.
Regards,
Farjana Parveen A
SIGN IN To post a reply.
- 7 Replies
- 3 Participants
-
AL Alexandre
- Aug 2, 2019 05:19 PM UTC
- Aug 8, 2019 12:03 PM UTC