diff --git a/CS/ViewModel/EFCore/LocalData/IDataItemCopyOperationsSupporter.cs b/CS/ViewModel/EFCore/LocalData/IDataItemCopyOperationsSupporter.cs new file mode 100644 index 0000000..e0cbf34 --- /dev/null +++ b/CS/ViewModel/EFCore/LocalData/IDataItemCopyOperationsSupporter.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace EFCoreIssues { + public interface IDataItemCopyOperationsSupporter { + object Clone(object item); + void CopyTo(object source, object target); + } +} diff --git a/CS/ViewModel/EFCore/LocalData/LocalData.csproj b/CS/ViewModel/EFCore/LocalData/LocalData.csproj index cf7b5b4..c7f6a6b 100644 --- a/CS/ViewModel/EFCore/LocalData/LocalData.csproj +++ b/CS/ViewModel/EFCore/LocalData/LocalData.csproj @@ -1,4 +1,4 @@ - + @@ -13,7 +13,7 @@ 512 {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 4 - true + true true true @@ -166,11 +166,14 @@ MSBuild:Compile Designer + + MSBuild:Compile Designer - + + App.xaml Code @@ -213,4 +216,4 @@ - + \ No newline at end of file diff --git a/CS/ViewModel/EFCore/LocalData/MainViewModel.cs b/CS/ViewModel/EFCore/LocalData/MainViewModel.cs index 0844a1f..c2e7051 100644 --- a/CS/ViewModel/EFCore/LocalData/MainViewModel.cs +++ b/CS/ViewModel/EFCore/LocalData/MainViewModel.cs @@ -1,18 +1,29 @@ using DevExpress.Mvvm; +using System.Collections.ObjectModel; using System.Linq; namespace EFCoreIssues { public class MainViewModel : ViewModelBase { EFCoreIssues.Issues.IssuesContext _Context; - System.Collections.Generic.IList _ItemsSource; + ObservableCollection _ItemsSource; - public System.Collections.Generic.IList ItemsSource + UserCopyOperationsSupporter _CopyOperationsSupporter; + public UserCopyOperationsSupporter CopyOperationsSupporter { + get { + if(_CopyOperationsSupporter == null) { + _CopyOperationsSupporter = new UserCopyOperationsSupporter(); + } + return _CopyOperationsSupporter; + } + } + + public ObservableCollection ItemsSource { get { if(_ItemsSource == null && !IsInDesignMode) { _Context = new EFCoreIssues.Issues.IssuesContext(); - _ItemsSource = _Context.Users.ToList(); + _ItemsSource = new ObservableCollection(_Context.Users); } return _ItemsSource; } diff --git a/CS/ViewModel/EFCore/LocalData/MainWindow.xaml b/CS/ViewModel/EFCore/LocalData/MainWindow.xaml index 560de16..ef3e943 100644 --- a/CS/ViewModel/EFCore/LocalData/MainWindow.xaml +++ b/CS/ViewModel/EFCore/LocalData/MainWindow.xaml @@ -1,31 +1,40 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/CS/ViewModel/EFCore/LocalData/UndoBehavior.cs b/CS/ViewModel/EFCore/LocalData/UndoBehavior.cs new file mode 100644 index 0000000..3f56d06 --- /dev/null +++ b/CS/ViewModel/EFCore/LocalData/UndoBehavior.cs @@ -0,0 +1,128 @@ +using DevExpress.Mvvm; +using DevExpress.Mvvm.UI.Interactivity; +using DevExpress.Mvvm.Xpf; +using DevExpress.Xpf.Grid; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Input; + +namespace EFCoreIssues { + public class UndoBehavior : Behavior { + public static readonly DependencyProperty CopyOperationsSupporterProperty = + DependencyProperty.Register(nameof(CopyOperationsSupporter), typeof(IDataItemCopyOperationsSupporter), typeof(UndoBehavior), new PropertyMetadata(null)); + + public IDataItemCopyOperationsSupporter CopyOperationsSupporter { + get { return (IDataItemCopyOperationsSupporter)GetValue(CopyOperationsSupporterProperty); } + set { SetValue(CopyOperationsSupporterProperty, value); } + } + + public static readonly DependencyProperty ValidateRowCommandProperty = + DependencyProperty.Register(nameof(ValidateRowCommand), typeof(ICommand), typeof(UndoBehavior), new PropertyMetadata(null)); + + public ICommand ValidateRowCommand { + get { return (ICommand)GetValue(ValidateRowCommandProperty); } + set { SetValue(ValidateRowCommandProperty, value); } + } + + public static readonly DependencyProperty ValidateRowDeletionCommandProperty = + DependencyProperty.Register(nameof(ValidateRowDeletionCommand), typeof(ICommand), typeof(UndoBehavior), new PropertyMetadata(null)); + + public ICommand ValidateRowDeletionCommand { + get { return (ICommand)GetValue(ValidateRowDeletionCommandProperty); } + set { SetValue(ValidateRowDeletionCommandProperty, value); } + } + + public ICommand UndoCommand { get; private set; } + + public UndoBehavior() { + UndoCommand = new DelegateCommand(Undo, CanUndo); + } + + IList source; + + protected override void OnAttached() { + base.OnAttached(); + AssociatedObject.ValidateRow += OnRowAddedOrEdited; + AssociatedObject.ValidateRowDeletion += OnRowDeleted; + AssociatedObject.RowEditStarted += OnEditingStarted; + AssociatedObject.DataSourceRefresh += OnRefresh; + AssociatedObject.InitNewRow += OnNewRowStarted; + source = (IList)AssociatedObject.DataControl.ItemsSource; + } + + private void OnRefresh(object sender, DataSourceRefreshEventArgs e) { + undoAction = null; + editingCache = null; + } + + protected override void OnDetaching() { + AssociatedObject.ValidateRow -= OnRowAddedOrEdited; + AssociatedObject.ValidateRowDeletion -= OnRowDeleted; + AssociatedObject.RowEditStarted -= OnEditingStarted; + AssociatedObject.DataSourceRefresh -= OnRefresh; + AssociatedObject.InitNewRow -= OnNewRowStarted; + source = null; + base.OnDetaching(); + } + + private void OnNewRowStarted(object sender, InitNewRowEventArgs e) { + isNewItemRowEditing = true; + } + + bool isNewItemRowEditing; + + object editingCache; + + private void OnEditingStarted(object sender, RowEditStartedEventArgs e) { + if(e.RowHandle != DataControlBase.NewItemRowHandle) { + editingCache = CopyOperationsSupporter.Clone(e.Row); + } + } + + private void OnRowDeleted(object sender, GridValidateRowDeletionEventArgs e) { + undoAction = new Action(() => { + InsertItem(e.RowHandles.Single(), CopyOperationsSupporter.Clone(e.Rows.Single())); //Somehow index is changing after a new adding to source + }); + } + + private void OnRowAddedOrEdited(object sender, GridRowValidationEventArgs e) { + var item = e.Row; + undoAction = e.IsNewItem ? new Action(() => RemoveItem(item)) : new Action(() => ApplyEditingCache(item)); + isNewItemRowEditing = false; + } + + void ApplyEditingCache(object item) { + CopyOperationsSupporter.CopyTo(editingCache, item); + AssociatedObject.DataControl.RefreshRow(source.IndexOf(item)); //TODO: find a way how to get row handle by element or list index + ValidateRowCommand.Execute(new RowValidationArgs(editingCache, source.IndexOf(item), false, new CancellationToken(), false)); + } + + Action undoAction; + + void Undo() { + undoAction?.Invoke(); + undoAction = null; + } + + bool CanUndo() { + return undoAction != null && !AssociatedObject.IsEditing && !isNewItemRowEditing && !AssociatedObject.IsDataSourceRefreshing; + } + + void RemoveItem(object item) { + source.Remove(item); + ValidateRowDeletionCommand.Execute(new ValidateRowDeletionArgs(new object[] { item }, new int[] { source.IndexOf(item) })); + } + + void InsertItem(int position, object item) { + source.Insert(position, item); + ValidateRowCommand.Execute(new RowValidationArgs(item, source.IndexOf(item), true, new CancellationToken(), false)); + } + + } +} diff --git a/CS/ViewModel/EFCore/LocalData/UndoManager.cs b/CS/ViewModel/EFCore/LocalData/UndoManager.cs new file mode 100644 index 0000000..ae533cb --- /dev/null +++ b/CS/ViewModel/EFCore/LocalData/UndoManager.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace EFCoreIssues { + public class UndoManager { + + } +} diff --git a/CS/ViewModel/EFCore/LocalData/UserCopyOperationsSupporter.cs b/CS/ViewModel/EFCore/LocalData/UserCopyOperationsSupporter.cs new file mode 100644 index 0000000..289d39c --- /dev/null +++ b/CS/ViewModel/EFCore/LocalData/UserCopyOperationsSupporter.cs @@ -0,0 +1,32 @@ +using EFCoreIssues.Issues; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace EFCoreIssues { + public class UserCopyOperationsSupporter : IDataItemCopyOperationsSupporter { + public object Clone(object item) { + var userItem = GetUser(item); + return new User() { FirstName = userItem.FirstName, Id = userItem.Id, Issues = userItem.Issues, LastName = userItem.LastName }; + } + + public void CopyTo(object source, object target) { + var userSource = GetUser(source); + var userTarget = GetUser(target); + userTarget.FirstName = userSource.FirstName; + userTarget.Id = userSource.Id; + userTarget.Issues = userSource.Issues; + userTarget.LastName = userSource.LastName; + } + + User GetUser(object item) { + var result = item as User; + if(result == null) { + throw new InvalidOperationException(); + } + return result; + } + } +} diff --git a/README.md b/README.md index e40afa9..9238d77 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,4 @@ -![](https://img.shields.io/endpoint?url=https://codecentral.devexpress.com/api/v1/VersionRange/265491908/21.2.3%2B) [![](https://img.shields.io/badge/Open_in_DevExpress_Support_Center-FF7200?style=flat-square&logo=DevExpress&logoColor=white)](https://supportcenter.devexpress.com/ticket/details/T899930) [![](https://img.shields.io/badge/📖_How_to_use_DevExpress_Examples-e9f6fc?style=flat-square)](https://docs.devexpress.com/GeneralInformation/403183)