first commit
This commit is contained in:
+500
@@ -0,0 +1,500 @@
|
||||
# Binding and Conversion System
|
||||
|
||||
<cite>
|
||||
**Referenced Files in This Document**
|
||||
- [StringToResourceConverter.cs](file://ClassicAssist/UI/Misc/ValueConverters/StringToResourceConverter.cs)
|
||||
- [JoinArrayValueConverter.cs](file://ClassicAssist/UI/Misc/ValueConverters/JoinArrayValueConverter.cs)
|
||||
- [LockStatusValueConverter.cs](file://ClassicAssist/UI/Misc/ValueConverters/LockStatusValueConverter.cs)
|
||||
- [DateTimeToTimeSpanValueConverter.cs](file://ClassicAssist/UI/Misc/ValueConverters/DateTimeToTimeSpanValueConverter.cs)
|
||||
- [EnumBindingSourceExtension.cs](file://ClassicAssist/UI/Misc/EnumBindingSourceExtension.cs)
|
||||
- [InverseBooleanConverter.cs](file://ClassicAssist/UI/Misc/ValueConverters/InverseBooleanConverter.cs)
|
||||
- [HexToIntValueConverter.cs](file://ClassicAssist/UI/Misc/ValueConverters/HexToIntValueConverter.cs)
|
||||
- [IntToHexStringValueConverter.cs](file://ClassicAssist/UI/Misc/ValueConverters/IntToHexStringValueConverter.cs)
|
||||
- [CellWidthValueConverter.cs](file://ClassicAssist/UI/Misc/ValueConverters/CellWidthValueConverter.cs)
|
||||
- [PriorityMultiValueConverter.cs](file://ClassicAssist/UI/Misc/ValueConverters/PriorityMultiValueConverter.cs)
|
||||
- [SetSkillLockMultiValueConverter.cs](file://ClassicAssist/UI/Misc/ValueConverters/SetSkillLockMultiValueConverter.cs)
|
||||
- [ValueConverterGroup.cs](file://ClassicAssist/UI/Misc/ValueConverters/ValueConverterGroup.cs)
|
||||
- [EnumMatchToBooleanConverter.cs](file://ClassicAssist/UI/Misc/ValueConverters/EnumMatchToBooleanConverter.cs)
|
||||
- [Avalonia StringToResourceConverter](file://ClassicAssist.Avalonia/Misc/StringToResourceConverter.cs)
|
||||
- [Avalonia JoinArrayValueConverter](file://ClassicAssist.Avalonia/Misc/JoinArrayValueConverter.cs)
|
||||
- [Avalonia LockStatusValueConverter](file://ClassicAssist.Avalonia/Misc/LockStatusValueConverter.cs)
|
||||
- [Avalonia DateTimeToTimeSpanValueConverter](file://ClassicAssist.Avalonia/Misc/DateTimeToTimeSpanValueConverter.cs)
|
||||
- [Avalonia EnumBindingSourceExtension](file://ClassicAssist.Avalonia/Misc/EnumBindingSourceExtension.cs)
|
||||
- [Avalonia InverseBooleanValueConverter](file://ClassicAssist.Avalonia/Misc/InverseBooleanValueConverter.cs)
|
||||
</cite>
|
||||
|
||||
## 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 data binding and value conversion system used across the WPF and Avalonia UI layers. It focuses on custom value converters for common scenarios such as string-to-resource lookup, array joining, lock status formatting, and date/time conversions. It also documents the enum binding extension that enables type-safe enumeration bindings and outlines best practices for creating new converters, implementing two-way bindings, and optimizing performance for large datasets.
|
||||
|
||||
## Project Structure
|
||||
The binding and conversion system is implemented in both WPF and Avalonia projects:
|
||||
- WPF converters and extensions are located under ClassicAssist/UI/Misc/ValueConverters and ClassicAssist/UI/Misc.
|
||||
- Avalonia equivalents are located under ClassicAssist.Avalonia/Misc.
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
subgraph "WPF"
|
||||
A["StringToResourceConverter.cs"]
|
||||
B["JoinArrayValueConverter.cs"]
|
||||
C["LockStatusValueConverter.cs"]
|
||||
D["DateTimeToTimeSpanValueConverter.cs"]
|
||||
E["EnumBindingSourceExtension.cs"]
|
||||
F["InverseBooleanConverter.cs"]
|
||||
G["HexToIntValueConverter.cs"]
|
||||
H["IntToHexStringValueConverter.cs"]
|
||||
I["CellWidthValueConverter.cs"]
|
||||
J["PriorityMultiValueConverter.cs"]
|
||||
K["SetSkillLockMultiValueConverter.cs"]
|
||||
L["ValueConverterGroup.cs"]
|
||||
M["EnumMatchToBooleanConverter.cs"]
|
||||
end
|
||||
subgraph "Avalonia"
|
||||
A2["StringToResourceConverter.cs"]
|
||||
B2["JoinArrayValueConverter.cs"]
|
||||
C2["LockStatusValueConverter.cs"]
|
||||
D2["DateTimeToTimeSpanValueConverter.cs"]
|
||||
E2["EnumBindingSourceExtension.cs"]
|
||||
F2["InverseBooleanValueConverter.cs"]
|
||||
end
|
||||
A --> A2
|
||||
B --> B2
|
||||
C --> C2
|
||||
D --> D2
|
||||
E --> E2
|
||||
F --> F2
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [StringToResourceConverter.cs](file://ClassicAssist/UI/Misc/ValueConverters/StringToResourceConverter.cs)
|
||||
- [JoinArrayValueConverter.cs](file://ClassicAssist/UI/Misc/ValueConverters/JoinArrayValueConverter.cs)
|
||||
- [LockStatusValueConverter.cs](file://ClassicAssist/UI/Misc/ValueConverters/LockStatusValueConverter.cs)
|
||||
- [DateTimeToTimeSpanValueConverter.cs](file://ClassicAssist/UI/Misc/ValueConverters/DateTimeToTimeSpanValueConverter.cs)
|
||||
- [EnumBindingSourceExtension.cs](file://ClassicAssist/UI/Misc/EnumBindingSourceExtension.cs)
|
||||
- [InverseBooleanConverter.cs](file://ClassicAssist/UI/Misc/ValueConverters/InverseBooleanConverter.cs)
|
||||
- [HexToIntValueConverter.cs](file://ClassicAssist/UI/Misc/ValueConverters/HexToIntValueConverter.cs)
|
||||
- [IntToHexStringValueConverter.cs](file://ClassicAssist/UI/Misc/ValueConverters/IntToHexStringValueConverter.cs)
|
||||
- [CellWidthValueConverter.cs](file://ClassicAssist/UI/Misc/ValueConverters/CellWidthValueConverter.cs)
|
||||
- [PriorityMultiValueConverter.cs](file://ClassicAssist/UI/Misc/ValueConverters/PriorityMultiValueConverter.cs)
|
||||
- [SetSkillLockMultiValueConverter.cs](file://ClassicAssist/UI/Misc/ValueConverters/SetSkillLockMultiValueConverter.cs)
|
||||
- [ValueConverterGroup.cs](file://ClassicAssist/UI/Misc/ValueConverters/ValueConverterGroup.cs)
|
||||
- [EnumMatchToBooleanConverter.cs](file://ClassicAssist/UI/Misc/ValueConverters/EnumMatchToBooleanConverter.cs)
|
||||
- [Avalonia StringToResource Converter](file://ClassicAssist.Avalonia/Misc/StringToResourceConverter.cs)
|
||||
- [Avalonia JoinArrayValueConverter](file://ClassicAssist.Avalonia/Misc/JoinArrayValueConverter.cs)
|
||||
- [Avalonia LockStatusValueConverter](file://ClassicAssist.Avalonia/Misc/LockStatusValueConverter.cs)
|
||||
- [Avalonia DateTimeToTimeSpanValueConverter](file://ClassicAssist.Avalonia/Misc/DateTimeToTimeSpanValueConverter.cs)
|
||||
- [Avalonia EnumBindingSourceExtension](file://ClassicAssist.Avalonia/Misc/EnumBindingSourceExtension.cs)
|
||||
- [Avalonia InverseBooleanValueConverter](file://ClassicAssist.Avalonia/Misc/InverseBooleanValueConverter.cs)
|
||||
|
||||
**Section sources**
|
||||
- [StringToResourceConverter.cs](file://ClassicAssist/UI/Misc/ValueConverters/StringToResourceConverter.cs)
|
||||
- [JoinArrayValueConverter.cs](file://ClassicAssist/UI/Misc/ValueConverters/JoinArrayValueConverter.cs)
|
||||
- [LockStatusValueConverter.cs](file://ClassicAssist/UI/Misc/ValueConverters/LockStatusValueConverter.cs)
|
||||
- [DateTimeToTimeSpanValueConverter.cs](file://ClassicAssist/UI/Misc/ValueConverters/DateTimeToTimeSpanValueConverter.cs)
|
||||
- [EnumBindingSourceExtension.cs](file://ClassicAssist/UI/Misc/EnumBindingSourceExtension.cs)
|
||||
- [InverseBooleanConverter.cs](file://ClassicAssist/UI/Misc/ValueConverters/InverseBooleanConverter.cs)
|
||||
- [HexToIntValueConverter.cs](file://ClassicAssist/UI/Misc/ValueConverters/HexToIntValueConverter.cs)
|
||||
- [IntToHexStringValueConverter.cs](file://ClassicAssist/UI/Misc/ValueConverters/IntToHexStringValueConverter.cs)
|
||||
- [CellWidthValueConverter.cs](file://ClassicAssist/UI/Misc/ValueConverters/CellWidthValueConverter.cs)
|
||||
- [PriorityMultiValueConverter.cs](file://ClassicAssist/UI/Misc/ValueConverters/PriorityMultiValueConverter.cs)
|
||||
- [SetSkillLockMultiValueConverter.cs](file://ClassicAssist/UI/Misc/ValueConverters/SetSkillLockMultiValueConverter.cs)
|
||||
- [ValueConverterGroup.cs](file://ClassicAssist/UI/Misc/ValueConverters/ValueConverterGroup.cs)
|
||||
- [EnumMatchToBooleanConverter.cs](file://ClassicAssist/UI/Misc/ValueConverters/EnumMatchToBooleanConverter.cs)
|
||||
- [Avalonia StringToResource Converter](file://ClassicAssist.Avalonia/Misc/StringToResourceConverter.cs)
|
||||
- [Avalonia JoinArrayValueConverter](file://ClassicAssist.Avalonia/Misc/JoinArrayValueConverter.cs)
|
||||
- [Avalonia LockStatusValueConverter](file://ClassicAssist.Avalonia/Misc/LockStatusValueConverter.cs)
|
||||
- [Avalonia DateTimeToTimeSpanValueConverter](file://ClassicAssist.Avalonia/Misc/DateTimeToTimeSpanValueConverter.cs)
|
||||
- [Avalonia EnumBindingSourceExtension](file://ClassicAssist.Avalonia/Misc/EnumBindingSourceExtension.cs)
|
||||
- [Avalonia InverseBooleanValueConverter](file://ClassicAssist.Avalonia/Misc/InverseBooleanValueConverter.cs)
|
||||
|
||||
## Core Components
|
||||
This section summarizes the key converters and extensions used throughout the application.
|
||||
|
||||
- String-to-resource lookup converter
|
||||
- Purpose: Converts a string key into a localized resource value at runtime.
|
||||
- Typical usage: Displaying user-facing strings from resource dictionaries.
|
||||
- Platform variants: WPF and Avalonia implementations exist.
|
||||
|
||||
- Array joining converter
|
||||
- Purpose: Joins an array or collection into a single string using a specified separator.
|
||||
- Typical usage: Building display strings from multiple values.
|
||||
|
||||
- Lock status formatter
|
||||
- Purpose: Formats skill or stat lock states into human-readable text or icons.
|
||||
- Typical usage: Rendering lock indicators in lists and grids.
|
||||
|
||||
- Date/time to time span converter
|
||||
- Purpose: Converts a DateTime to a TimeSpan for duration displays.
|
||||
- Typical usage: Showing elapsed or remaining time.
|
||||
|
||||
- Enum binding source extension
|
||||
- Purpose: Provides a markup-friendly way to bind to all members of an enumeration with optional descriptions.
|
||||
- Typical usage: Populating ComboBoxes or RadioButtons with enum options.
|
||||
|
||||
- Boolean helpers
|
||||
- Inverse boolean converter: Flips a boolean value for visibility toggles and similar patterns.
|
||||
- Enum match to boolean converter: Maps an enum value to a boolean based on equality or matching logic.
|
||||
|
||||
- Hexadecimal converters
|
||||
- Hex to int: Parses hex strings into integers.
|
||||
- Int to hex string: Formats integers as hexadecimal strings.
|
||||
|
||||
- Multi-value converters
|
||||
- Priority multi-value converter: Combines multiple inputs to compute a priority.
|
||||
- Set skill lock multi-value converter: Applies lock state changes based on multiple conditions.
|
||||
|
||||
- Value converter group
|
||||
- Purpose: Chains multiple converters together to perform sequential transformations.
|
||||
|
||||
**Section sources**
|
||||
- [StringToResourceConverter.cs](file://ClassicAssist/UI/Misc/ValueConverters/StringToResourceConverter.cs)
|
||||
- [JoinArrayValueConverter.cs](file://ClassicAssist/UI/Misc/ValueConverters/JoinArrayValueConverter.cs)
|
||||
- [LockStatusValueConverter.cs](file://ClassicAssist/UI/Misc/ValueConverters/LockStatusValueConverter.cs)
|
||||
- [DateTimeToTimeSpanValueConverter.cs](file://ClassicAssist/UI/Misc/ValueConverters/DateTimeToTimeSpanValueConverter.cs)
|
||||
- [EnumBindingSourceExtension.cs](file://ClassicAssist/UI/Misc/EnumBindingSourceExtension.cs)
|
||||
- [InverseBooleanConverter.cs](file://ClassicAssist/UI/Misc/ValueConverters/InverseBooleanConverter.cs)
|
||||
- [HexToIntValueConverter.cs](file://ClassicAssist/UI/Misc/ValueConverters/HexToIntValueConverter.cs)
|
||||
- [IntToHexStringValueConverter.cs](file://ClassicAssist/UI/Misc/ValueConverters/IntToHexStringValueConverter.cs)
|
||||
- [CellWidthValueConverter.cs](file://ClassicAssist/UI/Misc/ValueConverters/CellWidthValueConverter.cs)
|
||||
- [PriorityMultiValueConverter.cs](file://ClassicAssist/UI/Misc/ValueConverters/PriorityMultiValueConverter.cs)
|
||||
- [SetSkillLockMultiValueConverter.cs](file://ClassicAssist/UI/Misc/ValueConverters/SetSkillLockMultiValueConverter.cs)
|
||||
- [ValueConverterGroup.cs](file://ClassicAssist/UI/Misc/ValueConverters/ValueConverterGroup.cs)
|
||||
- [EnumMatchToBooleanConverter.cs](file://ClassicAssist/UI/Misc/ValueConverters/EnumMatchToBooleanConverter.cs)
|
||||
- [Avalonia StringToResource Converter](file://ClassicAssist.Avalonia/Misc/StringToResourceConverter.cs)
|
||||
- [Avalonia JoinArrayValueConverter](file://ClassicAssist.Avalonia/Misc/JoinArrayValueConverter.cs)
|
||||
- [Avalonia LockStatusValueConverter](file://ClassicAssist.Avalonia/Misc/LockStatusValueConverter.cs)
|
||||
- [Avalonia DateTimeToTimeSpanValueConverter](file://ClassicAssist.Avalonia/Misc/DateTimeToTimeSpanValueConverter.cs)
|
||||
- [Avalonia EnumBindingSourceExtension](file://ClassicAssist.Avalonia/Misc/EnumBindingSourceExtension.cs)
|
||||
- [Avalonia InverseBooleanValueConverter](file://ClassicAssist.Avalonia/Misc/InverseBooleanValueConverter.cs)
|
||||
|
||||
## Architecture Overview
|
||||
The binding system integrates with the UI framework’s binding engine. Converters implement platform-specific interfaces (e.g., IValueConverter in WPF or IValueConverter in Avalonia). The enum binding extension provides a convenient markup extension to enumerate types and optionally use description attributes for display names.
|
||||
|
||||
```mermaid
|
||||
classDiagram
|
||||
class StringToResourceConverter {
|
||||
+Convert(value, targetType, parameter, culture) object
|
||||
+ConvertBack(value, targetType, parameter, culture) object
|
||||
}
|
||||
class JoinArrayValueConverter {
|
||||
+Convert(values, targetType, parameter, culture) object
|
||||
+ConvertBack(value, targetType, parameter, culture) object
|
||||
}
|
||||
class LockStatusValueConverter {
|
||||
+Convert(value, targetType, parameter, culture) object
|
||||
+ConvertBack(value, targetType, parameter, culture) object
|
||||
}
|
||||
class DateTimeToTimeSpanValueConverter {
|
||||
+Convert(value, targetType, parameter, culture) object
|
||||
+ConvertBack(value, targetType, parameter, culture) object
|
||||
}
|
||||
class EnumBindingSourceExtension {
|
||||
+ProvideValue(serviceProvider) object
|
||||
}
|
||||
class InverseBooleanConverter {
|
||||
+Convert(value, targetType, parameter, culture) object
|
||||
+ConvertBack(value, targetType, parameter, culture) object
|
||||
}
|
||||
class HexToIntValueConverter {
|
||||
+Convert(value, targetType, parameter, culture) object
|
||||
+ConvertBack(value, targetType, parameter, culture) object
|
||||
}
|
||||
class IntToHexStringValueConverter {
|
||||
+Convert(value, targetType, parameter, culture) object
|
||||
+ConvertBack(value, targetType, parameter, culture) object
|
||||
}
|
||||
class CellWidthValueConverter {
|
||||
+Convert(value, targetType, parameter, culture) object
|
||||
+ConvertBack(value, targetType, parameter, culture) object
|
||||
}
|
||||
class PriorityMultiValueConverter {
|
||||
+Convert(values, targetType, parameter, culture) object
|
||||
+ConvertBack(value, targetType, parameters, culture) object
|
||||
}
|
||||
class SetSkillLockMultiValueConverter {
|
||||
+Convert(values, targetType, parameter, culture) object
|
||||
+ConvertBack(value, targetType, parameters, culture) object
|
||||
}
|
||||
class ValueConverterGroup {
|
||||
+Convert(value, targetType, parameter, culture) object
|
||||
+ConvertBack(value, targetType, parameter, culture) object
|
||||
}
|
||||
class EnumMatchToBooleanConverter {
|
||||
+Convert(value, targetType, parameter, culture) object
|
||||
+ConvertBack(value, targetType, parameter, culture) object
|
||||
}
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [StringToResourceConverter.cs](file://ClassicAssist/UI/Misc/ValueConverters/StringToResourceConverter.cs)
|
||||
- [JoinArrayValueConverter.cs](file://ClassicAssist/UI/Misc/ValueConverters/JoinArrayValueConverter.cs)
|
||||
- [LockStatusValueConverter.cs](file://ClassicAssist/UI/Misc/ValueConverters/LockStatusValueConverter.cs)
|
||||
- [DateTimeToTimeSpanValueConverter.cs](file://ClassicAssist/UI/Misc/ValueConverters/DateTimeToTimeSpanValueConverter.cs)
|
||||
- [EnumBindingSourceExtension.cs](file://ClassicAssist/UI/Misc/EnumBindingSourceExtension.cs)
|
||||
- [InverseBooleanConverter.cs](file://ClassicAssist/UI/Misc/ValueConverters/InverseBooleanConverter.cs)
|
||||
- [HexToIntValueConverter.cs](file://ClassicAssist/UI/Misc/ValueConverters/HexToIntValueConverter.cs)
|
||||
- [IntToHexStringValueConverter.cs](file://ClassicAssist/UI/Misc/ValueConverters/IntToHexStringValueConverter.cs)
|
||||
- [CellWidthValueConverter.cs](file://ClassicAssist/UI/Misc/ValueConverters/CellWidthValueConverter.cs)
|
||||
- [PriorityMultiValueConverter.cs](file://ClassicAssist/UI/Misc/ValueConverters/PriorityMultiValueConverter.cs)
|
||||
- [SetSkillLockMultiValueConverter.cs](file://ClassicAssist/UI/Misc/ValueConverters/SetSkillLockMultiValueConverter.cs)
|
||||
- [ValueConverterGroup.cs](file://ClassicAssist/UI/Misc/ValueConverters/ValueConverterGroup.cs)
|
||||
- [EnumMatchToBooleanConverter.cs](file://ClassicAssist/UI/Misc/ValueConverters/EnumMatchToBooleanConverter.cs)
|
||||
|
||||
## Detailed Component Analysis
|
||||
|
||||
### String-to-Resource Lookup Converter
|
||||
- Purpose: Resolves a string key to a localized resource value.
|
||||
- Behavior:
|
||||
- Accepts a key string and returns the corresponding resource entry.
|
||||
- Supports culture-aware resolution when applicable.
|
||||
- Two-way binding:
|
||||
- Typically one-way; ConvertBack may return the original key if needed by specific scenarios.
|
||||
- Usage example path:
|
||||
- [StringToResourceConverter.cs](file://ClassicAssist/UI/Misc/ValueConverters/StringToResourceConverter.cs)
|
||||
- [Avalonia StringToResource Converter](file://ClassicAssist.Avalonia/Misc/StringToResourceConverter.cs)
|
||||
|
||||
**Section sources**
|
||||
- [StringToResourceConverter.cs](file://ClassicAssist/UI/Misc/ValueConverters/StringToResourceConverter.cs)
|
||||
- [Avalonia StringToResource Converter](file://ClassicAssist.Avalonia/Misc/StringToResourceConverter.cs)
|
||||
|
||||
### Array Joining Converter
|
||||
- Purpose: Concatenates elements of an array or collection into a single string using a configurable separator.
|
||||
- Behavior:
|
||||
- Ignores null entries unless configured otherwise.
|
||||
- Supports trimming and case normalization via parameters.
|
||||
- Two-way binding:
|
||||
- Can split a joined string back into an array if required by the model.
|
||||
- Usage example path:
|
||||
- [JoinArrayValueConverter.cs](file://ClassicAssist/UI/Misc/ValueConverters/JoinArrayValueConverter.cs)
|
||||
- [Avalonia JoinArrayValueConverter](file://ClassicAssist.Avalonia/Misc/JoinArrayValueConverter.cs)
|
||||
|
||||
**Section sources**
|
||||
- [JoinArrayValueConverter.cs](file://ClassicAssist/UI/Misc/ValueConverters/JoinArrayValueConverter.cs)
|
||||
- [Avalonia JoinArrayValueConverter](file://ClassicAssist.Avalonia/Misc/JoinArrayValueConverter.cs)
|
||||
|
||||
### Lock Status Formatter
|
||||
- Purpose: Translates internal lock flags into readable labels or visual indicators.
|
||||
- Behavior:
|
||||
- Maps lock enums to display strings or icon resources.
|
||||
- May combine multiple lock states into a composite label.
|
||||
- Two-way binding:
|
||||
- ConvertBack can map a selected label/icon back to the appropriate lock flag.
|
||||
- Usage example path:
|
||||
- [LockStatusValueConverter.cs](file://ClassicAssist/UI/Misc/ValueConverters/LockStatusValueConverter.cs)
|
||||
- [Avalonia LockStatusValueConverter](file://ClassicAssist.Avalonia/Misc/LockStatusValueConverter.cs)
|
||||
|
||||
**Section sources**
|
||||
- [LockStatusValueConverter.cs](file://ClassicAssist/UI/Misc/ValueConverters/LockStatusValueConverter.cs)
|
||||
- [Avalonia LockStatusValueConverter](file://ClassicAssist.Avalonia/Misc/LockStatusValueConverter.cs)
|
||||
|
||||
### Date/Time to Time Span Converter
|
||||
- Purpose: Converts a DateTime to a TimeSpan representing duration relative to now or another reference point.
|
||||
- Behavior:
|
||||
- Computes absolute or relative durations.
|
||||
- Supports formatting parameters for display.
|
||||
- Two-way binding:
|
||||
- Usually one-way; ConvertBack may not be meaningful unless editing raw TimeSpan values.
|
||||
- Usage example path:
|
||||
- [DateTimeToTimeSpanValueConverter.cs](file://ClassicAssist/UI/Misc/ValueConverters/DateTimeToTimeSpanValueConverter.cs)
|
||||
- [Avalonia DateTimeToTimeSpanValueConverter](file://ClassicAssist.Avalonia/Misc/DateTimeToTimeSpanValueConverter.cs)
|
||||
|
||||
**Section sources**
|
||||
- [DateTimeToTimeSpanValueConverter.cs](file://ClassicAssist/UI/Misc/ValueConverters/DateTimeToTimeSpanValueConverter.cs)
|
||||
- [Avalonia DateTimeToTimeSpanValueConverter](file://ClassicAssist.Avalonia/Misc/DateTimeToTimeSpanValueConverter.cs)
|
||||
|
||||
### Enum Binding Source Extension
|
||||
- Purpose: Exposes all members of an enumeration to XAML/Axaml bindings, optionally using description attributes for friendly names.
|
||||
- Behavior:
|
||||
- Enumerates type members and builds a list suitable for ItemsSource binding.
|
||||
- Supports filtering and ordering parameters.
|
||||
- Two-way binding:
|
||||
- Works with SelectedItem bindings to update the underlying enum property.
|
||||
- Usage example path:
|
||||
- [EnumBindingSourceExtension.cs](file://ClassicAssist/UI/Misc/EnumBindingSourceExtension.cs)
|
||||
- [Avalonia EnumBindingSourceExtension](file://ClassicAssist.Avalonia/Misc/EnumBindingSourceExtension.cs)
|
||||
|
||||
**Section sources**
|
||||
- [EnumBindingSourceExtension.cs](file://ClassicAssist/UI/Misc/EnumBindingSourceExtension.cs)
|
||||
- [Avalonia EnumBindingSourceExtension](file://ClassicAssist.Avalonia/Misc/EnumBindingSourceExtension.cs)
|
||||
|
||||
### Boolean Helpers and Enum Matching
|
||||
- Inverse boolean converter:
|
||||
- Flips boolean values for visibility toggles and conditional rendering.
|
||||
- Usage example path: [InverseBooleanConverter.cs](file://ClassicAssist/UI/Misc/ValueConverters/InverseBooleanConverter.cs), [Avalonia InverseBooleanValueConverter](file://ClassicAssist.Avalonia/Misc/InverseBooleanValueConverter.cs)
|
||||
- Enum match to boolean converter:
|
||||
- Compares an enum value against expected values and returns a boolean.
|
||||
- Usage example path: [EnumMatchToBooleanConverter.cs](file://ClassicAssist/UI/Misc/ValueConverters/EnumMatchToBooleanConverter.cs)
|
||||
|
||||
**Section sources**
|
||||
- [InverseBooleanConverter.cs](file://ClassicAssist/UI/Misc/ValueConverters/InverseBooleanConverter.cs)
|
||||
- [Avalonia InverseBooleanValueConverter](file://ClassicAssist.Avalonia/Misc/InverseBooleanValueConverter.cs)
|
||||
- [EnumMatchToBooleanConverter.cs](file://ClassicAssist/UI/Misc/ValueConverters/EnumMatchToBooleanConverter.cs)
|
||||
|
||||
### Hexadecimal Converters
|
||||
- Hex to int converter:
|
||||
- Parses hexadecimal strings into integer values.
|
||||
- Usage example path: [HexToIntValueConverter.cs](file://ClassicAssist/UI/Misc/ValueConverters/HexToIntValueConverter.cs)
|
||||
- Int to hex string converter:
|
||||
- Formats integers as uppercase/lowercase hex strings.
|
||||
- Usage example path: [IntToHexStringValueConverter.cs](file://ClassicAssist/UI/Misc/ValueConverters/IntToHexStringValueConverter.cs)
|
||||
|
||||
**Section sources**
|
||||
- [HexToIntValueConverter.cs](file://ClassicAssist/UI/Misc/ValueConverters/HexToIntValueConverter.cs)
|
||||
- [IntToHexStringValueConverter.cs](file://ClassicAssist/UI/Misc/ValueConverters/IntToHexStringValueConverter.cs)
|
||||
|
||||
### Multi-Value Converters
|
||||
- Priority multi-value converter:
|
||||
- Aggregates multiple inputs to compute a numeric priority.
|
||||
- Usage example path: [PriorityMultiValueConverter.cs](file://ClassicAssist/UI/Misc/ValueConverters/PriorityMultiValueConverter.cs)
|
||||
- Set skill lock multi-value converter:
|
||||
- Determines lock state updates based on multiple conditions.
|
||||
- Usage example path: [SetSkillLockMultiValueConverter.cs](file://ClassicAssist/UI/Misc/ValueConverters/SetSkillLockMultiValueConverter.cs)
|
||||
|
||||
**Section sources**
|
||||
- [PriorityMultiValueConverter.cs](file://ClassicAssist/UI/Misc/ValueConverters/PriorityMultiValueConverter.cs)
|
||||
- [SetSkillLockMultiValueConverter.cs](file://ClassicAssist/UI/Misc/ValueConverters/SetSkillLockMultiValueConverter.cs)
|
||||
|
||||
### Value Converter Group
|
||||
- Purpose: Chains multiple converters sequentially to transform values step-by-step.
|
||||
- Behavior:
|
||||
- Applies each converter in order, passing output to the next.
|
||||
- Useful for composing complex transformations without extra code-behind.
|
||||
- Usage example path: [ValueConverterGroup.cs](file://ClassicAssist/UI/Misc/ValueConverters/ValueConverterGroup.cs)
|
||||
|
||||
**Section sources**
|
||||
- [ValueConverterGroup.cs](file://ClassicAssist/UI/Misc/ValueConverters/ValueConverterGroup.cs)
|
||||
|
||||
### Additional Converters
|
||||
- Cell width converter:
|
||||
- Adjusts column widths based on content metrics.
|
||||
- Usage example path: [CellWidthValueConverter.cs](file://ClassicAssist/UI/Misc/ValueConverters/CellWidthValueConverter.cs)
|
||||
|
||||
**Section sources**
|
||||
- [CellWidthValueConverter.cs](file://ClassicAssist/UI/Misc/ValueConverters/CellWidthValueConverter.cs)
|
||||
|
||||
## Dependency Analysis
|
||||
The converters are loosely coupled and primarily depend on the UI framework’s binding interfaces. Some converters rely on shared utilities or resource managers for localization and formatting.
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
SR["StringToResourceConverter"] --> RES["Resource Manager"]
|
||||
JA["JoinArrayValueConverter"] --> STR["String Utilities"]
|
||||
LS["LockStatusValueConverter"] --> ENUMS["Lock Enums"]
|
||||
DTTS["DateTimeToTimeSpanValueConverter"] --> TIME["Time Utilities"]
|
||||
EBS["EnumBindingSourceExtension"] --> REF["Reflection API"]
|
||||
INV["InverseBooleanConverter"] --> BOOL["Boolean Logic"]
|
||||
HEXI["HexToIntValueConverter"] --> PARSE["Parsing Utilities"]
|
||||
HEXO["IntToHexStringValueConverter"] --> FORMAT["Formatting Utilities"]
|
||||
CW["CellWidthValueConverter"] --> MEASURE["Measure Utilities"]
|
||||
PMV["PriorityMultiValueConverter"] --> AGG["Aggregation Logic"]
|
||||
SSL["SetSkillLockMultiValueConverter"] --> LOCK["Lock State Logic"]
|
||||
VCG["ValueConverterGroup"] --> CHAIN["Converter Chain"]
|
||||
```
|
||||
|
||||
[No diagram sources since this diagram shows conceptual dependencies rather than direct file mappings]
|
||||
|
||||
**Section sources**
|
||||
- [StringToResourceConverter.cs](file://ClassicAssist/UI/Misc/ValueConverters/StringToResourceConverter.cs)
|
||||
- [JoinArrayValueConverter.cs](file://ClassicAssist/UI/Misc/ValueConverters/JoinArrayValueConverter.cs)
|
||||
- [LockStatusValueConverter.cs](file://ClassicAssist/UI/Misc/ValueConverters/LockStatusValueConverter.cs)
|
||||
- [DateTimeToTimeSpanValueConverter.cs](file://ClassicAssist/UI/Misc/ValueConverters/DateTimeToTimeSpanValueConverter.cs)
|
||||
- [EnumBindingSourceExtension.cs](file://ClassicAssist/UI/Misc/EnumBindingSourceExtension.cs)
|
||||
- [InverseBooleanConverter.cs](file://ClassicAssist/UI/Misc/ValueConverters/InverseBooleanConverter.cs)
|
||||
- [HexToIntValueConverter.cs](file://ClassicAssist/UI/Misc/ValueConverters/HexToIntValueConverter.cs)
|
||||
- [IntToHexStringValueConverter.cs](file://ClassicAssist/UI/Misc/ValueConverters/IntToHexStringValueConverter.cs)
|
||||
- [CellWidthValueConverter.cs](file://ClassicAssist/UI/Misc/ValueConverters/CellWidthValueConverter.cs)
|
||||
- [PriorityMultiValueConverter.cs](file://ClassicAssist/UI/Misc/ValueConverters/PriorityMultiValueConverter.cs)
|
||||
- [SetSkillLockMultiValueConverter.cs](file://ClassicAssist/UI/Misc/ValueConverters/SetSkillLockMultiValueConverter.cs)
|
||||
- [ValueConverterGroup.cs](file://ClassicAssist/UI/Misc/ValueConverters/ValueConverterGroup.cs)
|
||||
|
||||
## Performance Considerations
|
||||
- Prefer one-way bindings where possible to reduce overhead.
|
||||
- Cache expensive computations inside converters and invalidate only when inputs change.
|
||||
- Use ValueConverterGroup judiciously; deep chains can degrade performance on large collections.
|
||||
- For large datasets, consider virtualization and avoid heavy formatting in item templates.
|
||||
- Reuse converter instances where feasible to minimize allocations.
|
||||
- Avoid synchronous blocking operations within Convert methods; offload work to background tasks and update bound properties asynchronously.
|
||||
|
||||
[No sources needed since this section provides general guidance]
|
||||
|
||||
## Troubleshooting Guide
|
||||
Common issues and resolutions:
|
||||
- Resource lookup failures:
|
||||
- Ensure keys exist in resource dictionaries and cultures are correctly set.
|
||||
- Verify the converter receives the expected key type.
|
||||
- Null or empty arrays:
|
||||
- Handle nulls explicitly in JoinArrayValueConverter to prevent exceptions.
|
||||
- Incorrect lock status display:
|
||||
- Confirm enum values align with converter expectations and mapping tables.
|
||||
- Date/time edge cases:
|
||||
- Guard against null dates and invalid ranges in DateTimeToTimeSpanValueConverter.
|
||||
- Enum binding population:
|
||||
- Check that the target enum type is correct and description attributes are applied if used.
|
||||
- Two-way binding inconsistencies:
|
||||
- Implement ConvertBack consistently and ensure it returns compatible types.
|
||||
|
||||
**Section sources**
|
||||
- [StringToResourceConverter.cs](file://ClassicAssist/UI/Misc/ValueConverters/StringToResourceConverter.cs)
|
||||
- [JoinArrayValueConverter.cs](file://ClassicAssist/UI/Misc/ValueConverters/JoinArrayValueConverter.cs)
|
||||
- [LockStatusValueConverter.cs](file://ClassicAssist/UI/Misc/ValueConverters/LockStatusValueConverter.cs)
|
||||
- [DateTimeToTimeSpanValueConverter.cs](file://ClassicAssist/UI/Misc/ValueConverters/DateTimeToTimeSpanValueConverter.cs)
|
||||
- [EnumBindingSourceExtension.cs](file://ClassicAssist/UI/Misc/EnumBindingSourceExtension.cs)
|
||||
|
||||
## Conclusion
|
||||
The binding and conversion system provides a robust foundation for transforming and presenting data across WPF and Avalonia UIs. By leveraging specialized converters and the enum binding extension, developers can create expressive, maintainable interfaces. Following the guidelines for two-way bindings and performance optimization ensures smooth experiences even with large datasets.
|
||||
|
||||
[No sources needed since this section summarizes without analyzing specific files]
|
||||
|
||||
## Appendices
|
||||
|
||||
### Creating New Converters
|
||||
- Steps:
|
||||
- Implement the platform’s IValueConverter interface.
|
||||
- Provide Convert and ConvertBack methods with clear parameter handling.
|
||||
- Add unit tests for typical and edge-case inputs.
|
||||
- Register the converter in resource dictionaries or markup extensions as needed.
|
||||
- Example paths:
|
||||
- [InverseBooleanConverter.cs](file://ClassicAssist/UI/Misc/ValueConverters/InverseBooleanConverter.cs)
|
||||
- [HexToIntValueConverter.cs](file://ClassicAssist/UI/Misc/ValueConverters/HexToIntValueConverter.cs)
|
||||
- [IntToHexStringValueConverter.cs](file://ClassicAssist/UI/Misc/ValueConverters/IntToHexStringValueConverter.cs)
|
||||
|
||||
**Section sources**
|
||||
- [InverseBooleanConverter.cs](file://ClassicAssist/UI/Misc/ValueConverters/InverseBooleanConverter.cs)
|
||||
- [HexToIntValueConverter.cs](file://ClassicAssist/UI/Misc/ValueConverters/HexToIntValueConverter.cs)
|
||||
- [IntToHexStringValueConverter.cs](file://ClassicAssist/UI/Misc/ValueConverters/IntToHexStringValueConverter.cs)
|
||||
|
||||
### Implementing Two-Way Bindings
|
||||
- Guidelines:
|
||||
- Ensure ConvertBack returns a value compatible with the source property type.
|
||||
- Validate inputs and handle invalid formats gracefully.
|
||||
- Keep ConvertBack fast and side-effect free.
|
||||
- Example paths:
|
||||
- [JoinArrayValueConverter.cs](file://ClassicAssist/UI/Misc/ValueConverters/JoinArrayValueConverter.cs)
|
||||
- [LockStatusValueConverter.cs](file://ClassicAssist/UI/Misc/ValueConverters/LockStatusValueConverter.cs)
|
||||
- [ValueConverterGroup.cs](file://ClassicAssist/UI/Misc/ValueConverters/ValueConverterGroup.cs)
|
||||
|
||||
**Section sources**
|
||||
- [JoinArrayValueConverter.cs](file://ClassicAssist/UI/Misc/ValueConverters/JoinArrayValueConverter.cs)
|
||||
- [LockStatusValueConverter.cs](file://ClassicAssist/UI/Misc/ValueConverters/LockStatusValueConverter.cs)
|
||||
- [ValueConverterGroup.cs](file://ClassicAssist/UI/Misc/ValueConverters/ValueConverterGroup.cs)
|
||||
|
||||
### Optimizing Binding Performance for Large Datasets
|
||||
- Recommendations:
|
||||
- Use one-way bindings where possible.
|
||||
- Minimize heavy computations in Convert; cache results and invalidate selectively.
|
||||
- Prefer lightweight formatting and avoid synchronous IO.
|
||||
- Leverage virtualization and paging for large collections.
|
||||
- Profile binding churn and refactor hot paths.
|
||||
|
||||
[No sources needed since this section provides general guidance]
|
||||
+439
@@ -0,0 +1,439 @@
|
||||
# Cross-Platform Abstraction Layer
|
||||
|
||||
<cite>
|
||||
**Referenced Files in This Document**
|
||||
- [IDispatcher.cs](file://ClassicAssist.Shared/IDispatcher.cs)
|
||||
- [IUIInvoker.cs](file://ClassicAssist.Shared/IUIInvoker.cs)
|
||||
- [WPFDispatcher.cs](file://ClassicAssist/WPFDispatcher.cs)
|
||||
- [WPFUIInvoker.cs](file://ClassicAssist/WPFUIInvoker.cs)
|
||||
- [AvaloniaDispatcher.cs](file://ClassicAssist.Avalonia/AvaloniaDispatcher.cs)
|
||||
- [AvaloniaUIInvoker.cs](file://ClassicAssist.Avalonia/AvaloniaUIInvoker.cs)
|
||||
- [Engine.cs (Shared)](file://ClassicAssist.Shared/Engine.cs)
|
||||
- [Engine.cs (WPF)](file://ClassicAssist/Engine.cs)
|
||||
- [Engine.cs (Avalonia)](file://ClassicAssist.Avalonia/Engine.cs)
|
||||
- [Program.cs (Avalonia)](file://ClassicAssist.Avalonia/Program.cs)
|
||||
- [App.axaml.cs (Avalonia)](file://ClassicAssist.Avalonia/App.axaml.cs)
|
||||
- [MessageBoxProvider.cs](file://ClassicAssist.Shared/Misc/MessageBoxProvider.cs)
|
||||
- [AvaloniaMessageBoxProvider.cs](file://ClassicAssist.Avalonia/Misc/AvaloniaMessageBoxProvider.cs)
|
||||
- [WPFMessageBoxProvider.cs](file://ClassicAssist/UI/Misc/WPFMessageBoxProvider.cs)
|
||||
- [NativeMethods.cs](file://ClassicAssist.Shared/Misc/NativeMethods.cs)
|
||||
- [PlatformDetector.cs](file://ClassicAssist.Shared/Engine/PlatformDetector.cs)
|
||||
</cite>
|
||||
|
||||
## 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 cross-platform abstraction layer that enables ClassicAssist to run on Windows, Linux, and macOS. It focuses on:
|
||||
- The dispatcher interface for thread marshaling
|
||||
- UI invoker patterns for cross-thread UI updates
|
||||
- Platform-specific optimizations
|
||||
- Handling differences in window management, file system access, and native interop calls across platforms
|
||||
- Migration strategies and platform-specific considerations
|
||||
|
||||
The goal is to provide a clear understanding of how shared logic remains platform-agnostic while delegating platform-specific behavior through well-defined interfaces.
|
||||
|
||||
## Project Structure
|
||||
At a high level, the project separates shared logic from platform-specific implementations:
|
||||
- Shared abstractions and core engine live in ClassicAssist.Shared
|
||||
- WPF-based implementation lives in ClassicAssist
|
||||
- Avalonia-based implementation lives in ClassicAssist.Avalonia
|
||||
- Launcher and updater are separate applications with their own UI frameworks
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
subgraph "Shared"
|
||||
S_ID["IDispatcher.cs"]
|
||||
S_UI["IUIInvoker.cs"]
|
||||
S_Eng["Engine.cs (Shared)"]
|
||||
S_MsgProv["MessageBoxProvider.cs"]
|
||||
S_Native["NativeMethods.cs"]
|
||||
S_Platform["PlatformDetector.cs"]
|
||||
end
|
||||
subgraph "WPF"
|
||||
W_Dispatch["WPFDispatcher.cs"]
|
||||
W_UIInv["WPFUIInvoker.cs"]
|
||||
W_Eng["Engine.cs (WPF)"]
|
||||
W_MsgProv["WPFMessageBoxProvider.cs"]
|
||||
end
|
||||
subgraph "Avalonia"
|
||||
A_Dispatch["AvaloniaDispatcher.cs"]
|
||||
A_UIInv["AvaloniaUIInvoker.cs"]
|
||||
A_Eng["Engine.cs (Avalonia)"]
|
||||
A_Program["Program.cs (Avalonia)"]
|
||||
A_App["App.axaml.cs (Avalonia)"]
|
||||
A_MsgProv["AvaloniaMessageBoxProvider.cs"]
|
||||
end
|
||||
S_ID --> W_Dispatch
|
||||
S_ID --> A_Dispatch
|
||||
S_UI --> W_UIInv
|
||||
S_UI --> A_UIInv
|
||||
S_Eng --> W_Eng
|
||||
S_Eng --> A_Eng
|
||||
S_MsgProv --> W_MsgProv
|
||||
S_MsgProv --> A_MsgProv
|
||||
S_Native --> W_Eng
|
||||
S_Native --> A_Eng
|
||||
S_Platform --> W_Eng
|
||||
S_Platform --> A_Eng
|
||||
A_Program --> A_Eng
|
||||
A_App --> A_Eng
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [IDispatcher.cs](file://ClassicAssist.Shared/IDispatcher.cs)
|
||||
- [IUIInvoker.cs](file://ClassicAssist.Shared/IUIInvoker.cs)
|
||||
- [WPFDispatcher.cs](file://ClassicAssist/WPFDispatcher.cs)
|
||||
- [WPFUIInvoker.cs](file://ClassicAssist/WPFUIInvoker.cs)
|
||||
- [AvaloniaDispatcher.cs](file://ClassicAssist.Avalonia/AvaloniaDispatcher.cs)
|
||||
- [AvaloniaUIInvoker.cs](file://ClassicAssist.Avalonia/AvaloniaUIInvoker.cs)
|
||||
- [Engine.cs (Shared)](file://ClassicAssist.Shared/Engine.cs)
|
||||
- [Engine.cs (WPF)](file://ClassicAssist/Engine.cs)
|
||||
- [Engine.cs (Avalonia)](file://ClassicAssist.Avalonia/Engine.cs)
|
||||
- [Program.cs (Avalonia)](file://ClassicAssist.Avalonia/Program.cs)
|
||||
- [App.axaml.cs (Avalonia)](file://ClassicAssist.Avalonia/App.axaml.cs)
|
||||
- [MessageBoxProvider.cs](file://ClassicAssist.Shared/Misc/MessageBoxProvider.cs)
|
||||
- [WPFMessageBoxProvider.cs](file://ClassicAssist/UI/Misc/WPFMessageBoxProvider.cs)
|
||||
- [AvaloniaMessageBoxProvider.cs](file://ClassicAssist.Avalonia/Misc/AvaloniaMessageBoxProvider.cs)
|
||||
- [NativeMethods.cs](file://ClassicAssist.Shared/Misc/NativeMethods.cs)
|
||||
- [PlatformDetector.cs](file://ClassicAssist.Shared/Engine/PlatformDetector.cs)
|
||||
|
||||
**Section sources**
|
||||
- [IDispatcher.cs](file://ClassicAssist.Shared/IDispatcher.cs)
|
||||
- [IUIInvoker.cs](file://ClassicAssist.Shared/IUIInvoker.cs)
|
||||
- [Engine.cs (Shared)](file://ClassicAssist.Shared/Engine.cs)
|
||||
- [Engine.cs (WPF)](file://ClassicAssist/Engine.cs)
|
||||
- [Engine.cs (Avalonia)](file://ClassicAssist.Avalonia/Engine.cs)
|
||||
- [Program.cs (Avalonia)](file://ClassicAssist.Avalonia/Program.cs)
|
||||
- [App.axaml.cs (Avalonia)](file://ClassicAssist.Avalonia/App.axaml.cs)
|
||||
- [MessageBoxProvider.cs](file://ClassicAssist.Shared/Misc/MessageBoxProvider.cs)
|
||||
- [WPFMessageBoxProvider.cs](file://ClassicAssist/UI/Misc/WPFMessageBoxProvider.cs)
|
||||
- [AvaloniaMessageBoxProvider.cs](file://ClassicAssist.Avalonia/Misc/AvaloniaMessageBoxProvider.cs)
|
||||
- [NativeMethods.cs](file://ClassicAssist.Shared/Misc/NativeMethods.cs)
|
||||
- [PlatformDetector.cs](file://ClassicAssist.Shared/Engine/PlatformDetector.cs)
|
||||
|
||||
## Core Components
|
||||
The cross-platform abstraction centers around two primary interfaces:
|
||||
- Dispatcher interface for thread marshaling
|
||||
- UI Invoker interface for cross-thread UI updates
|
||||
|
||||
These interfaces are implemented per platform (WPF and Avalonia), allowing shared code to remain framework-agnostic.
|
||||
|
||||
Key responsibilities:
|
||||
- Marshal work onto the UI thread safely
|
||||
- Provide a consistent API for invoking UI operations from background threads
|
||||
- Abstract away platform-specific threading models and message pumps
|
||||
|
||||
**Section sources**
|
||||
- [IDispatcher.cs](file://ClassicAssist.Shared/IDispatcher.cs)
|
||||
- [IUIInvoker.cs](file://ClassicAssist.Shared/IUIInvoker.cs)
|
||||
- [WPFDispatcher.cs](file://ClassicAssist/WPFDispatcher.cs)
|
||||
- [WPFUIInvoker.cs](file://ClassicAssist/WPFUIInvoker.cs)
|
||||
- [AvaloniaDispatcher.cs](file://ClassicAssist.Avalonia/AvaloniaDispatcher.cs)
|
||||
- [AvaloniaUIInvoker.cs](file://ClassicAssist.Avalonia/AvaloniaUIInvoker.cs)
|
||||
|
||||
## Architecture Overview
|
||||
The architecture uses dependency injection or static initialization to select the correct platform implementation at runtime. Shared components depend only on interfaces, not concrete classes.
|
||||
|
||||
```mermaid
|
||||
classDiagram
|
||||
class IDispatcher {
|
||||
+ "Dispatch(action)"
|
||||
+ "BeginInvoke(action)"
|
||||
+ "Invoke(action)"
|
||||
}
|
||||
class IUIInvoker {
|
||||
+ "InvokeOnUiThread(action)"
|
||||
+ "IsOnUIThread()"
|
||||
}
|
||||
class WPFDispatcher
|
||||
class WPFUIInvoker
|
||||
class AvaloniaDispatcher
|
||||
class AvaloniaUIInvoker
|
||||
IDispatcher <|.. WPFDispatcher : "implements"
|
||||
IDispatcher <|.. AvaloniaDispatcher : "implements"
|
||||
IUIInvoker <|.. WPFUIInvoker : "implements"
|
||||
IUIInvoker <|.. AvaloniaUIInvoker : "implements"
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [IDispatcher.cs](file://ClassicAssist.Shared/IDispatcher.cs)
|
||||
- [IUIInvoker.cs](file://ClassicAssist.Shared/IUIInvoker.cs)
|
||||
- [WPFDispatcher.cs](file://ClassicAssist/WPFDispatcher.cs)
|
||||
- [WPFUIInvoker.cs](file://ClassicAssist/WPFUIInvoker.cs)
|
||||
- [AvaloniaDispatcher.cs](file://ClassicAssist.Avalonia/AvaloniaDispatcher.cs)
|
||||
- [AvaloniaUIInvoker.cs](file://ClassicAssist.Avalonia/AvaloniaUIInvoker.cs)
|
||||
|
||||
## Detailed Component Analysis
|
||||
|
||||
### Dispatcher Interface and Implementations
|
||||
Purpose:
|
||||
- Provide a unified way to schedule work on the UI thread regardless of the underlying UI framework
|
||||
- Support both synchronous and asynchronous dispatching patterns
|
||||
|
||||
Implementation highlights:
|
||||
- WPFDispatcher bridges to WPF’s dispatcher model
|
||||
- AvaloniaDispatcher bridges to Avalonia’s dispatcher model
|
||||
- Both expose methods to enqueue actions and ensure they execute on the UI thread
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant BG as "Background Thread"
|
||||
participant Shared as "Shared Code"
|
||||
participant Disp as "IDispatcher"
|
||||
participant Impl as "WPFDispatcher / AvaloniaDispatcher"
|
||||
participant UIThread as "UI Thread"
|
||||
BG->>Shared : "Perform long-running task"
|
||||
Shared->>Disp : "BeginInvoke(action)"
|
||||
Disp->>Impl : "Forward call"
|
||||
Impl->>UIThread : "Marshal action"
|
||||
UIThread-->>Impl : "Execute action"
|
||||
Impl-->>Shared : "Return"
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [IDispatcher.cs](file://ClassicAssist.Shared/IDispatcher.cs)
|
||||
- [WPFDispatcher.cs](file://ClassicAssist/WPFDispatcher.cs)
|
||||
- [AvaloniaDispatcher.cs](file://ClassicAssist.Avalonia/AvaloniaDispatcher.cs)
|
||||
|
||||
**Section sources**
|
||||
- [IDispatcher.cs](file://ClassicAssist.Shared/IDispatcher.cs)
|
||||
- [WPFDispatcher.cs](file://ClassicAssist/WPFDispatcher.cs)
|
||||
- [AvaloniaDispatcher.cs](file://ClassicAssist.Avalonia/AvaloniaDispatcher.cs)
|
||||
|
||||
### UI Invoker Interface and Implementations
|
||||
Purpose:
|
||||
- Encapsulate UI framework-specific invocation semantics
|
||||
- Offer IsOnUIThread checks and safe InvokeOnUiThread helpers
|
||||
|
||||
Implementation highlights:
|
||||
- WPFUIInvoker uses WPF’s thread affinity APIs
|
||||
- AvaloniaUIInvoker uses Avalonia’s thread context APIs
|
||||
- Shared code calls IUIInvoker without knowing the underlying framework
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Start(["Start"]) --> Check["Check if currently on UI thread"]
|
||||
Check --> |Yes| ExecuteDirect["Execute UI update directly"]
|
||||
Check --> |No| Enqueue["Enqueue action via dispatcher"]
|
||||
Enqueue --> Wait["Wait for completion if needed"]
|
||||
ExecuteDirect --> End(["End"])
|
||||
Wait --> End
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [IUIInvoker.cs](file://ClassicAssist.Shared/IUIInvoker.cs)
|
||||
- [WPFUIInvoker.cs](file://ClassicAssist/WPFUIInvoker.cs)
|
||||
- [AvaloniaUIInvoker.cs](file://ClassicAssist.Avalonia/AvaloniaUIInvoker.cs)
|
||||
|
||||
**Section sources**
|
||||
- [IUIInvoker.cs](file://ClassicAssist.Shared/IUIInvoker.cs)
|
||||
- [WPFUIInvoker.cs](file://ClassicAssist/WPFUIInvoker.cs)
|
||||
- [AvaloniaUIInvoker.cs](file://ClassicAssist.Avalonia/AvaloniaUIInvoker.cs)
|
||||
|
||||
### Engine Initialization and Platform Selection
|
||||
Purpose:
|
||||
- Initialize the correct platform-specific dispatcher and UI invoker
|
||||
- Configure application lifecycle and entry points per platform
|
||||
|
||||
Implementation highlights:
|
||||
- Shared Engine defines common startup logic and configuration
|
||||
- WPF Engine initializes WPF-specific resources and sets up WPFDispatcher/WPFUIInvoker
|
||||
- Avalonia Engine initializes Avalonia-specific resources and sets up AvaloniaDispatcher/AvaloniaUIInvoker
|
||||
- Avalonia Program and App coordinate bootstrapping and DI setup
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant OS as "Operating System"
|
||||
participant Entry as "Program.cs (Avalonia)"
|
||||
participant App as "App.axaml.cs (Avalonia)"
|
||||
participant Eng as "Engine.cs (Avalonia)"
|
||||
participant Disp as "AvaloniaDispatcher"
|
||||
participant UIInv as "AvaloniaUIInvoker"
|
||||
OS->>Entry : "Launch process"
|
||||
Entry->>App : "Initialize Avalonia"
|
||||
App->>Eng : "Create and configure Engine"
|
||||
Eng->>Disp : "Register dispatcher"
|
||||
Eng->>UIInv : "Register UI invoker"
|
||||
Eng-->>App : "Ready"
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [Program.cs (Avalonia)](file://ClassicAssist.Avalonia/Program.cs)
|
||||
- [App.axaml.cs (Avalonia)](file://ClassicAssist.Avalonia/App.axaml.cs)
|
||||
- [Engine.cs (Avalonia)](file://ClassicAssist.Avalonia/Engine.cs)
|
||||
- [Engine.cs (WPF)](file://ClassicAssist/Engine.cs)
|
||||
- [Engine.cs (Shared)](file://ClassicAssist.Shared/Engine.cs)
|
||||
|
||||
**Section sources**
|
||||
- [Engine.cs (Shared)](file://ClassicAssist.Shared/Engine.cs)
|
||||
- [Engine.cs (WPF)](file://ClassicAssist/Engine.cs)
|
||||
- [Engine.cs (Avalonia)](file://ClassicAssist.Avalonia/Engine.cs)
|
||||
- [Program.cs (Avalonia)](file://ClassicAssist.Avalonia/Program.cs)
|
||||
- [App.axaml.cs (Avalonia)](file://ClassicAssist.Avalonia/App.axaml.cs)
|
||||
|
||||
### Window Management Abstraction
|
||||
Purpose:
|
||||
- Provide a consistent API for creating, showing, and managing windows across platforms
|
||||
- Hide differences between WPF Window and Avalonia Window lifecycles
|
||||
|
||||
Approach:
|
||||
- Use platform-specific providers registered by each Engine implementation
|
||||
- Expose simple Create/Show/Close operations from shared code
|
||||
|
||||
Considerations:
|
||||
- Ensure UI thread affinity when manipulating window state
|
||||
- Handle platform differences in modal dialogs and owner relationships
|
||||
|
||||
[No sources needed since this section doesn't analyze specific files]
|
||||
|
||||
### File System Access Abstraction
|
||||
Purpose:
|
||||
- Normalize path separators, case sensitivity, and special directories across platforms
|
||||
- Provide helper utilities for reading/writing configuration and data files
|
||||
|
||||
Approach:
|
||||
- Use shared utility functions that adapt to platform specifics
|
||||
- Centralize path resolution and normalization
|
||||
|
||||
Considerations:
|
||||
- Respect user profile locations and sandboxing constraints
|
||||
- Handle permission differences on Linux/macOS vs Windows
|
||||
|
||||
[No sources needed since this section doesn't analyze specific files]
|
||||
|
||||
### Native Interop Calls
|
||||
Purpose:
|
||||
- Provide a single point for platform-specific native calls
|
||||
- Abstract away differences in P/Invoke signatures and availability
|
||||
|
||||
Approach:
|
||||
- Define conditional compilation or runtime detection for available APIs
|
||||
- Wrap native calls behind shared interfaces where possible
|
||||
|
||||
Considerations:
|
||||
- Guard against missing symbols on non-Windows platforms
|
||||
- Prefer managed alternatives when available
|
||||
|
||||
**Section sources**
|
||||
- [NativeMethods.cs](file://ClassicAssist.Shared/Misc/NativeMethods.cs)
|
||||
- [PlatformDetector.cs](file://ClassicAssist.Shared/Engine/PlatformDetector.cs)
|
||||
|
||||
### Message Box Provider Abstraction
|
||||
Purpose:
|
||||
- Standardize user-facing dialog interactions across platforms
|
||||
- Allow shared code to show messages without depending on UI framework details
|
||||
|
||||
Implementation highlights:
|
||||
- MessageBoxProvider defines the shared contract
|
||||
- WPFMessageBoxProvider implements it using WPF dialogs
|
||||
- AvaloniaMessageBoxProvider implements it using Avalonia dialogs
|
||||
|
||||
```mermaid
|
||||
classDiagram
|
||||
class MessageBoxProvider {
|
||||
<<interface>>
|
||||
+ "Show(message)"
|
||||
}
|
||||
class WPFMessageBoxProvider
|
||||
class AvaloniaMessageBoxProvider
|
||||
MessageBoxProvider <|.. WPFMessageBoxProvider : "implements"
|
||||
MessageBoxProvider <|.. AvaloniaMessageBoxProvider : "implements"
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [MessageBoxProvider.cs](file://ClassicAssist.Shared/Misc/MessageBoxProvider.cs)
|
||||
- [WPFMessageBoxProvider.cs](file://ClassicAssist/UI/Misc/WPFMessageBoxProvider.cs)
|
||||
- [AvaloniaMessageBoxProvider.cs](file://ClassicAssist.Avalonia/Misc/AvaloniaMessageBoxProvider.cs)
|
||||
|
||||
**Section sources**
|
||||
- [MessageBoxProvider.cs](file://ClassicAssist.Shared/Misc/MessageBoxProvider.cs)
|
||||
- [WPFMessageBoxProvider.cs](file://ClassicAssist/UI/Misc/WPFMessageBoxProvider.cs)
|
||||
- [AvaloniaMessageBoxProvider.cs](file://ClassicAssist.Avalonia/Misc/AvaloniaMessageBoxProvider.cs)
|
||||
|
||||
## Dependency Analysis
|
||||
The following diagram shows how shared interfaces are implemented by platform-specific classes and how engines wire them together.
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
Shared["Shared Interfaces<br/>IDispatcher, IUIInvoker"] --> WPFImpl["WPF Implementations<br/>WPFDispatcher, WPFUIInvoker"]
|
||||
Shared --> AvaloniaImpl["Avalonia Implementations<br/>AvaloniaDispatcher, AvaloniaUIInvoker"]
|
||||
SharedEng["Shared Engine"] --> WPFEng["WPF Engine"]
|
||||
SharedEng --> AvaloniaEng["Avalonia Engine"]
|
||||
WPFEng --> WPFImpl
|
||||
AvaloniaEng --> AvaloniaImpl
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [IDispatcher.cs](file://ClassicAssist.Shared/IDispatcher.cs)
|
||||
- [IUIInvoker.cs](file://ClassicAssist.Shared/IUIInvoker.cs)
|
||||
- [WPFDispatcher.cs](file://ClassicAssist/WPFDispatcher.cs)
|
||||
- [WPFUIInvoker.cs](file://ClassicAssist/WPFUIInvoker.cs)
|
||||
- [AvaloniaDispatcher.cs](file://ClassicAssist.Avalonia/AvaloniaDispatcher.cs)
|
||||
- [AvaloniaUIInvoker.cs](file://ClassicAssist.Avalonia/AvaloniaUIInvoker.cs)
|
||||
- [Engine.cs (Shared)](file://ClassicAssist.Shared/Engine.cs)
|
||||
- [Engine.cs (WPF)](file://ClassicAssist/Engine.cs)
|
||||
- [Engine.cs (Avalonia)](file://ClassicAssist.Avalonia/Engine.cs)
|
||||
|
||||
**Section sources**
|
||||
- [IDispatcher.cs](file://ClassicAssist.Shared/IDispatcher.cs)
|
||||
- [IUIInvoker.cs](file://ClassicAssist.Shared/IUIInvoker.cs)
|
||||
- [WPFDispatcher.cs](file://ClassicAssist/WPFDispatcher.cs)
|
||||
- [WPFUIInvoker.cs](file://ClassicAssist/WPFUIInvoker.cs)
|
||||
- [AvaloniaDispatcher.cs](file://ClassicAssist.Avalonia/AvaloniaDispatcher.cs)
|
||||
- [AvaloniaUIInvoker.cs](file://ClassicAssist.Avalonia/AvaloniaUIInvoker.cs)
|
||||
- [Engine.cs (Shared)](file://ClassicAssist.Shared/Engine.cs)
|
||||
- [Engine.cs (WPF)](file://ClassicAssist/Engine.cs)
|
||||
- [Engine.cs (Avalonia)](file://ClassicAssist.Avalonia/Engine.cs)
|
||||
|
||||
## Performance Considerations
|
||||
- Minimize cross-thread marshals: batch UI updates when possible to reduce dispatcher overhead
|
||||
- Avoid blocking the UI thread: use BeginInvoke-style patterns for long-running tasks
|
||||
- Prefer lightweight invocations: keep actions small and focused to reduce contention
|
||||
- Cache frequently accessed platform info: avoid repeated platform detection calls
|
||||
- Be mindful of memory pressure: avoid capturing large objects in dispatched closures
|
||||
|
||||
[No sources needed since this section provides general guidance]
|
||||
|
||||
## Troubleshooting Guide
|
||||
Common issues and resolutions:
|
||||
- Cross-thread operation exceptions: ensure all UI updates go through IUIInvoker.InvokeOnUiThread
|
||||
- Deadlocks on UI thread: avoid calling synchronous Invoke from the UI thread; prefer asynchronous patterns
|
||||
- Missing platform features: guard native calls with platform detection and fallbacks
|
||||
- Dialog visibility problems: verify that dialogs are shown on the correct UI thread and have proper owners
|
||||
|
||||
**Section sources**
|
||||
- [IUIInvoker.cs](file://ClassicAssist.Shared/IUIInvoker.cs)
|
||||
- [WPFUIInvoker.cs](file://ClassicAssist/WPFUIInvoker.cs)
|
||||
- [AvaloniaUIInvoker.cs](file://ClassicAssist.Avalonia/AvaloniaUIInvoker.cs)
|
||||
- [PlatformDetector.cs](file://ClassicAssist.Shared/Engine/PlatformDetector.cs)
|
||||
|
||||
## Conclusion
|
||||
The cross-platform abstraction layer cleanly separates shared logic from platform-specific concerns using well-defined interfaces for dispatching and UI invocation. By centralizing platform selection in the Engine implementations and providing consistent APIs for window management, file system access, and native interop, ClassicAssist achieves robust support across Windows, Linux, and macOS. Following the migration strategies and performance guidelines outlined here will help maintain stability and portability as the codebase evolves.
|
||||
|
||||
[No sources needed since this section summarizes without analyzing specific files]
|
||||
|
||||
## Appendices
|
||||
|
||||
### Migration Strategies
|
||||
- Introduce new platform-specific feature:
|
||||
- Add an interface method to the relevant shared interface if necessary
|
||||
- Implement the method in WPF and Avalonia providers
|
||||
- Update Engines to register the new provider
|
||||
- Replace direct UI framework calls:
|
||||
- Refactor to use IUIInvoker and IDispatcher
|
||||
- Remove direct references to WPF/Avalonia types from shared code
|
||||
- Handle native interop:
|
||||
- Wrap calls in shared utilities with platform guards
|
||||
- Provide no-op or alternative implementations for unsupported platforms
|
||||
|
||||
[No sources needed since this section doesn't analyze specific files]
|
||||
+373
@@ -0,0 +1,373 @@
|
||||
# Custom Controls Library
|
||||
|
||||
<cite>
|
||||
**Referenced Files in This Document**
|
||||
- [HexDumpControl.axaml](file://ClassicAssist.Avalonia/Controls/HexDumpControl.axaml)
|
||||
- [HexDumpControl.axaml.cs](file://ClassicAssist.Avalonia/Controls/HexDumpControl.axaml.cs)
|
||||
- [EditTextBlock.axaml](file://ClassicAssist.Avalonia/Controls/EditTextBlock.axaml)
|
||||
- [EditTextBlock.axaml.cs](file://ClassicAssist.Avalonia/Controls/EditTextBlock.axaml.cs)
|
||||
- [TextBoxKey.axaml](file://ClassicAssist.Avalonia/Controls/TextBoxKey.axaml)
|
||||
- [TextBoxKey.axaml.cs](file://ClassicAssist.Avalonia/Controls/TextBoxKey.axaml.cs)
|
||||
- [HueEntryToRectanglesValueConverter.cs](file://ClassicAssist.Avalonia/Misc/HueEntryToRectanglesValueConverter.cs)
|
||||
- [HuePickerWindow.axaml](file://ClassicAssist.Avalonia/Views/HuePickerWindow.axaml)
|
||||
- [HuePickerWindow.axaml.cs](file://ClassicAssist.Avalonia/Views/HuePickerWindow.axaml.cs)
|
||||
</cite>
|
||||
|
||||
## 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)
|
||||
|
||||
## Introduction
|
||||
This document describes the custom controls library built with Avalonia, focusing on four reusable controls:
|
||||
- HexDumpControl for efficient binary data visualization
|
||||
- EditTextBlock for editable text display
|
||||
- TextBoxKey for key binding input
|
||||
- HueEntryControl for color selection (via a picker window and value converter)
|
||||
|
||||
It explains control properties, events, styling options, integration patterns, usage examples, customization guidelines, and performance considerations—especially for complex rendering like hex dump.
|
||||
|
||||
## Project Structure
|
||||
The controls are implemented under ClassicAssist.Avalonia/Controls and related resources. Each control typically consists of an AXAML markup file and a code-behind file. Supporting converters and windows reside under ClassicAssist.Avalonia/Misc and ClassicAssist.Avalonia/Views.
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
subgraph "Avalonia Controls"
|
||||
HDC["HexDumpControl.axaml<br/>HexDumpControl.axaml.cs"]
|
||||
EBT["EditTextBlock.axaml<br/>EditTextBlock.axaml.cs"]
|
||||
TK["TextBoxKey.axaml<br/>TextBoxKey.axaml.cs"]
|
||||
end
|
||||
subgraph "Color Selection"
|
||||
HEC["HueEntryControl (conceptual)<br/>uses HueEntryToRectanglesValueConverter"]
|
||||
HPW["HuePickerWindow.axaml<br/>HuePickerWindow.axaml.cs"]
|
||||
CONV["HueEntryToRectanglesValueConverter.cs"]
|
||||
end
|
||||
HDC --> |"renders bytes"| UI["Avalonia UI"]
|
||||
EBT --> |"editable text"| UI
|
||||
TK --> |"key capture"| UI
|
||||
HEC --> CONV
|
||||
HEC --> HPW
|
||||
```
|
||||
|
||||
[No sources needed since this diagram shows conceptual structure]
|
||||
|
||||
## Core Components
|
||||
- HexDumpControl: Displays large byte buffers as a scrollable grid of rows and columns with optional ASCII column, row headers, and selection support. Optimized for performance via virtualization and incremental updates.
|
||||
- EditTextBlock: A read-only TextBlock that can be toggled into edit mode to allow inline editing, then committed or canceled.
|
||||
- TextBoxKey: Captures keyboard input to represent key combinations (e.g., Ctrl+Shift+A), exposing a bound KeyBinding-like model.
|
||||
- HueEntryControl: Presents a hue selector entry that opens a HuePickerWindow and uses a value converter to render preview rectangles based on selected hues.
|
||||
|
||||
Integration pattern: Bind control properties to view models using Avalonia’s two-way bindings; expose events where appropriate; style via styles and templates.
|
||||
|
||||
**Section sources**
|
||||
- [HexDumpControl.axaml](file://ClassicAssist.Avalonia/Controls/HexDumpControl.axaml)
|
||||
- [HexDumpControl.axaml.cs](file://ClassicAssist.Avalonia/Controls/HexDumpControl.axaml.cs)
|
||||
- [EditTextBlock.axaml](file://ClassicAssist.Avalonia/Controls/EditTextBlock.axaml)
|
||||
- [EditTextBlock.axaml.cs](file://ClassicAssist.Avalonia/Controls/EditTextBlock.axaml.cs)
|
||||
- [TextBoxKey.axaml](file://ClassicAssist.Avalonia/Controls/TextBoxKey.axaml)
|
||||
- [TextBoxKey.axaml.cs](file://ClassicAssist.Avalonia/Controls/TextBoxKey.axaml.cs)
|
||||
- [HueEntryToRectanglesValueConverter.cs](file://ClassicAssist.Avalonia/Misc/HueEntryToRectanglesValueConverter.cs)
|
||||
- [HuePickerWindow.axaml](file://ClassicAssist.Avalonia/Views/HuePickerWindow.axaml)
|
||||
- [HuePickerWindow.axaml.cs](file://ClassicAssist.Avalonia/Views/HuePickerWindow.axaml.cs)
|
||||
|
||||
## Architecture Overview
|
||||
The controls follow a standard Avalonia pattern:
|
||||
- AXAML defines layout and visual tree
|
||||
- Code-behind exposes dependency properties and handles events
|
||||
- ViewModels bind to these properties
|
||||
- Converters transform values for display
|
||||
- Windows provide modal interactions (e.g., HuePickerWindow)
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant VM as "ViewModel"
|
||||
participant HDC as "HexDumpControl"
|
||||
participant UI as "Avalonia UI"
|
||||
VM->>HDC : Set Data property
|
||||
HDC->>UI : Render rows/columns (virtualized)
|
||||
UI-->>HDC : Scroll/Selection events
|
||||
HDC-->>VM : Notify changes (events/properties)
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [HexDumpControl.axaml](file://ClassicAssist.Avalonia/Controls/HexDumpControl.axaml)
|
||||
- [HexDumpControl.axaml.cs](file://ClassicAssist.Avalonia/Controls/HexDumpControl.axaml.cs)
|
||||
|
||||
## Detailed Component Analysis
|
||||
|
||||
### HexDumpControl
|
||||
Purpose: Efficiently visualize large byte arrays or streams as a hex grid with optional ASCII representation and selection.
|
||||
|
||||
Key responsibilities:
|
||||
- Accept a byte source and render it into a grid
|
||||
- Support scrolling, selection, and optional ASCII column
|
||||
- Provide styling hooks for colors, fonts, and cell sizes
|
||||
- Expose events for user interaction (e.g., selection changed)
|
||||
|
||||
Typical properties and behaviors:
|
||||
- Data: Source bytes or buffer
|
||||
- RowCount/ColumnWidth: Layout configuration
|
||||
- ShowAscii: Toggle ASCII column visibility
|
||||
- SelectedRange: Current selection range
|
||||
- Events: SelectionChanged, Scrolled, etc.
|
||||
|
||||
Styling options:
|
||||
- Cell background/foreground
|
||||
- Header brushes
|
||||
- Selection highlight brush
|
||||
- Font family and size for hex and ASCII cells
|
||||
|
||||
Usage example (conceptual):
|
||||
- Bind Data to a byte array in your ViewModel
|
||||
- Handle SelectionChanged to react to user selections
|
||||
- Style via ResourceDictionary for consistent look across app
|
||||
|
||||
Performance considerations:
|
||||
- Use virtualization to avoid creating all rows at once
|
||||
- Batch updates when data changes frequently
|
||||
- Avoid heavy per-cell computations; precompute if possible
|
||||
- Limit visible columns to reduce layout cost
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Start(["Set Data"]) --> Validate["Validate input length"]
|
||||
Validate --> InitLayout["Initialize grid layout<br/>rows/columns"]
|
||||
InitLayout --> Virtualize["Enable virtualization"]
|
||||
Virtualize --> Render["Render visible cells"]
|
||||
Render --> Interact{"User interaction?"}
|
||||
Interact --> |Scroll| UpdateVisible["Update visible viewport"]
|
||||
Interact --> |Select| UpdateSelection["Update selection state"]
|
||||
UpdateVisible --> Render
|
||||
UpdateSelection --> Render
|
||||
Interact --> |No| End(["Idle"])
|
||||
Render --> End
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [HexDumpControl.axaml](file://ClassicAssist.Avalonia/Controls/HexDumpControl.axaml)
|
||||
- [HexDumpControl.axaml.cs](file://ClassicAssist.Avalonia/Controls/HexDumpControl.axaml.cs)
|
||||
|
||||
**Section sources**
|
||||
- [HexDumpControl.axaml](file://ClassicAssist.Avalonia/Controls/HexDumpControl.axaml)
|
||||
- [HexDumpControl.axaml.cs](file://ClassicAssist.Avalonia/Controls/HexDumpControl.axaml.cs)
|
||||
|
||||
### EditTextBlock
|
||||
Purpose: Display text in a TextBlock-like control that can switch to an editable TextBox for inline editing.
|
||||
|
||||
Key responsibilities:
|
||||
- Show text in read-only mode
|
||||
- Enter edit mode on demand (e.g., double-click)
|
||||
- Commit or cancel edits
|
||||
- Keep content synchronized with a bound property
|
||||
|
||||
Typical properties and behaviors:
|
||||
- Text: Two-way bound text content
|
||||
- IsEditable: Toggle editability
|
||||
- EditModeTrigger: Event or command to enter edit mode
|
||||
- Events: EditingStarted, EditingEnded, TextChanged
|
||||
|
||||
Styling options:
|
||||
- Read-only appearance (font, padding)
|
||||
- Edit mode appearance (border, focus visuals)
|
||||
- Placeholder text styling
|
||||
|
||||
Usage example (conceptual):
|
||||
- Bind Text to a string property in ViewModel
|
||||
- Trigger edit mode via command or gesture
|
||||
- On commit, update ViewModel; on cancel, revert
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant User as "User"
|
||||
participant EBT as "EditTextBlock"
|
||||
participant VM as "ViewModel"
|
||||
User->>EBT : Double-click to edit
|
||||
EBT->>EBT : Enter edit mode
|
||||
User->>EBT : Type new text
|
||||
EBT->>VM : Update bound Text (two-way)
|
||||
User->>EBT : Press Enter or lose focus
|
||||
EBT->>EBT : Exit edit mode
|
||||
EBT-->>VM : Commit final value
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [EditTextBlock.axaml](file://ClassicAssist.Avalonia/Controls/EditTextBlock.axaml)
|
||||
- [EditTextBlock.axaml.cs](file://ClassicAssist.Avalonia/Controls/EditTextBlock.axaml.cs)
|
||||
|
||||
**Section sources**
|
||||
- [EditTextBlock.axaml](file://ClassicAssist.Avalonia/Controls/EditTextBlock.axaml)
|
||||
- [EditTextBlock.axaml.cs](file://ClassicAssist.Avalonia/Controls/EditTextBlock.axaml.cs)
|
||||
|
||||
### TextBoxKey
|
||||
Purpose: Capture keyboard input to represent key combinations for hotkeys or shortcuts.
|
||||
|
||||
Key responsibilities:
|
||||
- Capture key presses and modifiers
|
||||
- Format the combination into a readable string
|
||||
- Expose a bound KeyBinding-like model
|
||||
- Prevent default actions when capturing
|
||||
|
||||
Typical properties and behaviors:
|
||||
- KeyText: Displayed key combination string
|
||||
- Modifiers: Captured modifier keys
|
||||
- Events: KeyCaptured, ValidationFailed
|
||||
|
||||
Styling options:
|
||||
- Input border and focus visuals
|
||||
- Error state styling (invalid combinations)
|
||||
|
||||
Usage example (conceptual):
|
||||
- Bind KeyText to a string property in ViewModel
|
||||
- On capture, validate combination and notify ViewModel
|
||||
- Provide clear feedback for invalid inputs
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Start(["Focus TextBoxKey"]) --> Press["User presses keys"]
|
||||
Press --> Capture["Capture key and modifiers"]
|
||||
Capture --> Validate{"Valid combination?"}
|
||||
Validate --> |Yes| Update["Update KeyText and notify"]
|
||||
Validate --> |No| Error["Show validation error"]
|
||||
Update --> End(["Ready"])
|
||||
Error --> End
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [TextBoxKey.axaml](file://ClassicAssist.Avalonia/Controls/TextBoxKey.axaml)
|
||||
- [TextBoxKey.axaml.cs](file://ClassicAssist.Avalonia/Controls/TextBoxKey.axaml.cs)
|
||||
|
||||
**Section sources**
|
||||
- [TextBoxKey.axaml](file://ClassicAssist.Avalonia/Controls/TextBoxKey.axaml)
|
||||
- [TextBoxKey.axaml.cs](file://ClassicAssist.Avalonia/Controls/TextBoxKey.axaml.cs)
|
||||
|
||||
### HueEntryControl
|
||||
Purpose: Provide a compact entry for selecting hues, opening a HuePickerWindow and rendering previews using a value converter.
|
||||
|
||||
Key responsibilities:
|
||||
- Open HuePickerWindow to select hue(s)
|
||||
- Convert selected hues to visual rectangles for preview
|
||||
- Expose selected hue values to ViewModel
|
||||
|
||||
Typical properties and behaviors:
|
||||
- SelectedHues: Collection of selected hues
|
||||
- PreviewBrushes: Converted brushes for preview
|
||||
- Events: HueSelected, PickerClosed
|
||||
|
||||
Styling options:
|
||||
- Entry border and focus visuals
|
||||
- Preview rectangle size and spacing
|
||||
- Picker button styling
|
||||
|
||||
Usage example (conceptual):
|
||||
- Bind SelectedHues to a collection in ViewModel
|
||||
- Use HueEntryToRectanglesValueConverter to render preview rectangles
|
||||
- Handle HueSelected to update UI or settings
|
||||
|
||||
```mermaid
|
||||
classDiagram
|
||||
class HueEntryControl {
|
||||
+SelectedHues
|
||||
+PreviewBrushes
|
||||
+OpenPicker()
|
||||
}
|
||||
class HueEntryToRectanglesValueConverter {
|
||||
+Convert(value)
|
||||
}
|
||||
class HuePickerWindow {
|
||||
+OnHueSelected(hue)
|
||||
}
|
||||
HueEntryControl --> HueEntryToRectanglesValueConverter : "uses"
|
||||
HueEntryControl --> HuePickerWindow : "opens"
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [HueEntryToRectanglesValueConverter.cs](file://ClassicAssist.Avalonia/Misc/HueEntryToRectanglesValueConverter.cs)
|
||||
- [HuePickerWindow.axaml](file://ClassicAssist.Avalonia/Views/HuePickerWindow.axaml)
|
||||
- [HuePickerWindow.axaml.cs](file://ClassicAssist.Avalonia/Views/HuePickerWindow.axaml.cs)
|
||||
|
||||
**Section sources**
|
||||
- [HueEntryToRectanglesValueConverter.cs](file://ClassicAssist.Avalonia/Misc/HueEntryToRectanglesValueConverter.cs)
|
||||
- [HuePickerWindow.axaml](file://ClassicAssist.Avalonia/Views/HuePickerWindow.axaml)
|
||||
- [HuePickerWindow.axaml.cs](file://ClassicAssist.Avalonia/Views/HuePickerWindow.axaml.cs)
|
||||
|
||||
## Dependency Analysis
|
||||
- HexDumpControl depends on Avalonia’s ItemsControl/Grid infrastructure and may rely on virtualization features for performance.
|
||||
- EditTextBlock composes TextBlock and TextBox internally, switching visibility based on edit mode.
|
||||
- TextBoxKey listens to low-level keyboard events and formats them into a display string.
|
||||
- HueEntryControl depends on HueEntryToRectanglesValueConverter and HuePickerWindow for selection and preview.
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
HDC["HexDumpControl"] --> AV["Avalonia Grid/ItemsControl"]
|
||||
EBT["EditTextBlock"] --> TB["TextBox"]
|
||||
EBT --> TBLK["TextBlock"]
|
||||
TK["TextBoxKey"] --> KEV["Keyboard Events"]
|
||||
HEC["HueEntryControl"] --> CONV["HueEntryToRectanglesValueConverter"]
|
||||
HEC --> HPW["HuePickerWindow"]
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [HexDumpControl.axaml](file://ClassicAssist.Avalonia/Controls/HexDumpControl.axaml)
|
||||
- [EditTextBlock.axaml](file://ClassicAssist.Avalonia/Controls/EditTextBlock.axaml)
|
||||
- [TextBoxKey.axaml](file://ClassicAssist.Avalonia/Controls/TextBoxKey.axaml)
|
||||
- [HueEntryToRectanglesValueConverter.cs](file://ClassicAssist.Avalonia/Misc/HueEntryToRectanglesValueConverter.cs)
|
||||
- [HuePickerWindow.axaml](file://ClassicAssist.Avalonia/Views/HuePickerWindow.axaml)
|
||||
|
||||
**Section sources**
|
||||
- [HexDumpControl.axaml](file://ClassicAssist.Avalonia/Controls/HexDumpControl.axaml)
|
||||
- [EditTextBlock.axaml](file://ClassicAssist.Avalonia/Controls/EditTextBlock.axaml)
|
||||
- [TextBoxKey.axaml](file://ClassicAssist.Avalonia/Controls/TextBoxKey.axaml)
|
||||
- [HueEntryToRectanglesValueConverter.cs](file://ClassicAssist.Avalonia/Misc/HueEntryToRectanglesValueConverter.cs)
|
||||
- [HuePickerWindow.axaml](file://ClassicAssist.Avalonia/Views/HuePickerWindow.axaml)
|
||||
|
||||
## Performance Considerations
|
||||
- HexDumpControl:
|
||||
- Enable virtualization to avoid creating off-screen elements
|
||||
- Batch updates by replacing entire data sets rather than mutating large collections incrementally
|
||||
- Precompute ASCII representations if needed
|
||||
- Limit column count and adjust cell width to balance readability and performance
|
||||
- Debounce rapid data changes to reduce re-render frequency
|
||||
- EditTextBlock:
|
||||
- Avoid unnecessary rebinds during edit sessions
|
||||
- Use lightweight validation to prevent expensive checks on every keystroke
|
||||
- TextBoxKey:
|
||||
- Minimize event handling overhead; only process relevant key combinations
|
||||
- Cache formatted strings for repeated combinations
|
||||
- HueEntryControl:
|
||||
- Convert hues to brushes lazily and cache results
|
||||
- Limit number of preview rectangles to maintain responsiveness
|
||||
|
||||
[No sources needed since this section provides general guidance]
|
||||
|
||||
## Troubleshooting Guide
|
||||
Common issues and resolutions:
|
||||
- HexDumpControl not updating:
|
||||
- Ensure Data property is set on the UI thread
|
||||
- Verify virtualization is enabled and items panel supports recycling
|
||||
- EditTextBlock not committing edits:
|
||||
- Check focus management and blur handlers
|
||||
- Confirm two-way binding path is correct
|
||||
- TextBoxKey capturing wrong keys:
|
||||
- Validate modifier key detection logic
|
||||
- Ensure default actions are prevented during capture
|
||||
- HueEntryControl preview not showing:
|
||||
- Verify converter returns valid brushes
|
||||
- Confirm HuePickerWindow closes with selected hue and notifies control
|
||||
|
||||
**Section sources**
|
||||
- [HexDumpControl.axaml.cs](file://ClassicAssist.Avalonia/Controls/HexDumpControl.axaml.cs)
|
||||
- [EditTextBlock.axaml.cs](file://ClassicAssist.Avalonia/Controls/EditTextBlock.axaml.cs)
|
||||
- [TextBoxKey.axaml.cs](file://ClassicAssist.Avalonia/Controls/TextBoxKey.axaml.cs)
|
||||
- [HueEntryToRectanglesValueConverter.cs](file://ClassicAssist.Avalonia/Misc/HueEntryToRectanglesValueConverter.cs)
|
||||
- [HuePickerWindow.axaml.cs](file://ClassicAssist.Avalonia/Views/HuePickerWindow.axaml.cs)
|
||||
|
||||
## Conclusion
|
||||
The custom controls library provides robust, reusable components for common UI tasks in Avalonia applications. By leveraging dependency properties, events, and converters, each control integrates cleanly with MVVM patterns. For high-performance scenarios like hex dump rendering, prioritize virtualization and batch updates. Consistent styling and clear integration patterns ensure maintainability and extensibility across the application.
|
||||
|
||||
[No sources needed since this section summarizes without analyzing specific files]
|
||||
+530
@@ -0,0 +1,530 @@
|
||||
# MVVM Architecture Implementation
|
||||
|
||||
<cite>
|
||||
**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)
|
||||
</cite>
|
||||
|
||||
## 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]
|
||||
+395
@@ -0,0 +1,395 @@
|
||||
# Theming and Styling System
|
||||
|
||||
<cite>
|
||||
**Referenced Files in This Document**
|
||||
- [App.axaml](file://ClassicAssist.Avalonia/App.axaml)
|
||||
- [App.axaml.cs](file://ClassicAssist.Avalonia/App.axaml.cs)
|
||||
- [Icons.xaml](file://ClassicAssist.Avalonia/Assets/Icons.xaml)
|
||||
- [GroupBox.Theme.xaml](file://ClassicAssist.Avalonia/Controls/GroupBox.Theme.xaml)
|
||||
- [ResourceDictionary.xaml](file://ClassicAssist/Resources/ResourceDictionary.xaml)
|
||||
- [Colours.xaml](file://ClassicAssist/Resources/Colours.xaml)
|
||||
- [DarkTheme.xaml](file://ClassicAssist/Resources/DarkTheme.xaml)
|
||||
- [MainWindow.axaml](file://ClassicAssist.Avalonia/Views/MainWindow.axaml)
|
||||
- [DialogWindow.axaml](file://ClassicAssist.Avalonia/Views/DialogWindow.axaml)
|
||||
</cite>
|
||||
|
||||
## 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 theming and styling system used by ClassicAssist.Avalonia. It covers how resource dictionaries are organized, how color schemes and themes are defined and switched, how icons are implemented, how fonts are managed, and what cross-platform considerations apply. It also provides guidance for creating custom themes, overriding default styles, implementing responsive design patterns, and building dynamic theme switching with style inheritance hierarchies.
|
||||
|
||||
## Project Structure
|
||||
The Avalonia UI layer is organized into Views, Controls, Assets, and App-level configuration. Theming-related resources live both at the application level (Avalonia project) and within shared resources (ClassicAssist project). The main entry point configures global styles and resources.
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
subgraph "Avalonia App"
|
||||
A["App.axaml"]
|
||||
B["App.axaml.cs"]
|
||||
C["Assets/Icons.xaml"]
|
||||
D["Controls/GroupBox.Theme.xaml"]
|
||||
E["Views/MainWindow.axaml"]
|
||||
F["Views/DialogWindow.axaml"]
|
||||
end
|
||||
subgraph "Shared Resources"
|
||||
G["Resources/ResourceDictionary.xaml"]
|
||||
H["Resources/Colours.xaml"]
|
||||
I["Resources/DarkTheme.xaml"]
|
||||
end
|
||||
A --> C
|
||||
A --> D
|
||||
A --> G
|
||||
A --> H
|
||||
A --> I
|
||||
E --> A
|
||||
F --> A
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [App.axaml](file://ClassicAssist.Avalonia/App.axaml)
|
||||
- [App.axaml.cs](file://ClassicAssist.Avalonia/App.axaml.cs)
|
||||
- [Icons.xaml](file://ClassicAssist.Avalonia/Assets/Icons.xaml)
|
||||
- [GroupBox.Theme.xaml](file://ClassicAssist.Avalonia/Controls/GroupBox.Theme.xaml)
|
||||
- [ResourceDictionary.xaml](file://ClassicAssist/Resources/ResourceDictionary.xaml)
|
||||
- [Colours.xaml](file://ClassicAssist/Resources/Colours.xaml)
|
||||
- [DarkTheme.xaml](file://ClassicAssist/Resources/DarkTheme.xaml)
|
||||
- [MainWindow.axaml](file://ClassicAssist.Avalonia/Views/MainWindow.axaml)
|
||||
- [DialogWindow.axaml](file://ClassicAssist.Avalonia/Views/DialogWindow.axaml)
|
||||
|
||||
**Section sources**
|
||||
- [App.axaml](file://ClassicAssist.Avalonia/App.axaml)
|
||||
- [App.axaml.cs](file://ClassicAssist.Avalonia/App.axaml.cs)
|
||||
- [Icons.xaml](file://ClassicAssist.Avalonia/Assets/Icons.xaml)
|
||||
- [GroupBox.Theme.xaml](file://ClassicAssist.Avalonia/Controls/GroupBox.Theme.xaml)
|
||||
- [ResourceDictionary.xaml](file://ClassicAssist/Resources/ResourceDictionary.xaml)
|
||||
- [Colours.xaml](file://ClassicAssist/Resources/Colours.xaml)
|
||||
- [DarkTheme.xaml](file://ClassicAssist/Resources/DarkTheme.xaml)
|
||||
- [MainWindow.axaml](file://ClassicAssist.Avalonia/Views/MainWindow.axaml)
|
||||
- [DialogWindow.axaml](file://ClassicAssist.Avalonia/Views/DialogWindow.axaml)
|
||||
|
||||
## Core Components
|
||||
- Application-level resource dictionary: Centralizes merged dictionaries for colors, themes, icons, and control-specific themes.
|
||||
- Color scheme definitions: Shared color tokens that can be reused across controls and views.
|
||||
- Theme definitions: Named sets of resources (colors, brushes, etc.) that can be swapped at runtime.
|
||||
- Icon system: A centralized icon dictionary referenced by views and controls.
|
||||
- Control-level themes: Per-control theme dictionaries to override defaults consistently.
|
||||
- View integration: Views reference theme resources via static resources or themed bindings.
|
||||
|
||||
Key responsibilities:
|
||||
- App.axaml merges all theme-related dictionaries and exposes them globally.
|
||||
- ResourceDictionary.xaml aggregates shared resources from multiple files.
|
||||
- Colours.xaml defines semantic color tokens.
|
||||
- DarkTheme.xaml provides a complete dark theme set.
|
||||
- Icons.xaml defines reusable vector/icon resources.
|
||||
- GroupBox.Theme.xaml demonstrates control-level theme overrides.
|
||||
|
||||
**Section sources**
|
||||
- [App.axaml](file://ClassicAssist.Avalonia/App.axaml)
|
||||
- [ResourceDictionary.xaml](file://ClassicAssist/Resources/ResourceDictionary.xaml)
|
||||
- [Colours.xaml](file://ClassicAssist/Resources/Colours.xaml)
|
||||
- [DarkTheme.xaml](file://ClassicAssist/Resources/DarkTheme.xaml)
|
||||
- [Icons.xaml](file://ClassicAssist.Avalonia/Assets/Icons.xaml)
|
||||
- [GroupBox.Theme.xaml](file://ClassicAssist.Avalonia/Controls/GroupBox.Theme.xaml)
|
||||
|
||||
## Architecture Overview
|
||||
The theming architecture follows a layered approach:
|
||||
- Global app resources define base tokens and merged dictionaries.
|
||||
- Themes provide named resource sets that can be applied dynamically.
|
||||
- Control-level themes refine appearance for specific components.
|
||||
- Views consume resources through static references and data binding.
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant App as "App.axaml"
|
||||
participant Merged as "Merged Dictionaries"
|
||||
participant Colors as "Colours.xaml"
|
||||
participant Theme as "DarkTheme.xaml"
|
||||
participant Icons as "Icons.xaml"
|
||||
participant ControlTheme as "GroupBox.Theme.xaml"
|
||||
participant View as "MainWindow.axaml"
|
||||
App->>Merged : "Merge theme resources"
|
||||
Merged->>Colors : "Load color tokens"
|
||||
Merged->>Theme : "Load theme palette"
|
||||
Merged->>Icons : "Load icon definitions"
|
||||
Merged->>ControlTheme : "Load control overrides"
|
||||
View->>Merged : "Resolve resources via StaticResource"
|
||||
Note over View,Merged : "View uses theme tokens and icons"
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [App.axaml](file://ClassicAssist.Avalonia/App.axaml)
|
||||
- [ResourceDictionary.xaml](file://ClassicAssist/Resources/ResourceDictionary.xaml)
|
||||
- [Colours.xaml](file://ClassicAssist/Resources/Colours.xaml)
|
||||
- [DarkTheme.xaml](file://ClassicAssist/Resources/DarkTheme.xaml)
|
||||
- [Icons.xaml](file://ClassicAssist.Avalonia/Assets/Icons.xaml)
|
||||
- [GroupBox.Theme.xaml](file://ClassicAssist.Avalonia/Controls/GroupBox.Theme.xaml)
|
||||
- [MainWindow.axaml](file://ClassicAssist.Avalonia/Views/MainWindow.axaml)
|
||||
|
||||
## Detailed Component Analysis
|
||||
|
||||
### Resource Dictionary Structure
|
||||
- Aggregation: A central dictionary merges multiple theme-related dictionaries to keep resources modular and maintainable.
|
||||
- Scope: Resources are available application-wide when merged at the app level; local scopes can override as needed.
|
||||
- Organization: Semantic tokens (colors), theme palettes (dark/light), icons, and control-specific themes are separated into distinct files.
|
||||
|
||||
Practical implications:
|
||||
- Adding a new theme involves creating a new theme dictionary and merging it at the app level.
|
||||
- Overriding a single token is straightforward by redefining the key in a higher-priority dictionary.
|
||||
|
||||
**Section sources**
|
||||
- [ResourceDictionary.xaml](file://ClassicAssist/Resources/ResourceDictionary.xaml)
|
||||
- [App.axaml](file://ClassicAssist.Avalonia/App.axaml)
|
||||
|
||||
### Color Schemes and Theme Definitions
|
||||
- Color tokens: Defined in a dedicated file to ensure consistency across the UI.
|
||||
- Theme sets: Provide complete palettes (e.g., dark theme) including background, foreground, accent, and state colors.
|
||||
- Switching mechanism: At runtime, replace the active theme dictionary or update resource keys to reflect the selected theme.
|
||||
|
||||
Guidelines:
|
||||
- Use semantic keys rather than hard-coded values.
|
||||
- Keep theme dictionaries self-contained and keyed consistently.
|
||||
- Ensure contrast and accessibility requirements are met for each theme.
|
||||
|
||||
**Section sources**
|
||||
- [Colours.xaml](file://ClassicAssist/Resources/Colours.xaml)
|
||||
- [DarkTheme.xaml](file://ClassicAssist/Resources/DarkTheme.xaml)
|
||||
- [App.axaml](file://ClassicAssist.Avalonia/App.axaml)
|
||||
|
||||
### Theme Switching Mechanisms
|
||||
Two common approaches:
|
||||
- Dictionary replacement: Swap the active theme dictionary in the merged resources collection.
|
||||
- Resource key updates: Update specific resource keys (e.g., colors) to reflect the new theme without full replacement.
|
||||
|
||||
Implementation notes:
|
||||
- Perform theme changes on the UI thread to avoid synchronization issues.
|
||||
- Ensure views resolve resources after the change; most XAML-based frameworks automatically refresh when resources change.
|
||||
|
||||
Example flow:
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Start(["User selects theme"]) --> Choose["Determine target theme"]
|
||||
Choose --> Replace{"Replace dictionary<br/>or update keys?"}
|
||||
Replace --> |Replace| MergeUpdate["Update merged dictionaries"]
|
||||
Replace --> |Update Keys| KeyUpdate["Set new values for resource keys"]
|
||||
MergeUpdate --> Refresh["UI resolves updated resources"]
|
||||
KeyUpdate --> Refresh
|
||||
Refresh --> End(["Theme applied"])
|
||||
```
|
||||
|
||||
[No diagram sources since this is a conceptual flow]
|
||||
|
||||
**Section sources**
|
||||
- [App.axaml](file://ClassicAssist.Avalonia/App.axaml)
|
||||
- [App.axaml.cs](file://ClassicAssist.Avalonia/App.axaml.cs)
|
||||
|
||||
### Icon System Implementation
|
||||
- Centralized icons: Icons are defined in a dedicated dictionary and referenced by name across views and controls.
|
||||
- Usage pattern: References use static resource lookups by icon key.
|
||||
- Extensibility: New icons are added to the icon dictionary and consumed via their keys.
|
||||
|
||||
Best practices:
|
||||
- Keep icon assets consistent in size and style.
|
||||
- Prefer vector formats where possible for scalability.
|
||||
- Avoid duplicating icon definitions; always reference the central dictionary.
|
||||
|
||||
**Section sources**
|
||||
- [Icons.xaml](file://ClassicAssist.Avalonia/Assets/Icons.xaml)
|
||||
- [MainWindow.axaml](file://ClassicAssist.Avalonia/Views/MainWindow.axaml)
|
||||
|
||||
### Font Management
|
||||
- Fonts should be embedded as Avalonia resources and referenced by family name.
|
||||
- Use consistent font families across themes to maintain readability.
|
||||
- For cross-platform compatibility, verify font availability and fallbacks.
|
||||
|
||||
Recommendations:
|
||||
- Define font family tokens in resources for easy overrides.
|
||||
- Test rendering on Windows, macOS, and Linux to ensure consistent appearance.
|
||||
|
||||
**Section sources**
|
||||
- [App.axaml](file://ClassicAssist.Avalonia/App.axaml)
|
||||
|
||||
### Cross-Platform Styling Considerations
|
||||
- Platform differences: Window chrome, DPI scaling, and font rendering vary across platforms.
|
||||
- Responsive behavior: Use relative sizing and flexible layouts to adapt to different screen sizes and densities.
|
||||
- Accessibility: Ensure sufficient contrast and scalable text across themes.
|
||||
|
||||
Guidance:
|
||||
- Avoid platform-specific assumptions in shared resources.
|
||||
- Validate visual fidelity on each target OS during development.
|
||||
|
||||
**Section sources**
|
||||
- [App.axaml](file://ClassicAssist.Avalonia/App.axaml)
|
||||
|
||||
### Creating Custom Themes
|
||||
Steps:
|
||||
1. Create a new theme dictionary with consistent resource keys.
|
||||
2. Define color tokens and any control-specific overrides.
|
||||
3. Merge the new theme dictionary at the app level.
|
||||
4. Provide a runtime switch to activate the new theme.
|
||||
|
||||
Tips:
|
||||
- Mirror existing theme structure for predictability.
|
||||
- Test all major views and controls under the new theme.
|
||||
|
||||
**Section sources**
|
||||
- [DarkTheme.xaml](file://ClassicAssist/Resources/DarkTheme.xaml)
|
||||
- [App.axaml](file://ClassicAssist.Avalonia/App.axaml)
|
||||
|
||||
### Overriding Default Styles
|
||||
Approaches:
|
||||
- Control-level theme dictionaries: Override styles for specific controls (e.g., GroupBox).
|
||||
- Local scope overrides: Apply style overrides within a view’s resource scope.
|
||||
- Global overrides: Redefine resource keys at the app level to affect all instances.
|
||||
|
||||
Example focus:
|
||||
- GroupBox theme overrides demonstrate targeted control customization.
|
||||
|
||||
**Section sources**
|
||||
- [GroupBox.Theme.xaml](file://ClassicAssist.Avalonia/Controls/GroupBox.Theme.xaml)
|
||||
|
||||
### Implementing Responsive Design Patterns
|
||||
Patterns:
|
||||
- Use flexible panels and grid layouts to adapt to window resizing.
|
||||
- Leverage Avalonia’s built-in layout primitives for responsiveness.
|
||||
- Adjust spacing and typography based on available space.
|
||||
|
||||
Validation:
|
||||
- Test on various resolutions and DPI settings.
|
||||
- Ensure content remains readable and accessible.
|
||||
|
||||
**Section sources**
|
||||
- [MainWindow.axaml](file://ClassicAssist.Avalonia/Views/MainWindow.axaml)
|
||||
- [DialogWindow.axaml](file://ClassicAssist.Avalonia/Views/DialogWindow.axaml)
|
||||
|
||||
### Dynamic Theme Switching Example Flow
|
||||
Runtime sequence:
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant User as "User"
|
||||
participant View as "MainWindow.axaml"
|
||||
participant App as "App.axaml.cs"
|
||||
participant Res as "Resources"
|
||||
User->>View : "Select theme option"
|
||||
View->>App : "Invoke theme switch command"
|
||||
App->>Res : "Update merged dictionaries / resource keys"
|
||||
Res-->>View : "Resources refreshed"
|
||||
View-->>User : "New theme applied"
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [App.axaml.cs](file://ClassicAssist.Avalonia/App.axaml.cs)
|
||||
- [App.axaml](file://ClassicAssist.Avalonia/App.axaml)
|
||||
- [MainWindow.axaml](file://ClassicAssist.Avalonia/Views/MainWindow.axaml)
|
||||
|
||||
### Style Inheritance Hierarchies
|
||||
Conceptual hierarchy:
|
||||
- Base tokens (colors, fonts)
|
||||
- Theme palettes (dark/light)
|
||||
- Control-level themes (control-specific overrides)
|
||||
- View-level overrides (local scope)
|
||||
|
||||
```mermaid
|
||||
classDiagram
|
||||
class BaseTokens {
|
||||
+Color Primary
|
||||
+Color Secondary
|
||||
+FontFamily Main
|
||||
}
|
||||
class DarkTheme {
|
||||
+Brush Background
|
||||
+Brush Foreground
|
||||
+Brush Accent
|
||||
}
|
||||
class ControlThemes {
|
||||
+Style GroupBox
|
||||
+Style Button
|
||||
}
|
||||
class ViewOverrides {
|
||||
+Local ResourceScope
|
||||
}
|
||||
DarkTheme --> BaseTokens : "extends"
|
||||
ControlThemes --> DarkTheme : "uses"
|
||||
ViewOverrides --> ControlThemes : "overrides"
|
||||
```
|
||||
|
||||
[No diagram sources since this is a conceptual hierarchy]
|
||||
|
||||
## Dependency Analysis
|
||||
The theming system depends on a clear separation between resources and views:
|
||||
- App-level merges orchestrate resource loading order.
|
||||
- Theme dictionaries depend on base tokens.
|
||||
- Control themes depend on theme palettes.
|
||||
- Views depend on resolved resources.
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
Tokens["Base Tokens (Colours.xaml)"] --> Theme["Theme (DarkTheme.xaml)"]
|
||||
Theme --> ControlTheme["Control Theme (GroupBox.Theme.xaml)"]
|
||||
ControlTheme --> View["Views (MainWindow.axaml, DialogWindow.axaml)"]
|
||||
Icons["Icons (Icons.xaml)"] --> View
|
||||
App["App.axaml"] --> Tokens
|
||||
App --> Theme
|
||||
App --> ControlTheme
|
||||
App --> Icons
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [Colours.xaml](file://ClassicAssist/Resources/Colours.xaml)
|
||||
- [DarkTheme.xaml](file://ClassicAssist/Resources/DarkTheme.xaml)
|
||||
- [GroupBox.Theme.xaml](file://ClassicAssist.Avalonia/Controls/GroupBox.Theme.xaml)
|
||||
- [Icons.xaml](file://ClassicAssist.Avalonia/Assets/Icons.xaml)
|
||||
- [MainWindow.axaml](file://ClassicAssist.Avalonia/Views/MainWindow.axaml)
|
||||
- [DialogWindow.axaml](file://ClassicAssist.Avalonia/Views/DialogWindow.axaml)
|
||||
- [App.axaml](file://ClassicAssist.Avalonia/App.axaml)
|
||||
|
||||
**Section sources**
|
||||
- [App.axaml](file://ClassicAssist.Avalonia/App.axaml)
|
||||
- [ResourceDictionary.xaml](file://ClassicAssist/Resources/ResourceDictionary.xaml)
|
||||
|
||||
## Performance Considerations
|
||||
- Minimize resource churn: Prefer replacing entire theme dictionaries rather than updating many individual keys when switching themes.
|
||||
- Cache frequently used resources: Let the framework handle caching; avoid redundant lookups in code-behind.
|
||||
- Optimize icon assets: Use vector icons to reduce memory footprint and improve scaling performance.
|
||||
- Avoid heavy animations during theme switches to maintain responsiveness.
|
||||
|
||||
[No sources needed since this section provides general guidance]
|
||||
|
||||
## Troubleshooting Guide
|
||||
Common issues and remedies:
|
||||
- Missing resource keys: Ensure all referenced keys exist in the active theme dictionary.
|
||||
- Incorrect merge order: Verify that base tokens are merged before theme palettes and control themes.
|
||||
- Platform-specific rendering differences: Validate fonts and DPI scaling on each target OS.
|
||||
- Runtime theme not applying: Confirm that resource updates occur on the UI thread and that views resolve resources after updates.
|
||||
|
||||
Diagnostic steps:
|
||||
- Inspect merged dictionaries at runtime to confirm presence of required keys.
|
||||
- Temporarily revert to a known-good theme to isolate regressions.
|
||||
- Check for typos in resource keys and file paths.
|
||||
|
||||
**Section sources**
|
||||
- [App.axaml](file://ClassicAssist.Avalonia/App.axaml)
|
||||
- [App.axaml.cs](file://ClassicAssist.Avalonia/App.axaml.cs)
|
||||
|
||||
## Conclusion
|
||||
ClassicAssist.Avalonia’s theming system leverages a modular resource dictionary structure, semantic color tokens, and theme palettes to enable consistent and extensible styling. By separating concerns—base tokens, theme sets, control-level themes, and view usage—the system supports dynamic theme switching, easy customization, and cross-platform consistency. Following the guidelines here will help you create robust custom themes, override styles effectively, and implement responsive designs.
|
||||
|
||||
[No sources needed since this section summarizes without analyzing specific files]
|
||||
|
||||
## Appendices
|
||||
- Quick checklist for adding a new theme:
|
||||
- Create a new theme dictionary with consistent keys.
|
||||
- Merge it at the app level.
|
||||
- Provide a runtime switch to activate it.
|
||||
- Validate across views and platforms.
|
||||
|
||||
[No sources needed since this section provides general guidance]
|
||||
@@ -0,0 +1,513 @@
|
||||
# UI Framework
|
||||
|
||||
<cite>
|
||||
**Referenced Files in This Document**
|
||||
- [Program.cs](file://ClassicAssist.Avalonia/Program.cs)
|
||||
- [App.axaml](file://ClassicAssist.Avalonia/App.axaml)
|
||||
- [App.axaml.cs](file://ClassicAssist.Avalonia/App.axaml.cs)
|
||||
- [MainWindow.axaml](file://ClassicAssist.Avalonia/Views/MainWindow.axaml)
|
||||
- [MainWindow.axaml.cs](file://ClassicAssist.Avalonia/Views/MainWindow.axaml.cs)
|
||||
- [DialogWindow.axaml](file://ClassicAssist.Avalonia/Views/DialogWindow.axaml)
|
||||
- [DialogWindow.axaml.cs](file://ClassicAssist.Avalonia/Views/DialogWindow.axaml.cs)
|
||||
- [DialogWindowViewModel.cs](file://ClassicAssist.Avalonia/ViewModels/DialogWindowViewModel.cs)
|
||||
- [HexDumpControl.axaml](file://ClassicAssist.Avalonia/Controls/HexDumpControl.axaml)
|
||||
- [HexDumpControl.axaml.cs](file://ClassicAssist.Avalonia/Controls/HexDumpControl.axaml.cs)
|
||||
- [EditTextBlock.axaml](file://ClassicAssist.Avalonia/Controls/EditTextBlock.axaml)
|
||||
- [EditTextBlock.axaml.cs](file://ClassicAssist.Avalonia/Controls/EditTextBlock.axaml.cs)
|
||||
- [TextBoxKey.axaml](file://ClassicAssist.Avalonia/Controls/TextBoxKey.axaml)
|
||||
- [TextBoxKey.axaml.cs](file://ClassicAssist.Avalonia/Controls/TextBoxKey.axaml.cs)
|
||||
- [GroupBox.Theme.xaml](file://ClassicAssist.Avalonia/Controls/GroupBox.Theme.xaml)
|
||||
- [GroupBox.cs](file://ClassicAssist.Avalonia/Controls/GroupBox.cs)
|
||||
- [AvaloniaDispatcher.cs](file://ClassicAssist.Avalonia/AvaloniaDispatcher.cs)
|
||||
- [AvaloniaUIInvoker.cs](file://ClassicAssist.Avalonia/AvaloniaUIInvoker.cs)
|
||||
- [AvalonEditBehaviour.cs](file://ClassicAssist.Avalonia/Misc/AvalonEditBehaviour.cs)
|
||||
- [CloseOnClickBehaviour.cs](file://ClassicAssist.Avalonia/Misc/CloseOnClickBehaviour.cs)
|
||||
- [ComboBoxBehavior.cs](file://ClassicAssist.Avalonia/Misc/ComboBoxBehavior.cs)
|
||||
- [DateTimeToTimeSpanValueConverter.cs](file://ClassicAssist.Avalonia/Misc/DateTimeToTimeSpanValueConverter.cs)
|
||||
- [InverseBooleanValueConverter.cs](file://ClassicAssist.Avalonia/Misc/InverseBooleanValueConverter.cs)
|
||||
- [JoinArrayValueConverter.cs](file://ClassicAssist.Avalonia/Misc/JoinArrayValueConverter.cs)
|
||||
- [ListBoxAutoScrollBehaviour.cs](file://ClassicAssist.Avalonia/Misc/ListBoxAutoScrollBehaviour.cs)
|
||||
- [LockStatusValueConverter.cs](file://ClassicAssist.Avalonia/Misc/LockStatusValueConverter.cs)
|
||||
- [PacketEntryBinaryValueConverter.cs](file://ClassicAssist.Avalonia/Misc/PacketEntryBinaryValueConverter.cs)
|
||||
- [PacketEntryTextValueConverter.cs](file://ClassicAssist.Avalonia/Misc/PacketEntryTextValueConverter.cs)
|
||||
- [StringToResourceConverter.cs](file://ClassicAssist.Avalonia/Misc/StringToResourceConverter.cs)
|
||||
- [WindowInitializedBehaviour.cs](file://ClassicAssist.Avalonia/Misc/WindowInitializedBehaviour.cs)
|
||||
- [WindowResizeBehaviour.cs](file://ClassicAssist.Avalonia/Misc/WindowResizeBehaviour.cs)
|
||||
- [HueEntryToRectanglesValueConverter.cs](file://ClassicAssist.Avalonia/Misc/HueEntryToRectanglesValueConverter.cs)
|
||||
- [BaseViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/BaseViewModel.cs)
|
||||
- [MainWindowViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/MainWindowViewModel.cs)
|
||||
- [IDispatcher.cs](file://ClassicAssist.Shared/IDispatcher.cs)
|
||||
- [IUIInvoker.cs](file://ClassicAssist.Shared/IUIInvoker.cs)
|
||||
</cite>
|
||||
|
||||
## 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)
|
||||
|
||||
## Introduction
|
||||
This document describes the UI framework implementation for ClassicAssist.Avalonia, focusing on the MVVM architecture with Avalonia UI, cross-platform abstractions, theme and styling, custom controls, binding system, behaviors, performance strategies, and window management. It provides a clear separation between Views, ViewModels, and Models, and explains how platform-specific implementations are abstracted to support multiple targets consistently.
|
||||
|
||||
## Project Structure
|
||||
The UI layer is organized into distinct folders that reflect MVVM responsibilities:
|
||||
- Controls: Reusable UI components (e.g., HexDumpControl, EditTextBlock, TextBoxKey).
|
||||
- Views: Window and page-level XAML views bound to ViewModels.
|
||||
- ViewModels: Presentation logic and state exposed to Views.
|
||||
- Misc: Behaviors, value converters, and cross-cutting utilities.
|
||||
- App entry points: Application lifecycle and resource initialization.
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
subgraph "Avalonia UI"
|
||||
Program["Program.cs"]
|
||||
App["App.axaml / App.axaml.cs"]
|
||||
MainWindow["Views/MainWindow.axaml(.cs)"]
|
||||
DialogWindow["Views/DialogWindow.axaml(.cs)"]
|
||||
end
|
||||
subgraph "Shared UI Contracts"
|
||||
IDisp["IDispatcher.cs"]
|
||||
IInv["IUIInvoker.cs"]
|
||||
BaseVM["Shared/UI/ViewModels/BaseViewModel.cs"]
|
||||
MainVM["Shared/UI/ViewModels/MainWindowViewModel.cs"]
|
||||
end
|
||||
subgraph "Custom Controls"
|
||||
Hex["Controls/HexDumpControl.axaml(.cs)"]
|
||||
EditTxt["Controls/EditTextBlock.axaml(.cs)"]
|
||||
TxtKey["Controls/TextBoxKey.axaml(.cs)"]
|
||||
GroupBox["Controls/GroupBox.cs + Theme"]
|
||||
end
|
||||
subgraph "Behaviors & Converters"
|
||||
Behav["Misc/*Behaviour.cs"]
|
||||
Conv["Misc/*ValueConverter.cs"]
|
||||
end
|
||||
Program --> App
|
||||
App --> MainWindow
|
||||
MainWindow --> MainVM
|
||||
DialogWindow --> MainVM
|
||||
MainWindow --> Hex
|
||||
MainWindow --> EditTxt
|
||||
MainWindow --> TxtKey
|
||||
MainWindow --> GroupBox
|
||||
MainWindow --> Behav
|
||||
MainWindow --> Conv
|
||||
App --> IDisp
|
||||
App --> IInv
|
||||
MainVM --> BaseVM
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [Program.cs](file://ClassicAssist.Avalonia/Program.cs)
|
||||
- [App.axaml](file://ClassicAssist.Avalonia/App.axaml)
|
||||
- [App.axaml.cs](file://ClassicAssist.Avalonia/App.axaml.cs)
|
||||
- [MainWindow.axaml](file://ClassicAssist.Avalonia/Views/MainWindow.axaml)
|
||||
- [MainWindow.axaml.cs](file://ClassicAssist.Avalonia/Views/MainWindow.axaml.cs)
|
||||
- [DialogWindow.axaml](file://ClassicAssist.Avalonia/Views/DialogWindow.axaml)
|
||||
- [DialogWindow.axaml.cs](file://ClassicAssist.Avalonia/Views/DialogWindow.axaml.cs)
|
||||
- [HexDumpControl.axaml](file://ClassicAssist.Avalonia/Controls/HexDumpControl.axaml)
|
||||
- [HexDumpControl.axaml.cs](file://ClassicAssist.Avalonia/Controls/HexDumpControl.axaml.cs)
|
||||
- [EditTextBlock.axaml](file://ClassicAssist.Avalonia/Controls/EditTextBlock.axaml)
|
||||
- [EditTextBlock.axaml.cs](file://ClassicAssist.Avalonia/Controls/EditTextBlock.axaml.cs)
|
||||
- [TextBoxKey.axaml](file://ClassicAssist.Avalonia/Controls/TextBoxKey.axaml)
|
||||
- [TextBoxKey.axaml.cs](file://ClassicAssist.Avalonia/Controls/TextBoxKey.axaml.cs)
|
||||
- [GroupBox.cs](file://ClassicAssist.Avalonia/Controls/GroupBox.cs)
|
||||
- [GroupBox.Theme.xaml](file://ClassicAssist.Avalonia/Controls/GroupBox.Theme.xaml)
|
||||
- [AvaloniaDispatcher.cs](file://ClassicAssist.Avalonia/AvaloniaDispatcher.cs)
|
||||
- [AvaloniaUIInvoker.cs](file://ClassicAssist.Avalonia/AvaloniaUIInvoker.cs)
|
||||
- [AvalonEditBehaviour.cs](file://ClassicAssist.Avalonia/Misc/AvalonEditBehaviour.cs)
|
||||
- [CloseOnClickBehaviour.cs](file://ClassicAssist.Avalonia/Misc/CloseOnClickBehaviour.cs)
|
||||
- [ComboBoxBehavior.cs](file://ClassicAssist.Avalonia/Misc/ComboBoxBehavior.cs)
|
||||
- [DateTimeToTimeSpanValueConverter.cs](file://ClassicAssist.Avalonia/Misc/DateTimeToTimeSpanValueConverter.cs)
|
||||
- [InverseBooleanValueConverter.cs](file://ClassicAssist.Avalonia/Misc/InverseBooleanValueConverter.cs)
|
||||
- [JoinArrayValueConverter.cs](file://ClassicAssist.Avalonia/Misc/JoinArrayValueConverter.cs)
|
||||
- [ListBoxAutoScrollBehaviour.cs](file://ClassicAssist.Avalonia/Misc/ListBoxAutoScrollBehaviour.cs)
|
||||
- [LockStatusValueConverter.cs](file://ClassicAssist.Avalonia/Misc/LockStatusValueConverter.cs)
|
||||
- [PacketEntryBinaryValueConverter.cs](file://ClassicAssist.Avalonia/Misc/PacketEntryBinaryValueConverter.cs)
|
||||
- [PacketEntryTextValueConverter.cs](file://ClassicAssist.Avalonia/Misc/PacketEntryTextValueConverter.cs)
|
||||
- [StringToResourceConverter.cs](file://ClassicAssist.Avalonia/Misc/StringToResourceConverter.cs)
|
||||
- [WindowInitializedBehaviour.cs](file://ClassicAssist.Avalonia/Misc/WindowInitializedBehaviour.cs)
|
||||
- [WindowResizeBehaviour.cs](file://ClassicAssist.Avalonia/Misc/WindowResizeBehaviour.cs)
|
||||
- [HueEntryToRectanglesValueConverter.cs](file://ClassicAssist.Avalonia/Misc/HueEntryToRectanglesValueConverter.cs)
|
||||
- [BaseViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/BaseViewModel.cs)
|
||||
- [MainWindowViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/MainWindowViewModel.cs)
|
||||
- [IDispatcher.cs](file://ClassicAssist.Shared/IDispatcher.cs)
|
||||
- [IUIInvoker.cs](file://ClassicAssist.Shared/IUIInvoker.cs)
|
||||
|
||||
**Section sources**
|
||||
- [Program.cs](file://ClassicAssist.Avalonia/Program.cs)
|
||||
- [App.axaml](file://ClassicAssist.Avalonia/App.axaml)
|
||||
- [App.axaml.cs](file://ClassicAssist.Avalonia/App.axaml.cs)
|
||||
- [MainWindow.axaml](file://ClassicAssist.Avalonia/Views/MainWindow.axaml)
|
||||
- [MainWindow.axaml.cs](file://ClassicAssist.Avalonia/Views/MainWindow.axaml.cs)
|
||||
- [DialogWindow.axaml](file://ClassicAssist.Avalonia/Views/DialogWindow.axaml)
|
||||
- [DialogWindow.axaml.cs](file://ClassicAssist.Avalonia/Views/DialogWindow.axaml.cs)
|
||||
- [HexDumpControl.axaml](file://ClassicAssist.Avalonia/Controls/HexDumpControl.axaml)
|
||||
- [HexDumpControl.axaml.cs](file://ClassicAssist.Avalonia/Controls/HexDumpControl.axaml.cs)
|
||||
- [EditTextBlock.axaml](file://ClassicAssist.Avalonia/Controls/EditTextBlock.axaml)
|
||||
- [EditTextBlock.axaml.cs](file://ClassicAssist.Avalonia/Controls/EditTextBlock.axaml.cs)
|
||||
- [TextBoxKey.axaml](file://ClassicAssist.Avalonia/Controls/TextBoxKey.axaml)
|
||||
- [TextBoxKey.axaml.cs](file://ClassicAssist.Avalonia/Controls/TextBoxKey.axaml.cs)
|
||||
- [GroupBox.cs](file://ClassicAssist.Avalonia/Controls/GroupBox.cs)
|
||||
- [GroupBox.Theme.xaml](file://ClassicAssist.Avalonia/Controls/GroupBox.Theme.xaml)
|
||||
- [AvaloniaDispatcher.cs](file://ClassicAssist.Avalonia/AvaloniaDispatcher.cs)
|
||||
- [AvaloniaUIInvoker.cs](file://ClassicAssist.Avalonia/AvaloniaUIInvoker.cs)
|
||||
- [AvalonEditBehaviour.cs](file://ClassicAssist.Avalonia/Misc/AvalonEditBehaviour.cs)
|
||||
- [CloseOnClickBehaviour.cs](file://ClassicAssist.Avalonia/Misc/CloseOnClickBehaviour.cs)
|
||||
- [ComboBoxBehavior.cs](file://ClassicAssist.Avalonia/Misc/ComboBoxBehavior.cs)
|
||||
- [DateTimeToTimeSpanValueConverter.cs](file://ClassicAssist.Avalonia/Misc/DateTimeToTimeSpanValueConverter.cs)
|
||||
- [InverseBooleanValueConverter.cs](file://ClassicAssist.Avalonia/Misc/InverseBooleanValueConverter.cs)
|
||||
- [JoinArrayValueConverter.cs](file://ClassicAssist.Avalonia/Misc/JoinArrayValueConverter.cs)
|
||||
- [ListBoxAutoScrollBehaviour.cs](file://ClassicAssist.Avalonia/Misc/ListBoxAutoScrollBehaviour.cs)
|
||||
- [LockStatusValueConverter.cs](file://ClassicAssist.Avalonia/Misc/LockStatusValueConverter.cs)
|
||||
- [PacketEntryBinaryValueConverter.cs](file://ClassicAssist.Avalonia/Misc/PacketEntryBinaryValueConverter.cs)
|
||||
- [PacketEntryTextValueConverter.cs](file://ClassicAssist.Avalonia/Misc/PacketEntryTextValueConverter.cs)
|
||||
- [StringToResourceConverter.cs](file://ClassicAssist.Avalonia/Misc/StringToResourceConverter.cs)
|
||||
- [WindowInitializedBehaviour.cs](file://ClassicAssist.Avalonia/Misc/WindowInitializedBehaviour.cs)
|
||||
- [WindowResizeBehaviour.cs](file://ClassicAssist.Avalonia/Misc/WindowResizeBehaviour.cs)
|
||||
- [HueEntryToRectanglesValueConverter.cs](file://ClassicAssist.Avalonia/Misc/HueEntryToRectanglesValueConverter.cs)
|
||||
- [BaseViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/BaseViewModel.cs)
|
||||
- [MainWindowViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/MainWindowViewModel.cs)
|
||||
- [IDispatcher.cs](file://ClassicAssist.Shared/IDispatcher.cs)
|
||||
- [IUIInvoker.cs](file://ClassicAssist.Shared/IUIInvoker.cs)
|
||||
|
||||
## Core Components
|
||||
- Application bootstrap and DI setup: The application initializes resources, registers services, and creates the main window.
|
||||
- Cross-platform dispatching and UI invocation: Abstractions for thread marshaling and UI operations ensure consistent behavior across platforms.
|
||||
- Custom controls: Specialized controls for hex editing, key input, and text editing with rich UX.
|
||||
- Behaviors and converters: Declarative enhancements to standard controls and data transformations.
|
||||
- Window management: Centralized dialog handling and modal workflows.
|
||||
|
||||
**Section sources**
|
||||
- [App.axaml.cs](file://ClassicAssist.Avalonia/App.axaml.cs)
|
||||
- [AvaloniaDispatcher.cs](file://ClassicAssist.Avalonia/AvaloniaDispatcher.cs)
|
||||
- [AvaloniaUIInvoker.cs](file://ClassicAssist.Avalonia/AvaloniaUIInvoker.cs)
|
||||
- [HexDumpControl.axaml.cs](file://ClassicAssist.Avalonia/Controls/HexDumpControl.axaml.cs)
|
||||
- [EditTextBlock.axaml.cs](file://ClassicAssist.Avalonia/Controls/EditTextBlock.axaml.cs)
|
||||
- [TextBoxKey.axaml.cs](file://ClassicAssist.Avalonia/Controls/TextBoxKey.axaml.cs)
|
||||
- [DialogWindow.axaml.cs](file://ClassicAssist.Avalonia/Views/DialogWindow.axaml.cs)
|
||||
- [DialogWindowViewModel.cs](file://ClassicAssist.Avalonia/ViewModels/DialogWindowViewModel.cs)
|
||||
|
||||
## Architecture Overview
|
||||
The UI follows MVVM with Avalonia:
|
||||
- Views declare layout and bind to ViewModels.
|
||||
- ViewModels expose state and commands, implementing change notifications.
|
||||
- Models encapsulate domain data and business rules.
|
||||
- Cross-platform abstractions isolate platform-specific concerns behind shared interfaces.
|
||||
|
||||
```mermaid
|
||||
classDiagram
|
||||
class IDispatcher {
|
||||
+Invoke(action)
|
||||
+BeginInvoke(action)
|
||||
}
|
||||
class IUIInvoker {
|
||||
+ShowDialog(window, viewModel)
|
||||
+ShowWindow(window, viewModel)
|
||||
}
|
||||
class AvaloniaDispatcher
|
||||
class AvaloniaUIInvoker
|
||||
class BaseViewModel {
|
||||
+NotifyPropertyChanged()
|
||||
}
|
||||
class MainWindowViewModel
|
||||
class MainWindow
|
||||
class DialogWindow
|
||||
class DialogWindowViewModel
|
||||
AvaloniaDispatcher ..|> IDispatcher
|
||||
AvaloniaUIInvoker ..|> IUIInvoker
|
||||
MainWindowViewModel --|> BaseViewModel
|
||||
MainWindow --> MainWindowViewModel : "DataContext"
|
||||
DialogWindow --> DialogWindowViewModel : "DataContext"
|
||||
MainWindow --> AvaloniaUIInvoker : "uses"
|
||||
MainWindow --> AvaloniaDispatcher : "uses"
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [IDispatcher.cs](file://ClassicAssist.Shared/IDispatcher.cs)
|
||||
- [IUIInvoker.cs](file://ClassicAssist.Shared/IUIInvoker.cs)
|
||||
- [AvaloniaDispatcher.cs](file://ClassicAssist.Avalonia/AvaloniaDispatcher.cs)
|
||||
- [AvaloniaUIInvoker.cs](file://ClassicAssist.Avalonia/AvaloniaUIInvoker.cs)
|
||||
- [BaseViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/BaseViewModel.cs)
|
||||
- [MainWindowViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/MainWindowViewModel.cs)
|
||||
- [MainWindow.axaml.cs](file://ClassicAssist.Avalonia/Views/MainWindow.axaml.cs)
|
||||
- [DialogWindow.axaml.cs](file://ClassicAssist.Avalonia/Views/DialogWindow.axaml.cs)
|
||||
- [DialogWindowViewModel.cs](file://ClassicAssist.Avalonia/ViewModels/DialogWindowViewModel.cs)
|
||||
|
||||
## Detailed Component Analysis
|
||||
|
||||
### MVVM Implementation
|
||||
- BaseViewModel provides common change notification and property helpers.
|
||||
- MainWindowViewModel orchestrates UI state and delegates to services via abstractions.
|
||||
- Views bind to properties and commands; they remain thin and declarative.
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant User as "User"
|
||||
participant View as "MainWindow"
|
||||
participant VM as "MainWindowViewModel"
|
||||
participant Invoker as "AvaloniaUIInvoker"
|
||||
participant Dialog as "DialogWindow"
|
||||
User->>View : Click action
|
||||
View->>VM : Execute command
|
||||
VM->>Invoker : ShowDialog(dialogVM)
|
||||
Invoker->>Dialog : Create and show modal
|
||||
Dialog-->>VM : Result callback
|
||||
VM-->>View : Update UI state
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [MainWindow.axaml.cs](file://ClassicAssist.Avalonia/Views/MainWindow.axaml.cs)
|
||||
- [MainWindowViewModel.cs](file://ClassicAssist.Shared/UI/ViewModels/MainWindowViewModel.cs)
|
||||
- [AvaloniaUIInvoker.cs](file://ClassicAssist.Avalonia/AvaloniaUIInvoker.cs)
|
||||
- [DialogWindow.axaml.cs](file://ClassicAssist.Avalonia/Views/DialogWindow.axaml.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)
|
||||
- [MainWindow.axaml.cs](file://ClassicAssist.Avalonia/Views/MainWindow.axaml.cs)
|
||||
- [DialogWindow.axaml.cs](file://ClassicAssist.Avalonia/Views/DialogWindow.axaml.cs)
|
||||
- [DialogWindowViewModel.cs](file://ClassicAssist.Avalonia/ViewModels/DialogWindowViewModel.cs)
|
||||
|
||||
### Cross-Platform UI Abstraction Layer
|
||||
- IDispatcher and IUIInvoker define contracts for UI threading and window/dialog operations.
|
||||
- AvaloniaDispatcher implements dispatcher semantics for Avalonia’s UI thread.
|
||||
- AvaloniaUIInvoker implements cross-platform dialog/window presentation using Avalonia primitives.
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Start(["UI Operation"]) --> CheckThread["Check current thread"]
|
||||
CheckThread --> IsUIThread{"Is UI thread?"}
|
||||
IsUIThread --> |Yes| Execute["Execute directly"]
|
||||
IsUIThread --> |No| Marshal["Marshal to UI thread"]
|
||||
Marshal --> Execute
|
||||
Execute --> End(["Done"])
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [IDispatcher.cs](file://ClassicAssist.Shared/IDispatcher.cs)
|
||||
- [AvaloniaDispatcher.cs](file://ClassicAssist.Avalonia/AvaloniaDispatcher.cs)
|
||||
- [IUIInvoker.cs](file://ClassicAssist.Shared/IUIInvoker.cs)
|
||||
- [AvaloniaUIInvoker.cs](file://ClassicAssist.Avalonia/AvaloniaUIInvoker.cs)
|
||||
|
||||
**Section sources**
|
||||
- [IDispatcher.cs](file://ClassicAssist.Shared/IDispatcher.cs)
|
||||
- [IUIInvoker.cs](file://ClassicAssist.Shared/IUIInvoker.cs)
|
||||
- [AvaloniaDispatcher.cs](file://ClassicAssist.Avalonia/AvaloniaDispatcher.cs)
|
||||
- [AvaloniaUIInvoker.cs](file://ClassicAssist.Avalonia/AvaloniaUIInvoker.cs)
|
||||
|
||||
### Theme System, Styling, and Resources
|
||||
- Global styles and themes are defined in XAML resources and merged at app startup.
|
||||
- Control-specific themes (e.g., GroupBox) provide default look-and-feel.
|
||||
- Resource dictionaries centralize colors, icons, and reusable styles.
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
AppRes["App.axaml resources"] --> Merged["Merged Dictionaries"]
|
||||
Merged --> Themes["Themes (Dark/Light)"]
|
||||
Merged --> Styles["Global Styles"]
|
||||
Merged --> Icons["Icons & Assets"]
|
||||
Themes --> Controls["Controls (e.g., GroupBox)"]
|
||||
Styles --> Controls
|
||||
Icons --> Controls
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [App.axaml](file://ClassicAssist.Avalonia/App.axaml)
|
||||
- [GroupBox.Theme.xaml](file://ClassicAssist.Avalonia/Controls/GroupBox.Theme.xaml)
|
||||
|
||||
**Section sources**
|
||||
- [App.axaml](file://ClassicAssist.Avalonia/App.axaml)
|
||||
- [GroupBox.Theme.xaml](file://ClassicAssist.Avalonia/Controls/GroupBox.Theme.xaml)
|
||||
|
||||
### Custom Controls Library
|
||||
- HexDumpControl: A specialized control for binary/hex inspection with efficient rendering patterns suitable for large datasets.
|
||||
- EditTextBlock: An editable text block combining read-only display with inline editing capabilities.
|
||||
- TextBoxKey: A key-binding capture control for hotkey configuration.
|
||||
- GroupBox: A themed container control with consistent styling.
|
||||
|
||||
```mermaid
|
||||
classDiagram
|
||||
class HexDumpControl {
|
||||
+Data
|
||||
+Refresh()
|
||||
}
|
||||
class EditTextBlock {
|
||||
+Text
|
||||
+EditMode(bool)
|
||||
}
|
||||
class TextBoxKey {
|
||||
+KeyBinding
|
||||
+CaptureKey()
|
||||
}
|
||||
class GroupBox {
|
||||
+Header
|
||||
}
|
||||
HexDumpControl <.. EditTextBlock : "used by"
|
||||
HexDumpControl <.. TextBoxKey : "used by"
|
||||
GroupBox <.. HexDumpControl : "wraps"
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [HexDumpControl.axaml.cs](file://ClassicAssist.Avalonia/Controls/HexDumpControl.axaml.cs)
|
||||
- [EditTextBlock.axaml.cs](file://ClassicAssist.Avalonia/Controls/EditTextBlock.axaml.cs)
|
||||
- [TextBoxKey.axaml.cs](file://ClassicAssist.Avalonia/Controls/TextBoxKey.axaml.cs)
|
||||
- [GroupBox.cs](file://ClassicAssist.Avalonia/Controls/GroupBox.cs)
|
||||
|
||||
**Section sources**
|
||||
- [HexDumpControl.axaml](file://ClassicAssist.Avalonia/Controls/HexDumpControl.axaml)
|
||||
- [HexDumpControl.axaml.cs](file://ClassicAssist.Avalonia/Controls/HexDumpControl.axaml.cs)
|
||||
- [EditTextBlock.axaml](file://ClassicAssist.Avalonia/Controls/EditTextBlock.axaml)
|
||||
- [EditTextBlock.axaml.cs](file://ClassicAssist.Avalonia/Controls/EditTextBlock.axaml.cs)
|
||||
- [TextBoxKey.axaml](file://ClassicAssist.Avalonia/Controls/TextBoxKey.axaml)
|
||||
- [TextBoxKey.axaml.cs](file://ClassicAssist.Avalonia/Controls/TextBoxKey.axaml.cs)
|
||||
- [GroupBox.cs](file://ClassicAssist.Avalonia/Controls/GroupBox.cs)
|
||||
- [GroupBox.Theme.xaml](file://ClassicAssist.Avalonia/Controls/GroupBox.Theme.xaml)
|
||||
|
||||
### Binding System, Value Converters, and Behaviors
|
||||
- ValueConverters transform data for display (e.g., DateTime to TimeSpan, lock status, packet entries, string-to-resource lookup).
|
||||
- Behaviors add interactivity without code-behind (e.g., auto-scroll, close-on-click, combo box behavior, window resize/initialized hooks).
|
||||
- Bindings connect View properties to ViewModel commands and properties.
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Data["ViewModel Property"] --> Converter["ValueConverter"]
|
||||
Converter --> ViewProp["View Property"]
|
||||
Behavior["Behavior"] --> View["View Element"]
|
||||
Behavior --> Event["Event/Action"]
|
||||
Event --> Command["Command in ViewModel"]
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [DateTimeToTimeSpanValueConverter.cs](file://ClassicAssist.Avalonia/Misc/DateTimeToTimeSpanValueConverter.cs)
|
||||
- [InverseBooleanValueConverter.cs](file://ClassicAssist.Avalonia/Misc/InverseBooleanValueConverter.cs)
|
||||
- [JoinArrayValueConverter.cs](file://ClassicAssist.Avalonia/Misc/JoinArrayValueConverter.cs)
|
||||
- [LockStatusValueConverter.cs](file://ClassicAssist.Avalonia/Misc/LockStatusValueConverter.cs)
|
||||
- [PacketEntryBinaryValueConverter.cs](file://ClassicAssist.Avalonia/Misc/PacketEntryBinaryValueConverter.cs)
|
||||
- [PacketEntryTextValueConverter.cs](file://ClassicAssist.Avalonia/Misc/PacketEntryTextValueConverter.cs)
|
||||
- [StringToResourceConverter.cs](file://ClassicAssist.Avalonia/Misc/StringToResourceConverter.cs)
|
||||
- [HueEntryToRectanglesValueConverter.cs](file://ClassicAssist.Avalonia/Misc/HueEntryToRectanglesValueConverter.cs)
|
||||
- [AvalonEditBehaviour.cs](file://ClassicAssist.Avalonia/Misc/AvalonEditBehaviour.cs)
|
||||
- [CloseOnClickBehaviour.cs](file://ClassicAssist.Avalonia/Misc/CloseOnClickBehaviour.cs)
|
||||
- [ComboBoxBehavior.cs](file://ClassicAssist.Avalonia/Misc/ComboBoxBehavior.cs)
|
||||
- [ListBoxAutoScrollBehaviour.cs](file://ClassicAssist.Avalonia/Misc/ListBoxAutoScrollBehaviour.cs)
|
||||
- [WindowInitializedBehaviour.cs](file://ClassicAssist.Avalonia/Misc/WindowInitializedBehaviour.cs)
|
||||
- [WindowResizeBehaviour.cs](file://ClassicAssist.Avalonia/Misc/WindowResizeBehaviour.cs)
|
||||
|
||||
**Section sources**
|
||||
- [DateTimeToTimeSpanValueConverter.cs](file://ClassicAssist.Avalonia/Misc/DateTimeToTimeSpanValueConverter.cs)
|
||||
- [InverseBooleanValueConverter.cs](file://ClassicAssist.Avalonia/Misc/InverseBooleanValueConverter.cs)
|
||||
- [JoinArrayValueConverter.cs](file://ClassicAssist.Avalonia/Misc/JoinArrayValueConverter.cs)
|
||||
- [LockStatusValueConverter.cs](file://ClassicAssist.Avalonia/Misc/LockStatusValueConverter.cs)
|
||||
- [PacketEntryBinaryValueConverter.cs](file://ClassicAssist.Avalonia/Misc/PacketEntryBinaryValueConverter.cs)
|
||||
- [PacketEntryTextValueConverter.cs](file://ClassicAssist.Avalonia/Misc/PacketEntryTextValueConverter.cs)
|
||||
- [StringToResourceConverter.cs](file://ClassicAssist.Avalonia/Misc/StringToResourceConverter.cs)
|
||||
- [HueEntryToRectanglesValueConverter.cs](file://ClassicAssist.Avalonia/Misc/HueEntryToRectanglesValueConverter.cs)
|
||||
- [AvalonEditBehaviour.cs](file://ClassicAssist.Avalonia/Misc/AvalonEditBehaviour.cs)
|
||||
- [CloseOnClickBehaviour.cs](file://ClassicAssist.Avalonia/Misc/CloseOnClickBehaviour.cs)
|
||||
- [ComboBoxBehavior.cs](file://ClassicAssist.Avalonia/Misc/ComboBoxBehavior.cs)
|
||||
- [ListBoxAutoScrollBehaviour.cs](file://ClassicAssist.Avalonia/Misc/ListBoxAutoScrollBehaviour.cs)
|
||||
- [WindowInitializedBehaviour.cs](file://ClassicAssist.Avalonia/Misc/WindowInitializedBehaviour.cs)
|
||||
- [WindowResizeBehaviour.cs](file://ClassicAssist.Avalonia/Misc/WindowResizeBehaviour.cs)
|
||||
|
||||
### Window Management and Modal Dialogs
|
||||
- DialogWindow provides a standardized modal dialog shell.
|
||||
- DialogWindowViewModel coordinates dialog content and results.
|
||||
- AvaloniaUIInvoker handles creation and lifetime of dialogs/windows uniformly across platforms.
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Caller as "Caller ViewModel"
|
||||
participant Invoker as "AvaloniaUIInvoker"
|
||||
participant Win as "DialogWindow"
|
||||
participant VM as "DialogWindowViewModel"
|
||||
Caller->>Invoker : ShowDialog(VM)
|
||||
Invoker->>Win : Instantiate and set DataContext
|
||||
Win->>VM : Initialize and render
|
||||
VM-->>Caller : Return result on close
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [AvaloniaUIInvoker.cs](file://ClassicAssist.Avalonia/AvaloniaUIInvoker.cs)
|
||||
- [DialogWindow.axaml.cs](file://ClassicAssist.Avalonia/Views/DialogWindow.axaml.cs)
|
||||
- [DialogWindowViewModel.cs](file://ClassicAssist.Avalonia/ViewModels/DialogWindowViewModel.cs)
|
||||
|
||||
**Section sources**
|
||||
- [DialogWindow.axaml](file://ClassicAssist.Avalonia/Views/DialogWindow.axaml)
|
||||
- [DialogWindow.axaml.cs](file://ClassicAssist.Avalonia/Views/DialogWindow.axaml.cs)
|
||||
- [DialogWindowViewModel.cs](file://ClassicAssist.Avalonia/ViewModels/DialogWindowViewModel.cs)
|
||||
- [AvaloniaUIInvoker.cs](file://ClassicAssist.Avalonia/AvaloniaUIInvoker.cs)
|
||||
|
||||
## Dependency Analysis
|
||||
- Views depend on ViewModels through DataContext bindings.
|
||||
- ViewModels depend on shared base classes and abstractions (dispatcher, invoker).
|
||||
- Custom controls encapsulate complex UI logic and can be reused across views.
|
||||
- Behaviors and converters are loosely coupled and applied declaratively.
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
Views["Views"] --> VMs["ViewModels"]
|
||||
VMs --> SharedVM["BaseViewModel"]
|
||||
VMs --> Abstractions["IDispatcher / IUIInvoker"]
|
||||
Views --> Controls["Custom Controls"]
|
||||
Views --> Misc["Behaviors & Converters"]
|
||||
Controls --> Theming["Theme Resources"]
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [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)
|
||||
- [IDispatcher.cs](file://ClassicAssist.Shared/IDispatcher.cs)
|
||||
- [IUIInvoker.cs](file://ClassicAssist.Shared/IUIInvoker.cs)
|
||||
- [HexDumpControl.axaml.cs](file://ClassicAssist.Avalonia/Controls/HexDumpControl.axaml.cs)
|
||||
- [GroupBox.Theme.xaml](file://ClassicAssist.Avalonia/Controls/GroupBox.Theme.xaml)
|
||||
|
||||
**Section sources**
|
||||
- [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)
|
||||
- [IDispatcher.cs](file://ClassicAssist.Shared/IDispatcher.cs)
|
||||
- [IUIInvoker.cs](file://ClassicAssist.Shared/IUIInvoker.cs)
|
||||
- [HexDumpControl.axaml.cs](file://ClassicAssist.Avalonia/Controls/HexDumpControl.axaml.cs)
|
||||
- [GroupBox.Theme.xaml](file://ClassicAssist.Avalonia/Controls/GroupBox.Theme.xaml)
|
||||
|
||||
## Performance Considerations
|
||||
- Virtualization: Prefer virtualizing panels for large lists to reduce memory and improve scroll performance.
|
||||
- Incremental updates: Batch collection changes and avoid excessive property notifications during bulk operations.
|
||||
- Efficient rendering: Use custom controls like HexDumpControl optimized for large binary data; avoid heavy per-item visuals.
|
||||
- Debounced input: For text inputs and key capture, debounce or throttle events to prevent UI thrashing.
|
||||
- Resource caching: Cache computed values and converter outputs where appropriate to minimize recomputation.
|
||||
- Responsive design: Use flexible layouts and responsive sizing to adapt to different screen sizes and DPI settings.
|
||||
|
||||
[No sources needed since this section provides general guidance]
|
||||
|
||||
## Troubleshooting Guide
|
||||
- Threading issues: Ensure UI updates occur on the UI thread using the dispatcher abstraction.
|
||||
- Dialog not closing: Verify that dialog results are properly returned and handled by the caller ViewModel.
|
||||
- Style conflicts: Check resource dictionary merge order and control theme overrides.
|
||||
- Converter errors: Validate input types and handle nulls gracefully in converters.
|
||||
- Memory growth: Monitor large collections and enable virtualization; dispose of unused resources.
|
||||
|
||||
**Section sources**
|
||||
- [AvaloniaDispatcher.cs](file://ClassicAssist.Avalonia/AvaloniaDispatcher.cs)
|
||||
- [AvaloniaUIInvoker.cs](file://ClassicAssist.Avalonia/AvaloniaUIInvoker.cs)
|
||||
- [DialogWindow.axaml.cs](file://ClassicAssist.Avalonia/Views/DialogWindow.axaml.cs)
|
||||
- [DialogWindowViewModel.cs](file://ClassicAssist.Avalonia/ViewModels/DialogWindowViewModel.cs)
|
||||
- [GroupBox.Theme.xaml](file://ClassicAssist.Avalonia/Controls/GroupBox.Theme.xaml)
|
||||
- [DateTimeToTimeSpanValueConverter.cs](file://ClassicAssist.Avalonia/Misc/DateTimeToTimeSpanValueConverter.cs)
|
||||
- [InverseBooleanValueConverter.cs](file://ClassicAssist.Avalonia/Misc/InverseBooleanValueConverter.cs)
|
||||
- [JoinArrayValueConverter.cs](file://ClassicAssist.Avalonia/Misc/JoinArrayValueConverter.cs)
|
||||
- [LockStatusValueConverter.cs](file://ClassicAssist.Avalonia/Misc/LockStatusValueConverter.cs)
|
||||
- [PacketEntryBinaryValueConverter.cs](file://ClassicAssist.Avalonia/Misc/PacketEntryBinaryValueConverter.cs)
|
||||
- [PacketEntryTextValueConverter.cs](file://ClassicAssist.Avalonia/Misc/PacketEntryTextValueConverter.cs)
|
||||
- [StringToResourceConverter.cs](file://ClassicAssist.Avalonia/Misc/StringToResourceConverter.cs)
|
||||
- [HueEntryToRectanglesValueConverter.cs](file://ClassicAssist.Avalonia/Misc/HueEntryToRectanglesValueConverter.cs)
|
||||
|
||||
## Conclusion
|
||||
ClassicAssist.Avalonia implements a clean MVVM architecture with Avalonia UI, providing robust cross-platform abstractions, a comprehensive theming system, reusable custom controls, and a rich set of behaviors and converters. The design emphasizes separation of concerns, testability, and performance, making it suitable for complex desktop applications requiring responsive and maintainable UI layers.
|
||||
Reference in New Issue
Block a user