At a certain point, I realized that keeping all launcher logic inside MainWindow is a dead-end approach.
As the project grows, everything starts piling up in one place: game launch, updates, settings, tray logic, config handling, services, UI state, and tons of event handlers.
So I migrated the project structure to Prism + Unity to separate responsibilities, clean up the codebase, and prepare the project for further scaling.
📂 LizeriumLauncher
│ App.xaml
│ App.xaml.cs // PrismApplication entry point
│
├── 📂 Bootstrapper / Infrastructure
│ ├── UnityConfig.cs // dependency registration
│ ├── RegionNames.cs // region constants
│ └── ...
│
├── 📂 Services
│ ├── IGameService.cs
│ ├── GameService.cs
│ ├── ISettingsService.cs
│ ├── SettingsService.cs
│ └── ...
│
├── 📂 Models
│ ├── GameButtonData.cs
│ └── ...
│
├── 📂 Views
│ ├── MainWindow.xaml
│ ├── MainWindow.xaml.cs
│ ├── SettingsGameWindow.xaml
│ ├── UpdatesWindow.xaml
│ └── ...
│
├── 📂 ViewModels
│ ├── MainWindowViewModel.cs
│ ├── SettingsGameWindowViewModel.cs
│ ├── UpdatesWindowViewModel.cs
│ └── ...
│
└── 📂 Modules
├── GameModule
│ ├── GameModule.cs
│ ├── Views/...
│ ├── ViewModels/...
│ └── Services/...
│
├── SettingsModule
│ ├── SettingsModule.cs
│ ├── Views/...
│ ├── ViewModels/...
│ └── Services/...
│
└── ...
I use Prism as the architectural foundation for the WPF application, and Unity Container for dependency management.
This gives me several key advantages:
- I’m no longer tied to a single
MainWindow - I can split functionality into modules
- I can properly inject dependencies
- UI logic moves into ViewModels, instead of code-behind
- the application becomes easier to scale, maintain, and test
In short — this is no longer just a “window with buttons”, but a properly structured client application.
Previously, a lot of logic lived directly inside MainWindow.xaml.cs:
- game launching
- file updates
- config handling
- window navigation
- tray logic
- application state management
After switching to Prism, I started organizing everything by responsibility.
This is where I place all logic that does not belong in the UI.
For example:
- launching the game
- working with file paths
- reading/saving settings
- update logic
- file validation
- launcher core logic
Example:
public interface IGameService
{
void LaunchGame();
}public class GameService : IGameService
{
public void LaunchGame()
{
// game launch logic
}
}ViewModels contain UI state and commands, not business logic.
For example:
- button text
- state flags
- selected options
- commands (launch, update, open windows)
Example:
public class MainWindowViewModel : BindableBase
{
private readonly IGameService _gameService;
public DelegateCommand LaunchGameCommand { get; }
public MainWindowViewModel(IGameService gameService)
{
_gameService = gameService;
LaunchGameCommand = new DelegateCommand(OnLaunchGame);
}
private void OnLaunchGame()
{
_gameService.LaunchGame();
}
}So:
ViewModeldefines what to doServicedefines how it is done
Views contain only the visual layer:
- XAML
- layout
- bindings
- visual containers
- Prism regions
The window should render, not “own the entire application logic”.
When functionality becomes large, I isolate it into modules.
Examples:
- settings module
- update module
- game management module
- diagnostics module
- content/news module
This prevents the project from turning into a mess of hundreds of files in one place.
With Prism, the application startup becomes structured and predictable.
App.xaml.csstarts- Application inherits from
PrismApplication - Prism calls
RegisterTypes() - Services and dependencies are registered
- Prism calls
CreateShell() - Main window (
MainWindow) is created - Modules are loaded
- Views are injected into regions
The application runs as a Prism Shell application, not a plain WPF app.
Example:
public partial class App : PrismApplication
{
protected override Window CreateShell()
{
return Container.Resolve<MainWindow>();
}
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.RegisterSingleton<IGameService, GameService>();
containerRegistry.RegisterSingleton<ISettingsService, SettingsService>();
}
}Instead of manually calling new SomeService() everywhere, everything is registered in the container:
containerRegistry.RegisterSingleton<IGameService, GameService>();
containerRegistry.RegisterSingleton<ISettingsService, SettingsService>();Dependencies are then injected automatically.
Previously:
var window = new MainWindow();
window.Show();With Prism, this is handled by the container.
MainWindow is created as the Shell, meaning:
👉 the app is built as an architecture, not a set of manual calls
I keep MainWindow.xaml.cs minimal — only UI-specific logic.
If something:
- launches the game
- checks configs
- updates data
- works with files
→ it does NOT belong in the View
In short:
Everything that performs actual work
Everything that manages UI state and commands
Everything that is rendered
Data structures
Isolated functional areas
Because it breaks very quickly:
- code becomes unreadable
- reuse becomes impossible
- adding features becomes painful
- debugging gets harder
- the project becomes rigid
Prism allows me to build the application as a system, not as a single giant event handler.
Switching to Prism + Unity is not about “using patterns for the sake of it”.
It’s about keeping the project maintainable as it grows.
I use this architecture to:
- separate responsibilities
- keep
MainWindowclean - scale functionality
- plug in modules
- control dependencies
- build a real application, not a temporary UI script