UndoService 2.1.11

There is a newer version of this package available.
See the version list below for details.
dotnet add package UndoService --version 2.1.11                
NuGet\Install-Package UndoService -Version 2.1.11                
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="UndoService" Version="2.1.11" />                
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add UndoService --version 2.1.11                
#r "nuget: UndoService, 2.1.11"                
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install UndoService as a Cake Addin
#addin nuget:?package=UndoService&version=2.1.11

// Install UndoService as a Cake Tool
#tool nuget:?package=UndoService&version=2.1.11                

This is a simple undo/redo service based on the momento pattern. It generally doesn't require custom classes or methods, as it uses generic stacks to store state and delegate methods to access it. It can track changes to different parts of an application individually, while using one unified interface for performing undo/redo. This reduces the memory imprint and facilitates modular design.

Features

  • Multiple undo/redo stacks can be used concurrently, reducing memory imprint.
  • Undo/Redo can be performed interchangeably on the state of the application whole or on specific parts.
  • No requirement to write custom classes or methods as it uses generic stacks and delegate methods.
  • Optional cap on the size of Undo stacks.
  • Recorded states can be tagged. Tag values are present in the arguments of the StateSet event, which is raised on Undo or Redo.

Usage

The simplest approach is to use a single UndoService for application state. Alternatively you can use separate UndoServices for different sections in conjunction with an UndoServiceAggregate. This means that the whole of the application state does not need to be recorded on each change.

To create an UndoService, pass the delegate methods that are used to get and set the state. To use it, invoke RecordState() after making changes to the state. (Note that the initial state is recorded automatically when the UndoService is initialized.) Use CanUndo and CanRedo to enable/disable Undo/Redo commands.

Simple UndoService Example


        // We will demonstrate change tracking on this string. 
        private string _statefulString;     
        
        // This is the method to get the state of the tracked object.
        // (If you have an existing method, you can just put it in a wrapper to match the delegate signature.)
        private void GetStringState(out string state)
        {
            state = _statefulString;
        }

        // Method to set the state.
        private void SetStringState(string value)
        {
            _statefulString = value;
        }

        public void UndoRedoTest()
        {
            var undoServiceForString = new UndoService<string>(GetStringState, SetStringState, null);

            _statefulString = "One";
            undoServiceForString.RecordState();
            _statefulString = "Two";
            undoServiceForString.RecordState();

            undoServiceForString.Undo();
            Assert.IsTrue(_statefulString.Equals("One"));

            undoServiceForString.Redo();
            Assert.IsTrue(_statefulString.Equals("Two"));
        }

To create an UndoServiceAggregate, pass it an IUndoService array. To use it, invoke RecordState() in the child UndoServices to record changes. Generally undo and redo would be done via the UndoServiceAggregate. However, you can also do so in the child UndoServices directly to undo the last changes to specific elements.

Simple UndoServiceAggregate Example


        // We will use an UndoService to track changes to this
        private string _statefulString;     

        // We will use a second UndoService to track changes to this.
        private int _statefulInt;

        public void AggregateUndoServiceUndoRedoTest()
        {
            // Create the UndoServiceAggregate by passing an IUndoService array
            var undoServiceForInt = new UndoService<int>(GetIntState, SetIntState, null);
            var undoServiceForString = new UndoService<string>(GetStringState, SetStringState, null);
            IUndoService[] subservices = { undoServiceForInt, undoServiceForString };
            var serviceAggregate = new UndoServiceAggregate(subservices);


           // Use the Undo services to track changes to their associated objects.
            _statefulInt = 1;
            undoServiceForInt.RecordState();
            _statefulString = "One";
            undoServiceForString.RecordState();
            _statefulInt = 2;
            undoServiceForInt.RecordState();
            _statefulInt = 3;
            undoServiceForInt.RecordState();
            _statefulString = "Two";
            undoServiceForString.RecordState();


            // Use the Service Aggregate to undo/redo the most recent changes.
            serviceAggregate.Undo();
            Assert.IsTrue(_statefulString.Equals("One"));
            Assert.IsTrue(_statefulInt == 3);

            serviceAggregate.Undo();
            Assert.IsTrue(_statefulString.Equals("One"));
            Assert.IsTrue(_statefulInt == 2);

            serviceAggregate.Undo();
            Assert.IsTrue(_statefulString.Equals("One"));
            Assert.IsTrue(_statefulInt == 1);

            serviceAggregate.Redo();
            Assert.IsTrue(_statefulString.Equals("One"));
            Assert.IsTrue(_statefulInt == 2);

            serviceAggregate.Redo();
            Assert.IsTrue(_statefulString.Equals("One"));
            Assert.IsTrue(_statefulInt == 3);

            serviceAggregate.Redo();
            Assert.IsTrue(_statefulString.Equals("Two"));
            Assert.IsTrue(_statefulInt == 3);
        }
        
        
        // These are the methods to get/access state, used by the UndoServices above.

        private void GetStringState(out string state)
        {
            state = _statefulString;
        }

        private void SetStringState(string value)
        {
            _statefulString = value;
        }

        private void GetIntState(out int state)
        {
            state = _statefulInt;
        }

        private void SetIntState(int value)
        {
            _statefulInt = value;
        }

Refer to the unit test project in the source repository for examples of other features.

Public Interfaces

  • IStateTracker is used to record changes to state. It is implemented by UndoService.
  • IUndoRedo is used to execute Undo and Redo operations. It is implemented by UndoService and UndoServiceAggregate.
  • IUndoService is used to both record state and perform undo/redo. It is implemented by UndoService.

IStateTracker

    // Tracks changes to a part of the application for Undo/Redo. Used in conjunction with IUndoRedo
    public interface IStateTracker
    {
        // Raised when Undo or Redo is executed.
        event StateSetEventHandler StateSet;

        // Raised when RecordState() is executed.
        event StateRecordedEventHandler StateRecorded;

        // Records the current state of the tracked objects and puts it on the undo stack
        // If you assign a tag value, it will be in the StateSet event arguments if this state is restored.
        void RecordState(object tag = null);
    }

IUndoRedo


    // Performs Undo/redo actions. Used in conjunction with object(s) that implement IStateTracker
    public interface IUndoRedo
    {
        bool CanUndo { get; }
        
        bool CanRedo { get; }

        // Clear the Undo and Redo stacks.
        void ClearStacks();

        // Clear the Undo stack (but not the redo stack).
        void ClearUndoStack();

        void Undo();

        void Redo();
    }
Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • .NETStandard 2.0

    • No dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
2.3.2 1,821 11/17/2020
2.3.1 481 7/22/2020
2.3.1-alpha 345 7/21/2020
2.3.0-alpha 348 7/21/2020
2.2.3-alpha 320 7/15/2020
2.2.2-alpha 346 6/22/2020
2.2.0-alpha 349 6/3/2020
2.1.11 485 6/2/2020
2.1.11-alpha 346 5/28/2020
2.1.10-alpha 366 5/28/2020
2.1.9-alpha 312 5/28/2020
2.1.8-alpha 364 5/28/2020
2.1.7-alpha 368 5/27/2020
2.1.6-alpha 316 5/26/2020
2.1.5-alpha 298 5/26/2020
2.1.4-alpha 326 5/26/2020
2.1.3-alpha 353 5/25/2020
2.1.2-alpha 366 5/25/2020
2.0.2 486 5/21/2020
1.1.0 496 3/27/2020
1.0.0 512 3/16/2020

* AggregateUndoService renamed UndoServiceAggregate.
* Recorded states can now be tagged. Tag values are present in the arguments of the StateSet event, which is raised on Undo or Redo.
* Undo/Redo can now be performed interchangeably on the state of the application whole or on specific parts.