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:
- Views:
{Feature}View.xaml(e.g.,EftRemit_RunReportsView.xaml) - ViewModels:
{Feature}ViewModel.cswithI{Feature}ViewModelinterface - Services:
{Feature}Service.cswithI{Feature}Serviceinterface - 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:
- Type Safety: Constructor parameters ensure correct dependencies
- Interface Contracts: ViewModels must implement IBaseViewModel
- Initialization Order: Services must be registered before views
- 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
- Register Dependencies First: Always register services before views
- Use Interfaces: Define interfaces for all view models
- Avoid Circular Dependencies: Structure dependencies hierarchically
- Validate Services: Check for null services in constructors
- 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
Related Documentation
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.