2021-09-20 18:20:01 +02:00

183 lines
4.8 KiB
C#

/*
Copyright (C) 2014-2019 de4dot@gmail.com
This file is part of dnSpy
dnSpy is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
dnSpy is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with dnSpy. If not, see <http://www.gnu.org/licenses/>.
*/
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
namespace dnSpy.Contracts.MVVM {
/// <summary>
/// List of items
/// </summary>
/// <typeparam name="T"></typeparam>
public class ListVM<T> : INotifyPropertyChanged, IDataErrorInfo {
/// <summary>The list</summary>
protected ObservableCollection<T> list;
readonly Action<int, int>? onChanged;
int index;
/// <summary>
/// Gets the index
/// </summary>
protected int Index => index;
/// <summary>
/// Gets the items
/// </summary>
public IList<T> Items => list;
/// <summary>
///
/// </summary>
/// <param name="newValues"></param>
/// <param name="addDefault"></param>
/// <param name="defaultValue"></param>
public void InvalidateSelected(IEnumerable<T> newValues, bool addDefault, T defaultValue) {
//TODO: Optimize callers. This method is slow.
var newList = new ObservableCollection<T>();
if (addDefault)
newList.Add(defaultValue);
foreach (var v in newValues)
newList.Add(v);
T selectedItem = SelectedItem;
if (index < 0)
selectedItem = defaultValue;
int newIndex = index >= 0 && index < newList.Count &&
object.Equals(newList[index], selectedItem) ?
index : newList.IndexOf(selectedItem);
if (newIndex < 0) {
newList.Add(selectedItem);
newIndex = newList.Count - 1;
}
try {
list = newList;
index = -1;
OnPropertyChanged(nameof(SelectedIndex));
OnPropertyChanged(nameof(SelectedItem));
OnPropertyChanged(nameof(Items));
}
finally {
index = newIndex;
OnPropertyChanged(nameof(SelectedIndex));
OnPropertyChanged(nameof(SelectedItem));
}
}
/// <summary>
/// Gets/sets the selected index
/// </summary>
public int SelectedIndex {
get => index;
set {
if (index != value) {
int oldIndex = index;
Debug.Assert(value >= 0 && value < list.Count);
index = value;
OnPropertyChanged(nameof(SelectedIndex));
OnPropertyChanged(nameof(SelectedItem));
onChanged?.Invoke(oldIndex, index);
}
}
}
/// <summary>
/// Gets/sets the selected item
/// </summary>
public T SelectedItem {
get {
if (index < 0 || index >= list.Count)
return default!;
return list[index];
}
set {
if (index < 0 || !object.Equals(value, SelectedItem))
SelectedIndex = GetIndex(value);
}
}
/// <summary>
/// Constructor
/// </summary>
public ListVM()
: this((Action<int, int>?)null) {
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="onChanged">Called when the selected item gets changed</param>
public ListVM(Action<int, int>? onChanged) {
list = new ObservableCollection<T>();
index = -1;
this.onChanged = onChanged;
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="list">Initial value</param>
public ListVM(IList<T> list)
: this(list, null) {
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="list">Initial value</param>
/// <param name="onChanged">Called when the selected item gets changed</param>
public ListVM(IEnumerable<T> list, Action<int, int>? onChanged) {
this.list = new ObservableCollection<T>(list);
index = this.list.Count == 0 ? -1 : 0;
this.onChanged = onChanged;
}
int GetIndex(T value) {
int index = list.IndexOf(value);
if (index >= 0)
return index;
list.Add(value);
return list.Count - 1;
}
/// <inheritdoc/>
public event PropertyChangedEventHandler? PropertyChanged;
void OnPropertyChanged(string propName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
string IDataErrorInfo.Error { get { throw new NotImplementedException(); } }
string IDataErrorInfo.this[string columnName] {
get {
if (columnName == nameof(SelectedIndex)) {
if (DataErrorInfoDelegate is not null)
return DataErrorInfoDelegate(this);
}
return string.Empty;
}
}
/// <summary>
/// Can be set to validate the list
/// </summary>
public Func<ListVM<T>, string>? DataErrorInfoDelegate;
}
}