Skip to main content

Page Routing

Overview

The EFT Remittance Dashboard implements a streamlined page routing strategy that prioritizes simplicity and direct access over complex multi-page navigation. The routing system leverages the MepDash module's service registration patterns while maintaining a single-page application approach optimized for the remittance workflow.

Key Concepts

  • Service-Based Registration: All views and view models are registered through dependency injection
  • Singleton Pattern: Single instances ensure state persistence across the session
  • Direct Instantiation: Views are created directly rather than through route resolution
  • Type-Safe Dependencies: Constructor injection ensures compile-time safety

Implementation Details

Service Registration Pattern

The routing foundation is established in the RegisterServiceProvider class, which defines how views and view models are discovered and registered:

// MepApps.Dash.Ap.Rpt.EftRemittance/Services/RegisterServiceProvider.cs
internal static class RegisterServiceProvider
{
public static IServiceCollection RegisterServices(
IMepPluginServiceHandler mepPluginServiceHandler,
ISharedShellInterface sharedShellInterface)
{
IServiceCollection serviceCollection = new ServiceCollection();

// Register MepDash plugin services first
serviceCollection = mepPluginServiceHandler.RegisterMepPluginServices(serviceCollection);

// Register view models and views as singletons
serviceCollection.AddSingleton<MainViewModel>();
serviceCollection.AddSingleton<IEftRemit_RunReportsViewModel, EftRemit_RunReportsViewModel>();
serviceCollection.AddSingleton<EftRemit_RunReportsView>();

return serviceCollection;
}
}

This registration pattern ensures:

  • All components are available for dependency injection
  • Views maintain state throughout the session
  • Dependencies are resolved automatically

View Discovery Mechanism

The system discovers views through explicit registration rather than convention-based discovery:

// MainView.xaml.cs - Service provider initialization
private void InitializeServices()
{
var serviceCollection = RegisterServiceProvider.RegisterServices(
_mepPluginServiceHandler,
_sharedShellInterface);

_mepPluginServiceProvider = serviceCollection.BuildServiceProvider();
_mepPluginServiceProviderStatic = _mepPluginServiceProvider;

// Resolve the main view model
viewmodel = _mepPluginServiceProvider.GetService<MainViewModel>();
DataContext = viewmodel;
}

Route Configuration

While the dashboard doesn't use traditional routing, it establishes a route-like structure through service registration:

// Conceptual route table (implicit through DI registration)
// Route: "/" -> MainViewModel -> MainView
// Route: "/reports" -> EftRemit_RunReportsViewModel -> EftRemit_RunReportsView

Parameter Passing Patterns

Parameters can be passed through constructor injection and method calls:

// Constructor-based parameter passing
public class EftRemit_RunReportsViewModel : BaseViewModel
{
private readonly IEftRemittanceService _eftRemittanceService;
private readonly ISharedShellInterface _sharedShellInterface;

public EftRemit_RunReportsViewModel(
IEftRemittanceService eftRemittanceService,
ISharedShellInterface sharedShellInterface)
{
_eftRemittanceService = eftRemittanceService;
_sharedShellInterface = sharedShellInterface;
// Services are "parameters" passed during route resolution
}
}

// Method-based parameter passing for runtime values
public async Task InitializeWithPayment(string paymentNumber)
{
var paymentDetails = await _eftRemittanceService
.QueryPaymentNumber(paymentNumber, CheckFilter);
// Initialize view with specific payment
}

Examples

Example 1: View Factory Pattern

The ViewFactoryService equivalent is implemented through the service provider:

public class ViewFactory
{
private readonly IServiceProvider _serviceProvider;

public T CreateView<T>() where T : class
{
return _serviceProvider.GetRequiredService<T>();
}

public object CreateView(Type viewType)
{
return _serviceProvider.GetRequiredService(viewType);
}
}

// Usage
var reportsView = ViewFactory.CreateView<EftRemit_RunReportsView>();

Example 2: Route Registration with Metadata

While not implemented, the architecture supports adding route metadata:

public class RouteRegistration
{
public static void RegisterRoutes(IServiceCollection services)
{
services.AddSingleton<IRouteMetadata>(new RouteMetadata
{
Path = "/eft-remittance/reports",
ViewType = typeof(EftRemit_RunReportsView),
ViewModelType = typeof(EftRemit_RunReportsViewModel),
Title = "EFT Remittance Reports",
Icon = "Report"
});
}
}

Example 3: Deep Linking Support

The system can support deep linking through initialization parameters:

// MainView.xaml.cs
public void HandleDeepLink(string uri)
{
// Parse URI: syspro://eft-remittance?payment=PMT001&view=details
var parameters = ParseUri(uri);

if (parameters.ContainsKey("payment"))
{
var viewModel = _serviceProvider.GetService<IEftRemit_RunReportsViewModel>();
viewModel.SelectedPayment = new SelectionItem
{
Value = parameters["payment"]
};
// Auto-load payment details
}
}

Naming Conventions

The project follows consistent naming patterns for routing:

  1. Views: {Feature}View.xaml (e.g., EftRemit_RunReportsView.xaml)
  2. ViewModels: {Feature}ViewModel.cs with I{Feature}ViewModel interface
  3. Services: {Feature}Service.cs with I{Feature}Service interface
  4. Routes (conceptual): /eft-remittance/{action}

Service Lifetime Management

All routable components use singleton lifetime:

// Singleton registration ensures:
// 1. State persistence across navigation
// 2. Efficient memory usage
// 3. Consistent data across views

services.AddSingleton<MainViewModel>();
services.AddSingleton<IEftRemit_RunReportsViewModel, EftRemit_RunReportsViewModel>();
services.AddSingleton<EftRemit_RunReportsView>();

Route Constraints and Rules

While explicit routing isn't used, the system enforces constraints through:

  1. Type Safety: Constructor parameters ensure correct dependencies
  2. Interface Contracts: ViewModels must implement IBaseViewModel
  3. Initialization Order: Services must be registered before views
  4. Singleton Constraint: Each view has exactly one instance

Integration with SYSPRO Menu

The dashboard is launched from SYSPRO's menu system, which acts as the primary router:

<!-- SYSPRO Menu Configuration -->
<MenuItem>
<Id>AP.Reports.EftRemittance</Id>
<Title>EFT Remittance</Title>
<Module>MepApps.Dash.Ap.Rpt.EftRemittance</Module>
<View>MainView</View>
</MenuItem>

Best Practices

  1. Register Dependencies First: Always register services before views
  2. Use Interfaces: Define interfaces for all view models
  3. Avoid Circular Dependencies: Structure dependencies hierarchically
  4. Validate Services: Check for null services in constructors
  5. Document Routes: Comment the implicit routing structure

Common Pitfalls

  • Forgetting to register a service leads to runtime resolution failures
  • Circular dependencies cause stack overflow exceptions
  • Missing interface registrations break dependency injection
  • Incorrect service lifetimes cause state inconsistencies

Summary

The EFT Remittance Dashboard's page routing system prioritizes simplicity and reliability over complex navigation patterns. By leveraging dependency injection for service registration and resolution, the system provides a type-safe, maintainable approach to view management. While it doesn't implement traditional URL-based routing, the architecture provides all the benefits of a well-structured routing system: clear separation of concerns, testability, and extensibility for future enhancements.