GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — GitHub - ktsu-dev/Navigation: A robust .NET library for implementing navigation stacks with undo/redo support, persistence capabilities, and event-driven architecture. Perfect for applications requiring navigation state management.

BranchesTags
Open more actions menu

Latest commit

 

History

397 Commits
397 Commits

Folders and files

NameName
Last commit message
Last commit date
.claude
.claude
 
 
.github
.github
 
 
Navigation.Test
Navigation.Test
 
 
Navigation
Navigation
 
 
docs
docs
 
 
.editorconfig
.editorconfig
 
 
.gitattributes
.gitattributes
 
 
.gitignore
.gitignore
 
 
.mailmap
.mailmap
 
 
.runsettings
.runsettings
 
 
AUTHORS.md
AUTHORS.md
 
 
AUTHORS.url
AUTHORS.url
 
 
CHANGELOG.md
CHANGELOG.md
 
 
COPYRIGHT.md
COPYRIGHT.md
 
 
DESCRIPTION.md
DESCRIPTION.md
 
 
Directory.Packages.props
Directory.Packages.props
 
 
LATEST_CHANGELOG.md
LATEST_CHANGELOG.md
 
 
LICENSE.md
LICENSE.md
 
 
Navigation.sln
Navigation.sln
 
 
PROJECT_URL.url
PROJECT_URL.url
 
 
README.md
README.md
 
 
TAGS.md
TAGS.md
 
 
VERSION.md
VERSION.md
 
 
global.json
global.json
 
 
icon.png
icon.png
 
 

Repository files navigation

ktsu.Navigation

A robust .NET library for implementing navigation stacks with undo/redo support and persistence capabilities.

License NuGet Version NuGet Version NuGet Downloads GitHub commit activity GitHub contributors GitHub Actions Workflow Status

Overview

ktsu.Navigation provides a complete navigation stack implementation that supports:

  • Forward/Backward Navigation: Navigate through items with full browser-like back/forward functionality
  • Undo/Redo Operations: Complete undo/redo support for all navigation actions
  • Persistence: Save and restore navigation state to various storage backends
  • Event-Driven Architecture: React to navigation changes with comprehensive event notifications
  • Generic Design: Works with any navigation item type implementing INavigationItem
  • Factory Pattern: Easy creation of navigation stacks with different configurations

Perfect for applications that need robust navigation management, such as:

  • Code editors with file navigation
  • Image viewers with browsing history
  • Document management systems
  • Any application requiring navigation state management

Features

Core Navigation

  • ✅ NavigateTo, GoBack, GoForward operations
  • ✅ Current item tracking with CanGoBack/CanGoForward status
  • ✅ Complete navigation history management
  • ✅ Forward history clearing when navigating to new items
  • ✅ Thread-safe operations

Undo/Redo System

  • ✅ Full undo/redo support for navigation operations
  • ✅ Configurable history size limits
  • ✅ State change notifications
  • ✅ Error handling during undo/redo operations
  • ✅ Redirects made by a NavigationChanged handler during an undo or redo are not recorded as new actions, so the redo history survives them

Persistence

  • ✅ JSON file persistence provider
  • ✅ In-memory persistence for testing
  • ✅ Async operations with cancellation token support
  • ✅ Custom persistence provider support

Events & Extensibility

  • ✅ Navigation change events (NavigateTo, GoBack, GoForward, Clear)
  • ✅ Metadata support for navigation items
  • ✅ Generic design for custom navigation item types
  • ✅ Factory pattern for easy configuration

Installation

Add the NuGet package:

dotnet add package ktsu.Navigation

Quick Start

Basic Navigation

using ktsu.Navigation.Models;
using ktsu.Navigation.Services;

// Create navigation items
var page1 = new NavigationItem("page1", "Home Page");
var page2 = new NavigationItem("page2", "About Page");
var page3 = new NavigationItem("page3", "Contact Page");

// Create a navigation stack
var navigation = new Navigation<NavigationItem>();

// Navigate through items
navigation.NavigateTo(page1);  // Current: Home Page
navigation.NavigateTo(page2);  // Current: About Page
navigation.NavigateTo(page3);  // Current: Contact Page

// Go back and forward
var previous = navigation.GoBack();    // Current: About Page
var next = navigation.GoForward();     // Current: Contact Page

// Check navigation state
Console.WriteLine($"Current: {navigation.Current?.DisplayName}");
Console.WriteLine($"Can go back: {navigation.CanGoBack}");
Console.WriteLine($"Can go forward: {navigation.CanGoForward}");

With Undo/Redo Support

using ktsu.Navigation.Services;

// Create navigation with undo/redo support
var undoRedoProvider = new SimpleUndoRedoProvider(maxHistorySize: 50);
var navigation = new Navigation<NavigationItem>(undoRedoProvider);

navigation.NavigateTo(page1);
navigation.NavigateTo(page2);

// Undo the last navigation
undoRedoProvider.Undo();  // Back to page1

// Redo the navigation
undoRedoProvider.Redo();  // Forward to page2 again

With Persistence

using ktsu.Navigation.Services;

// Create navigation with JSON file persistence
var persistenceProvider = new JsonFilePersistenceProvider<NavigationItem>("navigation-state.json");
var navigation = new Navigation<NavigationItem>(undoRedoProvider: null, persistenceProvider: persistenceProvider);

navigation.NavigateTo(page1);
navigation.NavigateTo(page2);

// Save navigation state
await navigation.SaveStateAsync();

// Later... restore navigation state
await navigation.LoadStateAsync();
Console.WriteLine($"Restored to: {navigation.Current?.DisplayName}");

Saving subclasses of the item type

JsonFilePersistenceProvider<T> writes each item as T. If a Navigation<NavigationItem> holds a subclass such as DocumentPage : NavigationItem, JSON can't save its type or the properties it adds. So SaveStateAsync throws NotSupportedException naming the subclass, and the last save stays in place. To keep several page types in one stack, configure polymorphism for T through the JSON options:

using System.Text.Json;
using System.Text.Json.Serialization.Metadata;

var options = new JsonSerializerOptions
{
    WriteIndented = true,
    PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
    TypeInfoResolver = new DefaultJsonTypeInfoResolver
    {
        Modifiers =
        {
            typeInfo =>
            {
                if (typeInfo.Type == typeof(NavigationItem))
                {
                    typeInfo.PolymorphismOptions = new JsonPolymorphismOptions
                    {
                        DerivedTypes = { new JsonDerivedType(typeof(DocumentPage), "document") },
                    };
                }
            },
        },
    },
};

var persistenceProvider = new JsonFilePersistenceProvider<NavigationItem>("navigation-state.json", options);

Every subclass you save needs a JsonDerivedType entry. Plain NavigationItem items keep working alongside them.

Complete Example with All Features

using ktsu.Navigation.Services;

// Create providers
var undoRedoProvider = new SimpleUndoRedoProvider();
var persistenceProvider = new JsonFilePersistenceProvider<NavigationItem>("app-navigation.json");

// Create navigation with all features
var navigation = new Navigation<NavigationItem>(undoRedoProvider, persistenceProvider);

// Subscribe to navigation events
navigation.NavigationChanged += (sender, e) =>
{
    Console.WriteLine($"Navigation: {e.NavigationType}");
    Console.WriteLine($"From: {e.PreviousItem?.DisplayName ?? "None"}");
    Console.WriteLine($"To: {e.CurrentItem?.DisplayName ?? "None"}");
};

// Use factory for easier creation
var factory = new NavigationStackFactory(undoRedoProvider);
var anotherNavigation = factory.CreateNavigationStack<NavigationItem>(undoRedoProvider, persistenceProvider);

Custom Navigation Items

public class DocumentNavigationItem : INavigationItem
{
    public string Id { get; }
    public string DisplayName { get; set; }
    public DateTime CreatedAt { get; }
    public IReadOnlyDictionary<string, object> Metadata => _metadata.AsReadOnly();
    
    private readonly Dictionary<string, object> _metadata = new();
    
    public DocumentNavigationItem(string filePath, string displayName)
        : this(filePath, displayName, DateTime.UtcNow, null)
    {
        SetMetadata("FilePath", filePath);
        SetMetadata("FileSize", new FileInfo(filePath).Length);
    }
    
    // JsonFilePersistenceProvider reloads saved items through this constructor. System.Text.Json
    // needs each parameter to have the same name and type as the property it sets.
    [JsonConstructor]
    public DocumentNavigationItem(string id, string displayName, DateTime createdAt, IReadOnlyDictionary<string, object>? metadata)
    {
        Id = id;
        DisplayName = displayName;
        CreatedAt = createdAt;
        foreach (var entry in metadata ?? new Dictionary<string, object>())
        {
            _metadata[entry.Key] = entry.Value;
        }
    }
    
    public void SetMetadata(string key, object value) => _metadata[key] = value;
    public bool RemoveMetadata(string key) => _metadata.Remove(key);
}

// Use with navigation
var navigation = new Navigation<DocumentNavigationItem>();
var document = new DocumentNavigationItem("/path/to/file.txt", "My Document");
navigation.NavigateTo(document);

Architecture

The library follows clean architecture principles with well-separated concerns:

  • Models: NavigationItem, NavigationState - Core data structures
  • Contracts: Interfaces defining the contracts for all components
  • Services: Implementation classes for navigation, persistence, and undo/redo

Key Interfaces

  • INavigation<T>: Main navigation operations interface
  • INavigationItem: Contract for items that can be navigated to
  • IPersistenceProvider<T>: Contract for persistence implementations
  • IUndoRedoProvider: Contract for undo/redo implementations

Extension Points

Create custom persistence providers by implementing the IPersistenceProvider<T> interface:

public class DatabasePersistenceProvider<T> : IPersistenceProvider<T> where T : INavigationItem
{
    public async Task SaveStateAsync(INavigationState<T> state, CancellationToken cancellationToken = default)
    {
        // Save to database
        await Task.CompletedTask;
    }

    public async Task<INavigationState<T>?> LoadStateAsync(CancellationToken cancellationToken = default)
    {
        // Load from database
        return await Task.FromResult<INavigationState<T>?>(null);
    }

    // Implement HasSavedState, ClearState, etc.
}

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

License

MIT License. Copyright (c) ktsu.dev

About

A robust .NET library for implementing navigation stacks with undo/redo support, persistence capabilities, and event-driven architecture. Perfect for applications requiring navigation state management.

ktsu.dev

Topics

asyncbrowser-historydotnetevent-drivengenerichistoryjsonnavigationpersistenceredoserializationstackstate-managementundo

Resources

Readme
MIT license
Activity
Custom properties

Stars

0 stars

Watchers

0 watching

Forks

0 forks
Report repository

Releases

Packages

Used by

Contributors

Languages

You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.