I have a project for my college. In it, I'm trying to implement MVVM + WPF + EF6. In the course of a problem that I can not solve for too long.

A little bit about what I could do:

There is a class MedRecordViewModel - view model with the selecteditem property and defined CRUD commands, there is a view MedRecord Edit which is passed as datacontexts - MedRecordViewModel.

MedRecordEdit

The essence of the problem: I can not undo the changes that are immediately applied due to the interface implemented inotifypropertychanged.

I found a solution like "create a clone of an object using AutoMapper'a and in case of cancellation just overwrite the created clone into the current selecteditem", but due to the fact that the auto-driver creates a completely new object, this object seems to stop being tracked by my ObservableCollection.

Questions:

  1. How to make undo changes?
  2. How to measure automapper with EF?
  • Where is your Model in MVVM ? - tym32167
  • I would make the property of your student in MedRecordViewModel and on the team that handles the student’s choice (or in the designer, if you have a separate view for it), would write selecteditem to this property. And in case of cancellation (in the CancelCommand handler), it would be returned back. And I understand you correctly, that writing to the database goes directly at the moment of changing any property? If so, this is not a good idea (to put it mildly). I would put a record in the database on the command processing the click on the "Done" button - foxhound

1 answer 1

example work

You can use the IEditableObject implementation in the model class.

 public class Person : INotifyPropertyChanged, IEditableObject { //ссылка Π½Π° экзСмпляр хранСния ΠΏΡ€Π΅Π΄Ρ‹Π΄.Π·Π½Π°Ρ‡Π΅Π½ΠΈΠΉ Π½Π° врСмя рСдактирования private Person _tempValues; public event PropertyChangedEventHandler PropertyChanged; public int Id { get; set; } private int _OrderNumber; public int OrderNumber { get => _OrderNumber; set { _OrderNumber = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(OrderNumber))); } } private string _FirstName; public string FirstName { get => _FirstName; set { _FirstName = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(FirstName))); } } private string _LastName; public string LastName { get => _LastName; set { _LastName = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(LastName))); } } #region РСализация IEditableObject /// <summary> /// Когда начинаСтся Ρ€Π΅Π΄Π°ΠΊΡ‚ΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅ /// </summary> public void BeginEdit() { _tempValues = new Person { Id = this.Id, OrderNumber = this.OrderNumber, FirstName = this.FirstName, LastName = this.LastName }; } /// <summary> /// Когда Ρ€Π΅Π΄Π°ΠΊΡ‚ΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅ Π·Π°Π²Π΅Ρ€ΡˆΠ΅Π½ΠΎ /// </summary> public void EndEdit() { _tempValues = null; } /// <summary> /// Когда Ρ€Π΅Π΄Π°ΠΊΡ‚ΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅ отмСняСтся /// </summary> public void CancelEdit() { if (_tempValues == null) return; this.Id = _tempValues.Id; this.OrderNumber = _tempValues.OrderNumber; this.FirstName = _tempValues.FirstName; this.LastName = _tempValues.LastName; } #endregion } 

For example, the DataGrid that is called "out of the box" supports calling corresponding methods, and in particular, calls CancelEdit() if you press Esc during editing a cell. Well, in the case of editing in textboxes, you can do this

 public class MainViewModel : IMainViewModel, INotifyPropertyChanged { private readonly IPersonRepository _repo; private Person _selectedPerson; public event PropertyChangedEventHandler PropertyChanged; //ctor public MainViewModel(IPersonRepository repository) { _repo = repository ?? throw new ArgumentNullException(nameof(repository)); } public async Task LoadData() { People = new ObservableCollection<Person>(); var result = await _repo.GetPeopleOrderedByLastName(); List<Person> people = result.ToList(); for (int i = 0; i < people.Count; i++) { people[i].OrderNumber = i + 1; People.Add(people[i]); } } /// <summary> /// ΠšΠΎΠ»Π»Π΅ΠΊΡ†ΠΈΡ для ListView /// </summary> public ObservableCollection<Person> People { get; private set; } /// <summary> /// Π’Ρ‹Π΄Π΅Π»Π΅Π½Π½Ρ‹ΠΉ Π² ListView Ρ‡Π΅Π»ΠΎΠ²Π΅ΠΊ /// </summary> public Person SelectedPerson { get => _selectedPerson; set { //отмСняСм Ρ€Π΅Π΄Π°ΠΊΡ‚ΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅ ΠΏΡ€Π΅Π΄Ρ‹Π΄ΡƒΡ‰. _selectedPerson?.CancelEdit(); _selectedPerson = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(SelectedPerson))); //Π½Π°Ρ‡ΠΈΠ½Π°Π΅ΠΌ Ρ€Π΅Π΄Π°ΠΊΡ‚ΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅ _selectedPerson?.BeginEdit(); } } /// <summary> /// Кнопка Π“ΠΎΡ‚ΠΎΠ²ΠΎ /// </summary> private ICommand _AddCommand; public ICommand AddCommand { get => _AddCommand = _AddCommand ?? new RelayCommand(OnAdd); } private async void OnAdd() { if (SelectedPerson == null) return; //Π·Π°ΠΊΠ°Π½Ρ‡ΠΈΠ²Π°Π΅ΠΌ Ρ€Π΅Π΄Π°ΠΊΡ‚ΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅ SelectedPerson.EndEdit(); //обновляСм Π² Π‘Π” await _repo.UpdatePerson(SelectedPerson); SelectedPerson = null; //ΠΏΠ΅Ρ€Π΅Π·Π°Π³Ρ€ΡƒΠΆΠ°Π΅ΠΌ список ΠΈΠ· Π‘Π” await LoadData(); } /// <summary> /// Кнопка ΠžΡ‚ΠΌΠ΅Π½Π° /// </summary> private ICommand _CancelCommand; public ICommand CancelCommand { get => _CancelCommand = _CancelCommand ?? new RelayCommand(OnCancel); } private void OnCancel() { if (SelectedPerson == null) return; //отмСняСм Ρ€Π΅Π΄Π°ΠΊΡ‚ΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅ SelectedPerson.CancelEdit(); } } 

The whole example can be found here .

  • Hmm, why IMainViewModel , and not just some kind of implementation in the class? Here I have a sin, I still do not understand why we need interfaces, maybe you can explain it?) - EvgeniyZ
  • one
    @EvgeniyZ is just my habit of starting writing with abstractions, and, as they call it, nobody has canceled weak connectedness ... - Bulson 10:48 pm