Skip to content

Commit

Permalink
Add IEditableObject interface and behavior
Browse files Browse the repository at this point in the history
  • Loading branch information
Marcel Sandermann committed Dec 22, 2021
1 parent 34cd1b5 commit d2e0c84
Showing 1 changed file with 149 additions and 79 deletions.
228 changes: 149 additions & 79 deletions src/Moryx.Controls/EntryEditor/EntryViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,34 +7,105 @@
using System.Linq;
using System.Runtime.CompilerServices;
using Moryx.Serialization;
using Moryx.Tools;

namespace Moryx.Controls
{
/// <summary>
/// View model that represents a page in the config editor
/// </summary>
public class EntryViewModel : INotifyPropertyChanged
public class EntryViewModel : INotifyPropertyChanged, IEditableObject
{
#region Fields and Properties
private ObservableCollection<EntryViewModel> _subEntries;
private string _preEditValue;

/// <summary>
/// The entry of this view model
/// </summary>
public Entry Entry { get; }
public Entry Entry { get; private set; }

/// <summary>
/// Parent view model
/// </summary>
public EntryViewModel Parent { get; set; }

///
public string DisplayName { get; private set; }

///
public EntryValueType ValueType { get; private set; }

///
public EntryUnitType UnitType { get; private set; }

///
public string DefaultValue => Entry.Value.Default;

///
public string Description => Entry.Description;

/// <summary>
/// Flag if this entry is readonly and text boxes shall be disabled
/// </summary>
public bool IsReadOnly => Entry.Value.IsReadOnly;

// TODO: AL6 Remove direct access to Model and the helper variable _preEditValue from BeginEdit, CancelEdit and EndEdit methods
///
public string Value
{
get => Entry.Value.Current;
set
{
if (Entry.Value.Current?.Equals(value) ?? false)
return;
Entry.Value.Current = value;
OnPropertyChanged();
}
}

private void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}

///
public ObservableCollection<string> PossibleValues
{
get
{
var possibleValues = Entry?.Value.Possible;
return possibleValues != null ? new ObservableCollection<string>(possibleValues) : null;
}
}

///
public ObservableCollection<EntryViewModel> SubEntries
{
get => _subEntries;
set
{
_subEntries = value;
OnPropertyChanged();
}
}
#endregion

#region Constructors
/// <summary>
/// Initializes a new instance of the <see cref="EntryViewModel"/> class.
/// </summary>
public EntryViewModel(IList<Entry> entries)
public EntryViewModel() : this(new Entry() { DisplayName = "Root",
Value = new EntryValue() { Type = EntryValueType.Class }, SubEntries = new List<Entry>() })
{
DisplayName = "Root";
ValueType = EntryValueType.Class;
SubEntries = new ObservableWrapperCollection(entries);
}

UpdateParent();
/// <summary>
/// Initializes a new instance of the <see cref="EntryViewModel"/> class.
/// </summary>
public EntryViewModel(IList<Entry> entries) : this()
{
UpdateModel(entries);
}

/// <summary>
Expand All @@ -44,57 +115,27 @@ public EntryViewModel(Entry entry)
{
Entry = entry;
DisplayName = Entry.DisplayName;
Value = Entry.Value.Current;
ValueType = Entry.Value.Type;
UnitType = Entry.Value.UnitType;
SubEntries = new ObservableWrapperCollection(entry.SubEntries);
SubEntries = new ObservableCollection<EntryViewModel>(Entry.SubEntries.Select(e => new EntryViewModel(e)));

UpdateParent();
}

private void UpdateParent(EntryViewModel entry)
{
entry.Parent = this;
}
#endregion

private void UpdateParent()
{
foreach (var entryViewModel in SubEntries)
{
UpdateParent(entryViewModel);
}
}

///
public string DisplayName { get; }

///
public string Value
private void UpdateParent(EntryViewModel entry)
{
get => Entry.Value.Current;
set
{
Entry.Value.Current = value;
OnPropertyChanged();
}
if (entry.Parent is null || !entry.Parent.Equals(this))
entry.Parent = this;
}

///
public EntryValueType ValueType { get; }

///
public EntryUnitType UnitType { get; }

///
public string DefaultValue => Entry.Value.Default;

///
public string Description => Entry.Description;

/// <summary>
/// Flag if this entry is readonly and text boxes shall be disabled
/// </summary>
public bool IsReadOnly => Entry.Value.IsReadOnly;

/// <summary>
/// Add prototype of this name to the subEntries
/// </summary>
Expand All @@ -117,68 +158,97 @@ public void ReplaceWithPrototype(string prototypeName)
// Update entry
Entry.Value = prototype.Value;
Entry.SubEntries = prototype.SubEntries;
Entry.Value.Type = prototype.Value.Type;
Entry.Value.UnitType = prototype.Value.UnitType;
// Update our observable collection
SubEntries = new ObservableWrapperCollection(Entry.SubEntries);
SubEntries = new ObservableCollection<EntryViewModel>(Entry.SubEntries.Select(e => new EntryViewModel(e)));

UpdateParent();
}

///
public ObservableCollection<string> PossibleValues
/// <inheritdoc />
public void BeginEdit()
{
get
{
var possibleValues = Entry?.Value.Possible;
return possibleValues != null ? new ObservableCollection<string>(possibleValues) : null;
}
SubEntries.BeginEdit();
_preEditValue = Entry.Value.Current;
}

private ObservableCollection<EntryViewModel> _subEntries;
/// <inheritdoc />
public void EndEdit()
{
SubEntries.EndEdit();
_preEditValue = Entry.Value.Current;
CopyToModel();
}

///
public ObservableCollection<EntryViewModel> SubEntries
private void CopyToModel()
{
get => _subEntries;
set
{
_subEntries = value;
OnPropertyChanged();
}
Entry.DisplayName = DisplayName;
Entry.Value.Current = Value;
Entry.Value.Type = ValueType;
Entry.Value.UnitType = UnitType;
Entry.SubEntries = SubEntries.Select(vm => vm.Entry).ToList();

UpdateParent();
}

/// <inheritdoc />
public event PropertyChangedEventHandler PropertyChanged;

private void OnPropertyChanged([CallerMemberName] string propertyName = null)
public void CancelEdit()
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
SubEntries.CancelEdit();
Value = _preEditValue;
CopyFromModel();
}
}

internal class ObservableWrapperCollection : ObservableCollection<EntryViewModel>
{
private readonly IList<Entry> _entries;
private void CopyFromModel()
{
DisplayName = Entry.DisplayName;
UnitType = Entry.Value.UnitType;
ValueType = Entry.Value.Type;
MergeIntoViewModelCollection(SubEntries, Entry.SubEntries);

public ObservableWrapperCollection(IList<Entry> entries)
UpdateParent();
}

private void MergeIntoViewModelCollection(ObservableCollection<EntryViewModel> entryViewModels, IList<Entry> updated)
{
_entries = entries;
// Copy current state to our collection
for (var i = 0; i < entries.Count; i++)
// Remove those without identifiert or not existing in the updated collection
var removed = entryViewModels.Where(vm => vm.Entry.Identifier == "" || updated.All(e => e.Identifier != vm.Entry.Identifier)).ToList();
foreach (var obj in removed)
entryViewModels.Remove(obj);

foreach (var updatedEntry in updated)
{
base.InsertItem(i, new EntryViewModel(entries[i]));
var match = entryViewModels.FirstOrDefault(vm => vm.Entry.Identifier == updatedEntry.Identifier);
if (match is null) // Add new Entry
entryViewModels.Add(new EntryViewModel(updatedEntry));
else // Update Entry
match.UpdateModel(updatedEntry);
}
}

protected override void InsertItem(int index, EntryViewModel item)
/// <summary>
/// Updates the internal model
/// </summary>
/// <param name="entry">The updated entry instance</param>
public void UpdateModel(Entry entry)
{
_entries.Add(item.Entry);
base.InsertItem(index, item);
Entry = entry;
CopyFromModel();
}

protected override void RemoveItem(int index)
/// <summary>
/// Updates the internal model
/// </summary>
/// <param name="entries">The updated subentry instances</param>
public void UpdateModel(IList<Entry> entries)
{
_entries.Remove(this.ElementAt(index).Entry);
base.RemoveItem(index);
MergeIntoViewModelCollection(SubEntries, entries);
CopyToModel();
UpdateParent();
}

/// <inheritdoc />
public event PropertyChangedEventHandler PropertyChanged;
}
}

0 comments on commit d2e0c84

Please sign in to comment.