# MVVM Architecture Implementation **Referenced Files in This Document** - [BaseViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/BaseViewModel.cs) - [MainWindowViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/MainWindowViewModel.cs) - [App.axaml.cs](file://ClassicAssist.Avalonia/App.axaml.cs) - [Program.cs](file://ClassicAssist.Avalonia/Program.cs) - [MainWindow.axaml.cs](file://ClassicAssist.Avalonia/Views/MainWindow.axaml.cs) - [AvaloniaDispatcher.cs](file://ClassicAssist.Avalonia/AvaloniaDispatcher.cs) - [AvaloniaUIInvoker.cs](file://ClassicAssist.Avalonia/AvaloniaUIInvoker.cs) - [IDispatcher.cs](file://ClassicAssist.Shared/IDispatcher.cs) - [IUIInvoker.cs](file://ClassicAssist.Shared/IUIInvoker.cs) - [DialogWindowViewModel.cs](file://ClassicAssist.Avalonia/ViewModels/DialogWindowViewModel.cs) - [AutolootViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/Agents/AutolootViewModel.cs) - [DressTabViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/Agents/DressTabViewModel.cs) - [FriendsTabViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/Agents/FriendsTabViewModel.cs) - [OrganizerTabViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/Agents/OrganizerTabViewModel.cs) - [ScavengerTabViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/Agents/ScavengerTabViewModel.cs) - [VendorBuyTabViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/Agents/VendorBuyTabViewModel.cs) - [VendorSellTabViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/Agents/VendorSellTabViewModel.cs) - [ClilocSelectionViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/Autoloot/ClilocSelectionViewModel.cs) - [CustomPropertiesViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/Autoloot/CustomPropertiesViewModel.cs) - [PropertySelectionViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/Autoloot/PropertySelectionViewModel.cs) - [AboutControlViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/AboutControlViewModel.cs) - [DebugViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/DebugViewModel.cs) - [GeneralControlViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/GeneralControlViewModel.cs) - [HotkeyEntryViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/HotkeyEntryViewModel.cs) - [HotkeysTabViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/HotkeysTabViewModel.cs) - [MacrosTabViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/MacrosTabViewModel.cs) - [NewProfileViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/NewProfileViewModel.cs) - [ObjectInspectorViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/ObjectInspectorViewModel.cs) - [OptionsTabViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/OptionsTabViewModel.cs) - [RepositionableGumpViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/RepositionableGumpViewModel.cs) - [SkillsTabViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/SkillsTabViewModel.cs) - [ChatManager.cs](file://ClassicAssist.Shared/Data/Chat/ChatManager.cs) - [ChatMessage.cs](file://ClassicAssist.Shared/Data/Chat/ChatMessage.cs) - [IncomingPacketHandlers.cs](file://ClassicAssist.Shared/UO/Network/IncomingPacketHandlers.cs) - [MobileIncoming.cs](file://ClassicAssist.Shared/UO/Network/Packets/MobileIncoming.cs) - [UseObject.cs](file://ClassicAssist.Shared/UO/Network/Packets/UseObject.cs) ## Table of Contents 1. [Introduction](#introduction) 2. [Project Structure](#project-structure) 3. [Core Components](#core-components) 4. [Architecture Overview](#architecture-overview) 5. [Detailed Component Analysis](#detailed-component-analysis) 6. [Dependency Analysis](#dependency-analysis) 7. [Performance Considerations](#performance-considerations) 8. [Troubleshooting Guide](#troubleshooting-guide) 9. [Conclusion](#conclusion) 10. [Appendices](#appendices) ## Introduction This document explains the Model-View-ViewModel (MVVM) architecture implemented in ClassicAssist.Avalonia. It focuses on separation of concerns between Views (XAML), ViewModels (C# logic), and Models (data), with emphasis on: - BaseViewModel hierarchy and property change notification patterns - Command binding mechanisms - Data flow from game engine through ViewModels to UI components - ViewModel composition, dependency injection, and lifecycle management - Error handling strategies, validation patterns, and testing approaches for MVVM components The goal is to provide a clear mental model for both new contributors and experienced developers working on the Avalonia-based UI layer. ## Project Structure The project follows a layered structure: - Shared core (models, services, commands, base MVVM infrastructure) - Avalonia-specific presentation layer (Views, ViewModels, converters, behaviors) - Game engine integration points (networking, packets, UO data) ```mermaid graph TB subgraph "Shared Core" VM_Base["BaseViewModel"] VM_Main["MainWindowViewModel"] VM_Agents["Agent Tab ViewModels"] VM_Autoloot["Autoloot ViewModels"] VM_Others["Other ViewModels"] ChatMgr["ChatManager"] Incoming["IncomingPacketHandlers"] end subgraph "Avalonia UI" App["App.axaml.cs"] Program["Program.cs"] MainWindow["MainWindow.axaml.cs"] Dispatcher["AvaloniaDispatcher"] UIInvoker["AvaloniaUIInvoker"] end Program --> App App --> MainWindow MainWindow --> VM_Main VM_Main --> VM_Agents VM_Main --> VM_Autoloot VM_Main --> VM_Others VM_Agents --> ChatMgr VM_Autoloot --> ChatMgr VM_Others --> ChatMgr Incoming --> ChatMgr VM_Main --> Dispatcher VM_Main --> UIInvoker ``` **Diagram sources** - [Program.cs](file://ClassicAssist.Avalonia/Program.cs) - [App.axaml.cs](file://ClassicAssist.Avalonia/App.axaml.cs) - [MainWindow.axaml.cs](file://ClassicAssist.Avalonia/Views/MainWindow.axaml.cs) - [MainWindowViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/MainWindowViewModel.cs) - [BaseViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/BaseViewModel.cs) - [AvaloniaDispatcher.cs](file://ClassicAssist.Avalonia/AvaloniaDispatcher.cs) - [AvaloniaUIInvoker.cs](file://ClassicAssist.Avalonia/AvaloniaUIInvoker.cs) - [ChatManager.cs](file://ClassicAssist.Shared/Data/Chat/ChatManager.cs) - [IncomingPacketHandlers.cs](file://ClassicAssist.Shared/UO/Network/IncomingPacketHandlers.cs) **Section sources** - [Program.cs](file://ClassicAssist.Avalonia/Program.cs) - [App.axaml.cs](file://ClassicAssist.Avalonia/App.axaml.cs) - [MainWindow.axaml.cs](file://ClassicAssist.Avalonia/Views/MainWindow.axaml.cs) - [MainWindowViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/MainWindowViewModel.cs) - [BaseViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/BaseViewModel.cs) - [AvaloniaDispatcher.cs](file://ClassicAssist.Avalonia/AvaloniaDispatcher.cs) - [AvaloniaUIInvoker.cs](file://ClassicAssist.Avalonia/AvaloniaUIInvoker.cs) - [ChatManager.cs](file://ClassicAssist.Shared/Data/Chat/ChatManager.cs) - [IncomingPacketHandlers.cs](file://ClassicAssist.Shared/UO/Network/IncomingPacketHandlers.cs) ## Core Components - BaseViewModel: Provides common MVVM infrastructure such as property change notification and command support. All ViewModels derive from this class to ensure consistent behavior. - MainWindowViewModel: Orchestrates top-level UI state and composes child ViewModels for tabs and features. - AvaloniaDispatcher and AvaloniaUIInvoker: Implement shared interfaces to marshal work onto the UI thread and present UI interactions (e.g., dialogs). - ChatManager and related models: Centralize chat-related data and events that ViewModels consume. Key responsibilities: - BaseViewModel encapsulates INotifyPropertyChanged semantics and command execution helpers. - MainWindowViewModel wires together tab ViewModels and exposes them to the view via bindings. - Dispatcher and UIInvoker abstract platform-specific threading and UI operations. **Section sources** - [BaseViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/BaseViewModel.cs) - [MainWindowViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/MainWindowViewModel.cs) - [AvaloniaDispatcher.cs](file://ClassicAssist.Avalonia/AvaloniaDispatcher.cs) - [AvaloniaUIInvoker.cs](file://ClassicAssist.Avalonia/AvaloniaUIInvoker.cs) - [IDispatcher.cs](file://ClassicAssist.Shared/IDispatcher.cs) - [IUIInvoker.cs](file://ClassicAssist.Shared/IUIInvoker.cs) - [ChatManager.cs](file://ClassicAssist.Shared/Data/Chat/ChatManager.cs) ## Architecture Overview The MVVM layers are cleanly separated: - Views (AXAML + code-behind) bind to properties and commands exposed by ViewModels. - ViewModels contain presentation logic, coordinate with models/services, and raise property change notifications. - Models represent domain data and business rules; they may publish events or expose collections that ViewModels observe. ```mermaid sequenceDiagram participant Engine as "Game Engine" participant Incoming as "IncomingPacketHandlers" participant ChatMgr as "ChatManager" participant VM as "ViewModel (e.g., ChatViewModel)" participant View as "Avalonia View" Engine->>Incoming : "Parse incoming packet" Incoming->>ChatMgr : "Update chat state / raise event" ChatMgr-->>VM : "Notify changes (events/collections)" VM->>VM : "Update bound properties" VM-->>View : "INotifyPropertyChanged triggers UI update" ``` **Diagram sources** - [IncomingPacketHandlers.cs](file://ClassicAssist.Shared/UO/Network/IncomingPacketHandlers.cs) - [ChatManager.cs](file://ClassicAssist.Shared/Data/Chat/ChatManager.cs) - [BaseViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/BaseViewModel.cs) ## Detailed Component Analysis ### BaseViewModel Hierarchy and Property Change Notification BaseViewModel centralizes: - Property change notification implementation - Common command helpers and execution context - Lifecycle hooks used by derived ViewModels Derived ViewModels inherit these capabilities and focus on feature-specific logic. ```mermaid classDiagram class BaseViewModel { +OnInitialized() +OnDisposed() +RaisePropertyChanged(propertyName) +ExecuteCommand(command) } class MainWindowViewModel { +Tabs +ActiveTab +Initialize() } class AutolootViewModel class DressTabViewModel class FriendsTabViewModel class OrganizerTabViewModel class ScavengerTabViewModel class VendorBuyTabViewModel class VendorSellTabViewModel class ClilocSelectionViewModel class CustomPropertiesViewModel class PropertySelectionViewModel class AboutControlViewModel class DebugViewModel class GeneralControlViewModel class HotkeyEntryViewModel class HotkeysTabViewModel class MacrosTabViewModel class NewProfileViewModel class ObjectInspectorViewModel class OptionsTabViewModel class RepositionableGumpViewModel class SkillsTabViewModel class DialogWindowViewModel MainWindowViewModel --|> BaseViewModel AutolootViewModel --|> BaseViewModel DressTabViewModel --|> BaseViewModel FriendsTabViewModel --|> BaseViewModel OrganizerTabViewModel --|> BaseViewModel ScavengerTabViewModel --|> BaseViewModel VendorBuyTabViewModel --|> BaseViewModel VendorSellTabViewModel --|> BaseViewModel ClilocSelectionViewModel --|> BaseViewModel CustomPropertiesViewModel --|> BaseViewModel PropertySelectionViewModel --|> BaseViewModel AboutControlViewModel --|> BaseViewModel DebugViewModel --|> BaseViewModel GeneralControlViewModel --|> BaseViewModel HotkeyEntryViewModel --|> BaseViewModel HotkeysTabViewModel --|> BaseViewModel MacrosTabViewModel --|> BaseViewModel NewProfileViewModel --|> BaseViewModel ObjectInspectorViewModel --|> BaseViewModel OptionsTabViewModel --|> BaseViewModel RepositionableGumpViewModel --|> BaseViewModel SkillsTabViewModel --|> BaseViewModel DialogWindowViewModel --|> BaseViewModel ``` **Diagram sources** - [BaseViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/BaseViewModel.cs) - [MainWindowViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/MainWindowViewModel.cs) - [AutolootViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/Agents/AutolootViewModel.cs) - [DressTabViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/Agents/DressTabViewModel.cs) - [FriendsTabViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/Agents/FriendsTabViewModel.cs) - [OrganizerTabViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/Agents/OrganizerTabViewModel.cs) - [ScavengerTabViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/Agents/ScavengerTabViewModel.cs) - [VendorBuyTabViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/Agents/VendorBuyTabViewModel.cs) - [VendorSellTabViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/Agents/VendorSellTabViewModel.cs) - [ClilocSelectionViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/Autoloot/ClilocSelectionViewModel.cs) - [CustomPropertiesViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/Autoloot/CustomPropertiesViewModel.cs) - [PropertySelectionViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/Autoloot/PropertySelectionViewModel.cs) - [AboutControlViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/AboutControlViewModel.cs) - [DebugViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/DebugViewModel.cs) - [GeneralControlViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/GeneralControlViewModel.cs) - [HotkeyEntryViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/HotkeyEntryViewModel.cs) - [HotkeysTabViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/HotkeysTabViewModel.cs) - [MacrosTabViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/MacrosTabViewModel.cs) - [NewProfileViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/NewProfileViewModel.cs) - [ObjectInspectorViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/ObjectInspectorViewModel.cs) - [OptionsTabViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/OptionsTabViewModel.cs) - [RepositionableGumpViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/RepositionableGumpViewModel.cs) - [SkillsTabViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/SkillsTabViewModel.cs) - [DialogWindowViewModel.cs](file://ClassicAssist.Avalonia/ViewModels/DialogWindowViewModel.cs) **Section sources** - [BaseViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/BaseViewModel.cs) - [MainWindowViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/MainWindowViewModel.cs) - [AutolootViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/Agents/AutolootViewModel.cs) - [DressTabViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/Agents/DressTabViewModel.cs) - [FriendsTabViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/Agents/FriendsTabViewModel.cs) - [OrganizerTabViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/Agents/OrganizerTabViewModel.cs) - [ScavengerTabViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/Agents/ScavengerTabViewModel.cs) - [VendorBuyTabViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/Agents/VendorBuyTabViewModel.cs) - [VendorSellTabViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/Agents/VendorSellTabViewModel.cs) - [ClilocSelectionViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/Autoloot/ClilocSelectionViewModel.cs) - [CustomPropertiesViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/Autoloot/CustomPropertiesViewModel.cs) - [PropertySelectionViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/Autoloot/PropertySelectionViewModel.cs) - [AboutControlViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/AboutControlViewModel.cs) - [DebugViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/DebugViewModel.cs) - [GeneralControlViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/GeneralControlViewModel.cs) - [HotkeyEntryViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/HotkeyEntryViewModel.cs) - [HotkeysTabViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/HotkeysTabViewModel.cs) - [MacrosTabViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/MacrosTabViewModel.cs) - [NewProfileViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/NewProfileViewModel.cs) - [ObjectInspectorViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/ObjectInspectorViewModel.cs) - [OptionsTabViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/OptionsTabViewModel.cs) - [RepositionableGumpViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/RepositionableGumpViewModel.cs) - [SkillsTabViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/SkillsTabViewModel.cs) - [DialogWindowViewModel.cs](file://ClassicAssist.Avalonia/ViewModels/DialogWindowViewModel.cs) ### Command Binding Mechanisms Commands in ViewModels encapsulate user actions and are bound directly in XAML. Typical patterns include: - Defining command properties on ViewModels - Executing asynchronous operations safely on background threads - Marshaling results back to the UI thread using the dispatcher abstraction ```mermaid flowchart TD Start(["User Action"]) --> Bind["XAML binds to ViewModel.Command"] Bind --> Execute["ViewModel executes command"] Execute --> Validate["Validate inputs"] Validate --> Valid{"Valid?"} Valid --> |No| ShowError["Show error via UIInvoker"] Valid --> |Yes| DoWork["Perform background work"] DoWork --> UpdateUI["Marshal result to UI thread"] UpdateUI --> End(["UI updates via properties"]) ShowError --> End ``` **Diagram sources** - [BaseViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/BaseViewModel.cs) - [AvaloniaDispatcher.cs](file://ClassicAssist.Avalonia/AvaloniaDispatcher.cs) - [AvaloniaUIInvoker.cs](file://ClassicAssist.Avalonia/AvaloniaUIInvoker.cs) - [IDispatcher.cs](file://ClassicAssist.Shared/IDispatcher.cs) - [IUIInvoker.cs](file://ClassicAssist.Shared/IUIInvoker.cs) **Section sources** - [BaseViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/BaseViewModel.cs) - [AvaloniaDispatcher.cs](file://ClassicAssist.Avalonia/AvaloniaDispatcher.cs) - [AvaloniaUIInvoker.cs](file://ClassicAssist.Avalonia/AvaloniaUIInvoker.cs) - [IDispatcher.cs](file://ClassicAssist.Shared/IDispatcher.cs) - [IUIInvoker.cs](file://ClassicAssist.Shared/IUIInvoker.cs) ### Data Flow from Game Engine to UI Incoming network packets are processed by handlers, which update shared managers (e.g., ChatManager). ViewModels subscribe to manager events or observe collections, then update their own properties. The UI reacts automatically due to property change notifications. ```mermaid sequenceDiagram participant Net as "Network Layer" participant Handler as "IncomingPacketHandlers" participant Packet as "Packets (e.g., MobileIncoming, UseObject)" participant Manager as "ChatManager" participant VM as "ViewModel" participant View as "Avalonia View" Net->>Handler : "Receive packet bytes" Handler->>Packet : "Deserialize packet" Packet->>Manager : "Apply changes / raise events" Manager-->>VM : "Notify subscribers" VM->>VM : "Update bound properties" VM-->>View : "INotifyPropertyChanged" View-->>View : "Render updated UI" ``` **Diagram sources** - [IncomingPacketHandlers.cs](file://ClassicAssist.Shared/UO/Network/IncomingPacketHandlers.cs) - [MobileIncoming.cs](file://ClassicAssist.Shared/UO/Network/Packets/MobileIncoming.cs) - [UseObject.cs](file://ClassicAssist.Shared/UO/Network/Packets/UseObject.cs) - [ChatManager.cs](file://ClassicAssist.Shared/Data/Chat/ChatManager.cs) - [BaseViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/BaseViewModel.cs) **Section sources** - [IncomingPacketHandlers.cs](file://ClassicAssist.Shared/UO/Network/IncomingPacketHandlers.cs) - [MobileIncoming.cs](file://ClassicAssist.Shared/UO/Network/Packets/MobileIncoming.cs) - [UseObject.cs](file://ClassicAssist.Shared/UO/Network/Packets/UseObject.cs) - [ChatManager.cs](file://ClassicAssist.Shared/Data/Chat/ChatManager.cs) - [BaseViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/BaseViewModel.cs) ### ViewModel Composition and Dependency Injection Composition occurs at application startup: - Program initializes the application framework - App configures services and creates root ViewModels - MainWindow sets its DataContext to the root ViewModel - Child ViewModels are composed into parent ViewModels (e.g., tabs) ```mermaid sequenceDiagram participant Main as "Program.Main" participant App as "App" participant Window as "MainWindow" participant RootVM as "MainWindowViewModel" participant ChildVM as "Child ViewModel(s)" Main->>App : "Run()" App->>App : "Configure DI / services" App->>RootVM : "Create root ViewModel" App->>Window : "Create main window" Window->>Window : "Set DataContext = RootVM" RootVM->>ChildVM : "Compose child ViewModels" Window-->>Window : "Bind to RootVM and children" ``` **Diagram sources** - [Program.cs](file://ClassicAssist.Avalonia/Program.cs) - [App.axaml.cs](file://ClassicAssist.Avalonia/App.axaml.cs) - [MainWindow.axaml.cs](file://ClassicAssist.Avalonia/Views/MainWindow.axaml.cs) - [MainWindowViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/MainWindowViewModel.cs) **Section sources** - [Program.cs](file://ClassicAssist.Avalonia/Program.cs) - [App.axaml.cs](file://ClassicAssist.Avalonia/App.axaml.cs) - [MainWindow.axaml.cs](file://ClassicAssist.Avalonia/Views/MainWindow.axaml.cs) - [MainWindowViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/MainWindowViewModel.cs) ### Lifecycle Management Typical lifecycle stages: - Initialization: ViewModels set up subscriptions, load configuration, and prepare initial state - Active: ViewModels respond to user input and engine events - Disposal: ViewModels unsubscribe from events and release resources BaseViewModel provides hooks for initialization and disposal. Ensure all long-lived subscriptions are cleaned up to prevent memory leaks. ```mermaid stateDiagram-v2 [*] --> Created Created --> Initialized : "OnInitialized" Initialized --> Active : "Bound to View" Active --> Disposing : "OnDisposed" Disposing --> [*] ``` **Diagram sources** - [BaseViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/BaseViewModel.cs) **Section sources** - [BaseViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/BaseViewModel.cs) ### Error Handling Strategies - Commands should catch exceptions and report errors via UIInvoker - Validation failures should be surfaced to users promptly - Background tasks must marshal exceptions back to the UI thread before reporting ```mermaid flowchart TD Entry(["Command Entry"]) --> TryBlock["Try block"] TryBlock --> Success{"Success?"} Success --> |Yes| Done(["Return success"]) Success --> |No| Catch["Catch exception"] Catch --> Marshal["Marshal to UI thread"] Marshal --> Report["Report via UIInvoker"] Report --> Done ``` **Diagram sources** - [BaseViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/BaseViewModel.cs) - [AvaloniaUIInvoker.cs](file://ClassicAssist.Avalonia/AvaloniaUIInvoker.cs) - [AvaloniaDispatcher.cs](file://ClassicAssist.Avalonia/AvaloniaDispatcher.cs) **Section sources** - [BaseViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/BaseViewModel.cs) - [AvaloniaUIInvoker.cs](file://ClassicAssist.Avalonia/AvaloniaUIInvoker.cs) - [AvaloniaDispatcher.cs](file://ClassicAssist.Avalonia/AvaloniaDispatcher.cs) ### Validation Patterns - Input validation within commands before executing side effects - Property setters can validate and raise property change notifications only when valid - Use centralized validation helpers where appropriate to keep ViewModels focused [No sources needed since this section provides general guidance] ### Testing Approaches for MVVM Components - Unit test ViewModels without UI dependencies by mocking IDispatcher and IUIInvoker - Assert property change notifications and command outcomes - Simulate engine events by invoking manager methods and verifying ViewModel state changes [No sources needed since this section provides general guidance] ## Dependency Analysis The following diagram shows key runtime dependencies among MVVM components and UI abstractions. ```mermaid graph LR Program["Program.cs"] --> App["App.axaml.cs"] App --> MainWindow["MainWindow.axaml.cs"] MainWindow --> MainWindowVM["MainWindowViewModel.cs"] MainWindowVM --> BaseVM["BaseViewModel.cs"] MainWindowVM --> Dispatcher["AvaloniaDispatcher.cs"] MainWindowVM --> UIInvoker["AvaloniaUIInvoker.cs"] Dispatcher --> IDisp["IDispatcher.cs"] UIInvoker --> IUI["IUIInvoker.cs"] MainWindowVM --> ChatMgr["ChatManager.cs"] Incoming["IncomingPacketHandlers.cs"] --> ChatMgr ``` **Diagram sources** - [Program.cs](file://ClassicAssist.Avalonia/Program.cs) - [App.axaml.cs](file://ClassicAssist.Avalonia/App.axaml.cs) - [MainWindow.axaml.cs](file://ClassicAssist.Avalonia/Views/MainWindow.axaml.cs) - [MainWindowViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/MainWindowViewModel.cs) - [BaseViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/BaseViewModel.cs) - [AvaloniaDispatcher.cs](file://ClassicAssist.Avalonia/AvaloniaDispatcher.cs) - [AvaloniaUIInvoker.cs](file://ClassicAssist.Avalonia/AvaloniaUIInvoker.cs) - [IDispatcher.cs](file://ClassicAssist.Shared/IDispatcher.cs) - [IUIInvoker.cs](file://ClassicAssist.Shared/IUIInvoker.cs) - [ChatManager.cs](file://ClassicAssist.Shared/Data/Chat/ChatManager.cs) - [IncomingPacketHandlers.cs](file://ClassicAssist.Shared/UO/Network/IncomingPacketHandlers.cs) **Section sources** - [Program.cs](file://ClassicAssist.Avalonia/Program.cs) - [App.axaml.cs](file://ClassicAssist.Avalonia/App.axaml.cs) - [MainWindow.axaml.cs](file://ClassicAssist.Avalonia/Views/MainWindow.axaml.cs) - [MainWindowViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/MainWindowViewModel.cs) - [BaseViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/BaseViewModel.cs) - [AvaloniaDispatcher.cs](file://ClassicAssist.Avalonia/AvaloniaDispatcher.cs) - [AvaloniaUIInvoker.cs](file://ClassicAssist.Avalonia/AvaloniaUIInvoker.cs) - [IDispatcher.cs](file://ClassicAssist.Shared/IDispatcher.cs) - [IUIInvoker.cs](file://ClassicAssist.Shared/IUIInvoker.cs) - [ChatManager.cs](file://ClassicAssist.Shared/Data/Chat/ChatManager.cs) - [IncomingPacketHandlers.cs](file://ClassicAssist.Shared/UO/Network/IncomingPacketHandlers.cs) ## Performance Considerations - Minimize heavy work on the UI thread; use background tasks and marshal results back via the dispatcher - Avoid excessive property change notifications by batching updates where possible - Use virtualization-friendly collections for large lists in views - Dispose subscriptions and timers in ViewModels to prevent memory growth over time [No sources needed since this section provides general guidance] ## Troubleshooting Guide Common issues and resolutions: - UI not updating after property changes: verify property change notifications are raised and bound correctly - Cross-thread exceptions: ensure UI updates are marshaled through the dispatcher - Memory leaks: confirm ViewModels unsubscribe from long-lived events during disposal - Errors not shown to users: check that exceptions are caught and reported via UIInvoker **Section sources** - [BaseViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/BaseViewModel.cs) - [AvaloniaDispatcher.cs](file://ClassicAssist.Avalonia/AvaloniaDispatcher.cs) - [AvaloniaUIInvoker.cs](file://ClassicAssist.Avalonia/AvaloniaUIInvoker.cs) ## Conclusion ClassicAssist.Avalonia implements a clean MVVM architecture with a strong separation of concerns. BaseViewModel standardizes property change notification and command execution, while ViewModels compose feature-specific logic and interact with shared managers. The dispatcher and UI invoker abstractions decouple UI threading and interaction details, enabling robust error handling and testability. Following the patterns outlined here will help maintain consistency, performance, and reliability across the UI layer. ## Appendices - Additional ViewModels referenced in this document: - Agents: Autoloot, Dress, Friends, Organizer, Scavenger, Vendor Buy/Sell - Autoloot: Cliloc Selection, Custom Properties, Property Selection - Other: About, Debug, General, Hotkeys, Macros, New Profile, Object Inspector, Options, Repositionable Gump, Skills - Dialog: DialogWindowViewModel [No sources needed since this section summarizes references already cited above]