Types for collection which supports undo/redo.
$ dotnet add package Shos.UndoRedoListList and ObservableCollection which support undo/redo.
You can install Shos.UndoRedoList to your project with NuGet on Visual Studio.
PM>Install-Package Shos.UndoRedoList -version 1.0.5
>dotnet add package Shos.UndoRedoList --version 1.0.5
<PackageReference Include="Shos.UndoRedoList" Version="1.0.5" />
Types for collection which supports undo/redo.
Tests for Shos.UndoRedoList.
Performance tests for Shos.UndoRedoList.
Sample WPF app for UndoRedoObservableCollection.

<TElement, TList> : IList<TElement> where TList : IList<TElement>, new()IList implemented collection which supports undo/redo.
<TElement> : UndoRedoList<TElement, List<TElement>>List implemented collection which supports undo/redo.
<TElement> : UndoRedoList<TElement, ObservableCollection<TElement>>, INotifyCollectionChangedObservableCollection which supports undo/redo.
<TElement> : IEnumerable<TElement>In computer science, a circular buffer, circular queue, cyclic buffer or ring buffer is a data structure that uses a single, fixed-size buffer as if it were connected end-to-end. This structure lends itself easily to buffering data streams.
<TElement>Specialized RingBuffer for undo/redo.
<ModuloArithmetic>In mathematics, modular arithmetic is a system of arithmetic for integers, where numbers "wrap around" when reaching a certain value, called the modulus.
Extension methods for IEnumerable.
*.puml files in Shos.UndoRedoList/Documents/ClassDiagrams are class diagram for PlantUML.
The following image is a class diagram made from these *.puml files.

using Microsoft.VisualStudio.TestTools.UnitTesting;
using Shos.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
namespace Shos.UndoRedoList.Tests
{
[TestClass]
public class SampleTest
{
[TestMethod]
public void UndoRedoTest()
{
// list which support undo/redo.
var target = new UndoRedoList<int, List<int>>();
Assert.IsFalse(target.CanUndo);
Assert.IsFalse(target.CanRedo);
// Modify target
target.Add(100);
Assert.AreEqual(1, target.Count);
Assert.AreEqual(100, target[0]);
Assert.IsTrue (target.CanUndo);
Assert.IsFalse(target.CanRedo);
// Undo
Assert.IsTrue(target.Undo());
Assert.AreEqual(0, target.Count);
Assert.IsFalse(target.CanUndo);
Assert.IsTrue (target.CanRedo);
// Redo
Assert.IsTrue(target.Redo());
Assert.AreEqual(1, target.Count);
Assert.AreEqual(100, target[0]);
Assert.IsTrue (target.CanUndo);
Assert.IsFalse(target.CanRedo);
}
[TestMethod]
public void ActionScopeTest()
{
// list which support undo/redo.
var target = new UndoRedoList<int, List<int>>();
Assert.IsFalse(target.CanUndo);
Assert.IsFalse(target.CanRedo);
// ActionScope
using (var scope = new UndoRedoList<int, List<int>>.ActionScope(target)) {
// Modify target in ActionScope
target.Add(100);
target.Add(200);
target.Add(300);
}
Assert.AreEqual(3, target.Count);
Assert.AreEqual(100, target[0]);
Assert.AreEqual(200, target[1]);
Assert.AreEqual(300, target[2]);
Assert.IsTrue(target.CanUndo);
// Undo
Assert.IsTrue(target.Undo());
// The 3 actions in ActionScope can undo in one time.
Assert.AreEqual(0, target.Count);
Assert.IsFalse(target.CanUndo);
}
[TestMethod]
public void DisabledUndoScopeTest()
{
// list which support undo/redo.
var target = new UndoRedoList<int, List<int>>();
Assert.IsFalse(target.CanUndo);
Assert.IsFalse(target.CanRedo);
// DisabledUndoScope
using (var scope = new UndoRedoList<int, List<int>>.DisabledUndoScope(target)) {
// Modify target in DisabledUndoScope
target.Add(100);
}
// You can't undo actions in DisabledUndoScope.
Assert.IsFalse(target.CanUndo);
Assert.IsFalse(target.CanRedo);
}
[TestMethod]
public void UndoRedoListTest()
{
// List which support undo/redo.
var target = new UndoRedoList<int>();
target.Add(100);
// You can undo/redo also.
Assert.IsTrue (target.CanUndo);
Assert.IsFalse(target.CanRedo);
Assert.IsTrue(target.Undo());
Assert.IsTrue(target.Redo());
}
[TestMethod]
public void UndoRedoObservableCollectionTest()
{
// ObservableCollection which support undo/redo.
var target = new UndoRedoObservableCollection<int>();
target.CollectionChanged += ObservableCollection_CollectionChanged;
target.Add(100);
// You can undo/redo also.
Assert.IsTrue (target.CanUndo);
Assert.IsFalse(target.CanRedo);
Assert.IsTrue(target.Undo());
Assert.IsTrue(target.Redo());
// event handler
static void ObservableCollection_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{ /* do domething */ }
}
}
}
See also: Shos.UndoRedoList.SampleApp
Fujio Kojima: a software developer in Japan
This library is under the MIT License.