Skip to content

Fujiwo/Shos.UndoRedoList

Repository files navigation

Shos.UndoRedoList

List and ObservableCollection which support undo/redo.

NuGet

You can install Shos.UndoRedoList to your project with NuGet on Visual Studio.

Package Manager

PM>Install-Package Shos.UndoRedoList -version 1.0.5

.NET CLI

>dotnet add package Shos.UndoRedoList --version 1.0.5

PackageReference

<PackageReference Include="Shos.UndoRedoList" Version="1.0.5" />

Projects

Types for collection which supports undo/redo.

Tests for Shos.UndoRedoList.

Performance tests for Shos.UndoRedoList.

Sample WPF app for UndoRedoObservableCollection.

Shos.UndoRedoList.SampleApp

Types

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>

Circular buffer - Wikipedia

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.

Extension methods for IEnumerable.

Class diagram

*.puml files in Shos.UndoRedoList/Documents/ClassDiagrams are class diagram for PlantUML.

The following image is a class diagram made from these *.puml files.

Shos.UndoRedoList class diagram

Larger class diagram image is here.

Samples

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

Related Articles

Author Info

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)
  • Twitter
  • Instagram

License

This library is under the MIT License.

Releases

No releases published

Packages

No packages published

Languages