List 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.
class UndoRedoList<TElement, TList>
: IList<TElement>
where TList : IList<TElement>
, new()
IList implemented collection which supports undo/redo.
class UndoRedoList<TElement>
: UndoRedoList<TElement, List<TElement>>
List implemented collection which supports undo/redo.
class UndoRedoObservableCollection<TElement>
: UndoRedoList<TElement, ObservableCollection<TElement>>
, INotifyCollectionChanged
ObservableCollection which supports undo/redo.
class RingBuffer<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.
class UndoRedoRingBuffer : RingBuffer<TElement>
Specialized RingBuffer for undo/redo.
struct ModuloArithmetic : IEquatable<ModuloArithmetic>
Modular arithmetic - Wikipedia
In mathematics, modular arithmetic is a system of arithmetic for integers, where numbers "wrap around" when reaching a certain value, called the modulus.
static class EnumerableExtensions
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.
Larger class diagram image is here.
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
- Microsoft MVP for Development Tools - Visual C# (Jul. 2005 - Dec. 2014)
- Microsoft MVP for .NET (Jan. 2015 - Oct. 2015)
- Microsoft MVP for Visual Studio and Development Technologies (Nov. 2015 - Jun. 2018)
- Microsoft MVP for Developer Technologies (Nov. 2018 - Jun. 2024)
- MVP Profile
- Blog (Japanese)
- Web Site (Japanese)
This library is under the MIT License.