/*
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 .
*/
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
namespace dnSpy.Contracts.MVVM {
///
/// List of items
///
///
public class ListVM : INotifyPropertyChanged, IDataErrorInfo {
/// The list
protected ObservableCollection list;
readonly Action? onChanged;
int index;
///
/// Gets the index
///
protected int Index => index;
///
/// Gets the items
///
public IList Items => list;
///
///
///
///
///
///
public void InvalidateSelected(IEnumerable newValues, bool addDefault, T defaultValue) {
//TODO: Optimize callers. This method is slow.
var newList = new ObservableCollection();
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));
}
}
///
/// Gets/sets the selected index
///
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);
}
}
}
///
/// Gets/sets the selected item
///
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);
}
}
///
/// Constructor
///
public ListVM()
: this((Action?)null) {
}
///
/// Constructor
///
/// Called when the selected item gets changed
public ListVM(Action? onChanged) {
list = new ObservableCollection();
index = -1;
this.onChanged = onChanged;
}
///
/// Constructor
///
/// Initial value
public ListVM(IList list)
: this(list, null) {
}
///
/// Constructor
///
/// Initial value
/// Called when the selected item gets changed
public ListVM(IEnumerable list, Action? onChanged) {
this.list = new ObservableCollection(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;
}
///
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;
}
}
///
/// Can be set to validate the list
///
public Func, string>? DataErrorInfoDelegate;
}
}