Navigation Architecture
Overview
The AR Payment Reversal dashboard implements a sophisticated navigation architecture built on top of the MepDash module framework. This document explains the overall navigation strategy, routing patterns, and state management mechanisms that enable seamless transitions between different views while maintaining data integrity and user context.
Key Concepts
- Hierarchical Navigation Model: Three-tier navigation structure with MainView as the root container
- Event-Driven Navigation: Uses event aggregation for decoupled navigation triggers
- State Preservation: Implements INavigationTarget for lifecycle management
- Service-Based Routing: Centralized navigation service with dependency injection
Implementation Details
Core Navigation Components
The navigation architecture centers around several key components working in concert:
1. MainViewModel as Navigation Host
// MepApps.Dash.Ar.Maint.PaymentReversal/ViewModels/MainViewModel.cs (Lines 5-144)
public class MainViewModel : BaseRouteableViewModel
{
private readonly BasicMepNavigationUiHostNotificator _navigationUiHostNotificator;
public MainViewModel(...)
{
_navigationUiHostNotificator.MainContentChanged += (sender, e) => SetMainContent(sender);
this.Title = "AR Payment Reversal";
}
public void SetMainContent(object content)
{
Dispatcher.CurrentDispatcher.Invoke(() =>
{
var navigatedFromContent = mainContent;
MainContent = content;
if (navigatedFromContent is INavigationTarget)
((INavigationTarget) navigatedFromContent)?.NavigatedFrom();
if (content is INavigationTarget)
((INavigationTarget) content)?.NavigatedTo();
});
}
}
The MainViewModel serves as the primary navigation container, managing the active content and coordinating navigation lifecycle events. It handles:
- Content switching through the SetMainContent method
- Navigation lifecycle notifications (NavigatedFrom/NavigatedTo)
- Thread-safe UI updates via Dispatcher
2. BaseRouteableViewModel Pattern
// MepApps.Dash.Ar.Maint.PaymentReversal/Models/BindingModels/BaseRouteableViewModel.cs
public abstract class BaseRouteableViewModel : BaseViewModel, INavigationTarget
{
public virtual void NavigatedTo() { }
public virtual void NavigatedFrom() { }
public string Title { get; set; }
}
All navigable ViewModels inherit from BaseRouteableViewModel, providing:
- Consistent navigation lifecycle hooks
- Title management for view identification
- INotifyPropertyChanged implementation through BaseViewModel
Navigation Flow Architecture
The dashboard implements a specific navigation flow optimized for the payment reversal workflow:
MainView (Container)
├── ArReversePaymentQueueView (Default/Home)
│ ├── ArReversePaymentAddPaymentView (Add payments to queue)
│ │ └── CustomerSelectionDialogView (Modal selection)
│ └── ArReversePaymentCompletionView (Post-processing results)
└── SettingsDialogView (Configuration modal)
Navigation Service Integration
// MepApps.Dash.Ar.Maint.PaymentReversal/ViewModels/MainViewModel.cs (Lines 59-63)
var navigationService = MainView.MepPluginServiceProvider.GetService<IBasicMepNavigationService>();
navigationService.NavigateToNewUserControl<ArReversePaymentQueueView>();
The navigation service provides type-safe navigation with dependency injection support:
- Automatic view instantiation
- ViewModel binding
- Service resolution for dependencies
Navigation State Management
The architecture implements sophisticated state management through several patterns:
1. Property-Based Navigation Triggers
// MepApps.Dash.Ar.Maint.PaymentReversal/ViewModels/ArReversePaymentQueueViewModel.cs (Lines 79-95)
private async void ArReversePaymentQueueViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
switch (e.PropertyName)
{
case "ReversePaymentQueueHeaders":
await ReversePaymentQueueHeadersChanged().ConfigureAwait(true);
break;
}
}
Property changes can trigger navigation-related updates, enabling reactive navigation based on data changes.
2. Event-Driven Navigation
// MepApps.Dash.Ar.Maint.PaymentReversal/ViewModels/ArReversePaymentQueueViewModel.cs (Lines 259-272)
public event EventHandler<EventArgs> NavigateToAddPayment;
private async Task AddPayment()
{
IsLoading = true;
await Task.Delay(300).ConfigureAwait(true);
NavigateToAddPayment?.Invoke(this, new EventArgs());
}
Events decouple navigation triggers from navigation logic, allowing views to request navigation without knowing implementation details.
Navigation Guards and Validation
The architecture includes navigation validation to ensure data integrity:
// MepApps.Dash.Ar.Maint.PaymentReversal/ViewModels/ArReversePaymentQueueViewModel.cs (Lines 341-346)
if (ReversePaymentQueueHeaders == null || !ReversePaymentQueueHeaders.Any())
return;
IsLoading = true;
// Proceed with navigation
Navigation guards prevent invalid transitions and ensure required data is present before allowing navigation.
Thread Safety and UI Synchronization
All navigation operations are synchronized with the UI thread to prevent cross-thread exceptions:
// MepApps.Dash.Ar.Maint.PaymentReversal/ViewModels/MainViewModel.cs (Lines 84-105)
Dispatcher.CurrentDispatcher.Invoke(() =>
{
if (_logger.IsEnabled(LogLevel.Trace))
_logger.LogTrace("CHANGING MAIN CONTENT to {ContentIsNull}, {NameOfContent}",
content == null, content?.GetType().Name);
// Navigation logic here
});
Navigation Performance Optimization
The architecture includes several performance optimizations:
- Lazy Loading: Views are instantiated only when needed
- Loading Indicators: Visual feedback during navigation transitions
- Async Navigation: Non-blocking navigation operations
- View Caching: Previously loaded views can be cached for faster subsequent access
Error Handling in Navigation
Comprehensive error handling ensures navigation failures don't crash the application:
// MepApps.Dash.Ar.Maint.PaymentReversal/ViewModels/MainViewModel.cs (Lines 28-33)
catch (Exception ex)
{
_logger.LogError(ex, "Failed to initialize MainViewModel");
throw;
}
Navigation Lifecycle
The complete navigation lifecycle follows this pattern:
- Navigation Request: User action or system event triggers navigation
- Validation: Guards check if navigation is allowed
- NavigatedFrom: Current view receives notification it's being left
- Content Switch: MainViewModel updates MainContent property
- NavigatedTo: New view receives notification it's being activated
- Initialization: New view performs any required setup
- Render: UI updates to show new content
Memory Management During Navigation
The architecture implements proper disposal patterns to prevent memory leaks:
// MepApps.Dash.Ar.Maint.PaymentReversal/ViewModels/MainViewModel.cs (Lines 136-144)
private bool disposed = false;
public void Dispose()
{
if (!disposed)
{
disposed = true;
// Cleanup logic
}
}
ViewModels implement IDisposable to ensure proper cleanup when navigating away from views.
Best Practices Applied
- Separation of Concerns: Navigation logic separated from business logic
- Dependency Injection: All navigation services injected via DI container
- Event Aggregation: Loose coupling between navigation triggers and handlers
- Async/Await: Non-blocking navigation operations
- Logging: Comprehensive logging for debugging navigation issues
- Error Recovery: Graceful handling of navigation failures
Common Pitfalls to Avoid
- Direct View Instantiation: Always use navigation service for view creation
- Synchronous Heavy Operations: Use async patterns during navigation
- Missing Disposal: Ensure ViewModels are properly disposed
- Thread Violations: Always use Dispatcher for UI updates
- Circular Navigation: Implement guards to prevent infinite navigation loops
Related Documentation
- Page Routing - Details on route registration and discovery
- Navigation Events - Event-driven navigation patterns
- Dialog Navigation - Modal dialog navigation strategies
- Navigation Best Practices - Guidelines and recommendations
Summary
The AR Payment Reversal dashboard's navigation architecture provides a robust, maintainable, and performant framework for managing view transitions. By leveraging the MepDash module's navigation infrastructure, implementing proper lifecycle management, and following established patterns, the system ensures a smooth user experience while maintaining code quality and testability. The architecture's event-driven nature and dependency injection support make it easy to extend and modify navigation flows as requirements evolve.