This commit is contained in:
2026-04-17 14:55:32 -04:00
commit bc3ac1d4c9
18017 changed files with 4371742 additions and 0 deletions

View File

@@ -0,0 +1,108 @@
---
source_files:
- DataPRO/Modules/Groups/GroupChannelList/GroupChannelListModule.cs
generated_at: "2026-04-16T04:44:06.724645+00:00"
model: "Qwen/Qwen3-Coder-Next-FP8"
schema_version: 1
sha256: "3066a4421ff164bf"
---
# GroupChannelList
## Documentation: `GroupChannelListModule`
---
### 1. Purpose
The `GroupChannelListModule` is a Prism-based modular component responsible for registering the view and view model types associated with the group channel list UI functionality. It integrates into the applications modular architecture by implementing `IModule`, and uses Unity as its dependency injection container to register key UI components (`IGroupChannelListViewModel`, `IGroupChannelListView`, and `IGroupChannelSettingsListView`) as singleton services. Additionally, it exposes assembly-level metadata via custom attributes (`GroupChannelListModuleNameAttribute`, `GroupChannelListModuleImageAttribute`) to support dynamic discovery and UI presentation (e.g., in a module summary screen), including image, name, group, and region information.
---
### 2. Public Interface
#### `GroupChannelListModule`
- **`public GroupChannelListModule(IUnityContainer unityContainer)`**
Constructor. Injects the Unity container used for type registration.
- **`public void Initialize()`**
Registers three interfaces to their concrete implementations as singletons in the Unity container:
- `IGroupChannelListViewModel``GroupChannelListViewModel`
- `IGroupChannelListView``GroupChannelListView`
- `IGroupChannelSettingsListView``GroupChannelSettingsListView`
This method is called both directly by the constructors usage context (via `RegisterTypes`) and explicitly during Prism module initialization.
- **`public void OnInitialized(IContainerProvider containerProvider)`**
Currently empty; no logic implemented.
- **`public void RegisterTypes(IContainerRegistry containerRegistry)`**
Delegates to `Initialize()` (note: despite using `IContainerRegistry`, it internally uses `_unityContainer`, implying a potential mismatch or legacy pattern).
#### `GroupChannelListModuleNameAttribute`
- **`public GroupChannelListModuleNameAttribute()` / `GroupChannelListModuleNameAttribute(string s)`**
Constructor; ignores the `string s` parameter. Sets `AssemblyName` to `AssemblyNames.GroupChannelList.ToString()`.
- **`public override string AssemblyName { get; }`**
Returns `"GroupChannelList"` (value of `AssemblyNames.GroupChannelList.ToString()`).
- **`public override Type GetAttributeType()`**
Returns `typeof(TextAttribute)`.
- **`public override string GetAssemblyName()`**
Returns the value of `AssemblyName`.
#### `GroupChannelListModuleImageAttribute`
- **`public GroupChannelListModuleImageAttribute()` / `GroupChannelListModuleImageAttribute(string s)`**
Constructor; initializes `_img` by calling `AssemblyInfo.GetImage("GroupChannelList")`.
- **`public override BitmapImage AssemblyImage { get; }`**
Returns the image retrieved via `AssemblyInfo.GetImage("GroupChannelList")`.
- **`public override BitmapImage GetAssemblyImage()`**
Returns `AssemblyImage`.
- **`public override string AssemblyName { get; }`**
Returns `"GroupChannelList"`.
- **`public override string GetAssemblyName()`**
Returns `AssemblyName`.
- **`public override string AssemblyGroup { get; }`**
Returns `"Prepare"` (value of `eAssemblyGroups.Prepare.ToString()`).
- **`public override string GetAssemblyGroup()`**
Returns `AssemblyGroup`.
- **`public override eAssemblyRegion AssemblyRegion { get; }`**
Returns `eAssemblyRegion.GroupChannelListRegion`.
- **`public override eAssemblyRegion GetAssemblyRegion()`**
Returns `AssemblyRegion`.
- **`public override Type GetAttributeType()`**
Returns `typeof(ImageAttribute)`.
---
### 3. Invariants
- The module **must** be loaded in a Prism-based application using Unity as the DI container (as it directly uses `IUnityContainer` and `Unity` namespace).
- `Initialize()` must be called exactly once during module initialization to register the three types as singletons.
- `AssemblyNames.GroupChannelList`, `eAssemblyGroups.Prepare`, and `eAssemblyRegion.GroupChannelListRegion` must be defined elsewhere (in `DTS.Common` or `DTS.Common.Interface`) and must have consistent string/enum values; otherwise, runtime errors may occur (e.g., `AssemblyInfo.GetImage()` failure, region resolution failure).
- The `AssemblyImage` property assumes `AssemblyInfo.GetImage("GroupChannelList")` returns a valid `BitmapImage`; if not, null or exception may result (no null-check observed).
- The `RegisterTypes` method uses `IContainerRegistry`, but internally calls `Initialize()`, which uses `_unityContainer` (a `IUnityContainer`). This implies either:
- `IContainerRegistry` wraps `IUnityContainer` (e.g., via Prism.Unity integration), or
- A design inconsistency (see *Gotchas*).
---
### 4. Dependencies
#### Dependencies *of* this module:
- `DTS.Common` (specifically `AssemblyNames.GroupChannelList`, `eAssemblyGroups`, `eAssemblyRegion`, and `AssemblyInfo.GetImage(...)`)
- `DTS.Common.Interface.Groups.GroupChannelList` (for `IGroupChannelListViewModel`, `IGroupChannelListView`, `IGroupChannelSettingsListView`)
- `Prism.Modularity` (`IModule`, `ModuleAttribute`)
- `Prism.Ioc` (`IContainerProvider`, `IContainerRegistry`)
- `Unity` (`IUnityContainer`)
- `System.Windows.Media.Imaging` (`BitmapImage`)
#### Dependencies *on* this module:
- The host application (or other modules) must resolve `IGroupChannelListViewModel`, `IGroupChannelListView`, and `IGroupChannelSettingsListView` via DI after module initialization.
- UI regions (e.g., `GroupChannelListRegion`) must be defined elsewhere (e.g., in a shell or region manager) for views to be injected.
- The modules metadata attributes (`GroupChannelListModuleNameAttribute`, `GroupChannelListModuleImageAttribute`) are used by the host applications module discovery/UI logic (e.g., to populate a summary screen), implying external consumers rely on the attribute metadata structure.
---
### 5. Gotchas
- **`RegisterTypes` vs `Initialize` mismatch**: `RegisterTypes` accepts `IContainerRegistry` (Prisms abstraction), but `Initialize()` uses `_unityContainer` (`IUnityContainer`). If `IContainerRegistry` does not expose the underlying Unity container (or if `Initialize()` is called before `_unityContainer` is set), this could lead to incorrect registration or null reference. *This suggests potential tech debt or reliance on Prism.Unitys internal bridging.*
- **No null safety in `AssemblyImage`**: `AssemblyInfo.GetImage(...)` may return `null` if the image resource is missing or misnamed, but no defensive handling is present.
- **Unused constructor parameters**: The `string s` parameter in both attribute constructors is ignored, which may confuse developers expecting configurability.
- **Hardcoded string `"GroupChannelList"`**: Used in multiple places (`AssemblyNames.GroupChannelList.ToString()`, `AssemblyInfo.GetImage(...)`, `AssemblyGroup`). A typo or rename in `AssemblyNames.GroupChannelList` or `AssemblyInfo` would cause silent failures.
- **`OnInitialized` is empty**: Suggests incomplete implementation or future extensibility point.
- **No validation of view/view-model registration**: Assumes `IGroupChannelListViewModel`, etc., are implemented by `GroupChannelListViewModel`, etc., with compatible lifetimes (singletons). Misconfiguration here could cause runtime issues.
- **No documentation on `IGroupChannelListViewModel`/`IGroupChannelListView` interfaces**: Their contract (methods, events, properties) is not visible in this file.
None identified beyond the above.

View File

@@ -0,0 +1,120 @@
---
source_files:
- DataPRO/Modules/Groups/GroupChannelList/Converters/BooleanToWidthConverter.cs
- DataPRO/Modules/Groups/GroupChannelList/Converters/SensorIdBackgroundConverter.cs
generated_at: "2026-04-16T04:46:41.293810+00:00"
model: "Qwen/Qwen3-Coder-Next-FP8"
schema_version: 1
sha256: "b2bc34d167251ea0"
---
# Converters
## Documentation: `GroupChannelList.Converters` Module
### 1. Purpose
This module provides WPF value converters used for UI data binding in the `GroupChannelList` module. Specifically, it enables conditional presentation logic—converting boolean values to UI properties such as width (for collapsing/expanding UI elements) and background color (for highlighting sensor-related items). These converters facilitate declarative UI behavior in XAML without requiring additional view-model logic.
---
### 2. Public Interface
#### `BooleanToWidthConverter`
- **Namespace**: `GroupChannelList.Converters`
- **Type**: `class` implementing `IValueConverter`
- **Method**:
```csharp
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
```
- **Behavior**: Converts a `bool` input to a `double` width.
- If `value` is `null`, returns `0`.
- If `parameter` is provided and parses successfully as a `double`, uses that value for `true`; otherwise defaults to `double.NaN`.
- Returns the parsed width for `true`, and `0` for `false`.
- **Example usage in XAML**:
```xaml
Width="{Binding IsSensorActive, Converter={StaticResource BooleanToWidthConverter}, ConverterParameter=100}"
```
- **Method**:
```csharp
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
```
- **Behavior**: Always throws `NotImplementedException`. One-way conversion only.
#### `SensorIdBackgroundConverter`
- **Namespace**: `GroupChannelList.Converters`
- **Type**: `class` implementing `IValueConverter`
- **Field**:
```csharp
private static SolidColorBrush SensorIdBrush = new SolidColorBrush(Color.FromArgb(0xFF, 0xE3, 0xFB, 0xE1));
```
- A frozen `SolidColorBrush` with ARGB color `(0xFF, 0xE3, 0xFB, 0xE1)` (light green, #E3FBE1).
- **Method**:
```csharp
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
```
- **Behavior**: Converts a `bool` input to a `Brush`.
- If `value` is `null`, returns `Brushes.Transparent`.
- If `value` is `true`, returns `SensorIdBrush` (frozen for performance).
- If `value` is `false`, returns `Brushes.Transparent`.
- Any exception during conversion logs the message via `Trace.WriteLine` and returns `Brushes.Transparent`.
- **Example usage in XAML**:
```xaml
Background="{Binding HasSensorId, Converter={StaticResource SensorIdBackgroundConverter}}"
```
- **Method**:
```csharp
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
```
- **Behavior**: Always throws `NotImplementedException`. One-way conversion only.
---
### 3. Invariants
- **`BooleanToWidthConverter`**:
- Output is always `0` or a `double` (including `double.NaN` if `parameter` parsing fails).
- `parameter` is optional; if missing or invalid, `double.NaN` is used for `true`.
- No validation on `value` beyond null-checking; non-`bool` values will cause a runtime `InvalidCastException` (not caught).
- **`SensorIdBackgroundConverter`**:
- `SensorIdBrush` is frozen after first use (via `.Freeze()`) to ensure thread-safety and performance.
- Output is always a `Brush`; specifically `Brushes.Transparent` or `SensorIdBrush`.
- Null or non-`bool` inputs are handled gracefully (return `Brushes.Transparent`), but exceptions during conversion are silently logged.
---
### 4. Dependencies
- **Dependencies on external frameworks**:
- `System.Windows.Data` (WPF `IValueConverter` interface)
- `System.Windows.Media` (`SolidColorBrush`, `Brushes`)
- `System.Diagnostics` (`Trace`)
- `System.Globalization` (`CultureInfo`)
- **Dependencies on other modules**:
- None inferred from source (no internal project references in imports).
- Used by XAML views in the `GroupChannelList` module (inferred from namespace path).
- **Depended upon by**:
- XAML UI elements in `DataPRO.Modules.Groups.GroupChannelList` (e.g., `GroupChannelListView.xaml`), where these converters are referenced as static resources.
---
### 5. Gotchas
- **`BooleanToWidthConverter`**:
- `ConvertBack` is unimplemented—cannot be used in two-way bindings.
- `parameter` parsing is silent: invalid values (e.g., `"abc"`) result in `double.NaN` without error.
- Non-`bool` inputs (e.g., `null`, `int`, `string`) will throw `InvalidCastException` at runtime (not caught).
- **`SensorIdBackgroundConverter`**:
- `SensorIdBrush` is shared and frozen *after first use*; if frozen prematurely (e.g., before first conversion), subsequent calls are safe but the freeze is redundant.
- Exception handling is minimal: only logs to `Trace`, no user-facing error.
- Assumes `value` is `bool`; non-`bool` inputs (e.g., `null`, `int`) will throw `InvalidCastException` (not caught).
- **Both converters**:
- One-way only (`ConvertBack` throws `NotImplementedException`).
- No support for culture-specific formatting (uses default `CultureInfo` behavior).
- No documentation of expected `targetType` constraints (assumes WPF expects `double`/`Brush` outputs).
None identified beyond the above.

View File

@@ -0,0 +1,60 @@
---
source_files:
- DataPRO/Modules/Groups/GroupChannelList/Properties/Settings.Designer.cs
- DataPRO/Modules/Groups/GroupChannelList/Properties/AssemblyInfo.cs
generated_at: "2026-04-16T04:46:44.908575+00:00"
model: "Qwen/Qwen3-Coder-Next-FP8"
schema_version: 1
sha256: "068d1583fe59a4bb"
---
# Properties
## Documentation Page: `GroupChannelList.Properties.Settings`
---
### 1. **Purpose**
This module defines a strongly-typed settings class (`GroupChannelList.Properties.Settings`) for the `GroupChannelList` assembly, enabling centralized access to application-level configuration values via the .NET `ApplicationSettingsBase` infrastructure. It exists solely to expose a thread-safe singleton instance (`Default`) for reading (and potentially writing, though not evident here) user- or application-scoped settings—though no actual settings properties are declared in the provided source, indicating either a minimal placeholder or that settings are defined elsewhere (e.g., in `Settings.settings` or `App.config`).
---
### 2. **Public Interface**
The only public API surface exposed is:
- **`Settings.Default`**
- **Type**: `Settings` (a singleton instance)
- **Signature**: `public static Settings Default { get; }`
- **Behavior**: Returns the synchronized singleton instance of the `Settings` class, derived from `ApplicationSettingsBase`. This instance provides access to configuration properties (though none are visible in the provided source). Thread-safety is ensured via `ApplicationSettingsBase.Synchronized()`.
> **Note**: No additional public properties, methods, or fields are declared in the `Settings` class within the provided source. All settings (if any) are implicitly inherited from `ApplicationSettingsBase` and defined externally (e.g., in designer-generated or config files not included here).
---
### 3. **Invariants**
- The `Settings` class is **sealed** and **partial**, with auto-generated code marked with `<auto-generated>` and attributed with `CompilerGeneratedAttribute` and `GeneratedCodeAttribute`.
- The `Default` property returns a **synchronized singleton**, implying thread-safe access to settings (via `ApplicationSettingsBase.Synchronized`).
- The class resides in the `GroupChannelList.Properties` namespace, consistent with .NET conventions for strongly-typed settings.
- No runtime validation or custom logic is present in the provided source—behavior depends entirely on settings defined externally.
---
### 4. **Dependencies**
- **Depends on**:
- `System.Configuration` (specifically `ApplicationSettingsBase`)
- `System.Runtime.CompilerServices` (for `CompilerGeneratedAttribute`)
- `System.CodeDom.Compiler` (for `GeneratedCodeAttribute`)
- **Depended upon by**:
- Other modules in the `DataPRO` solution (e.g., `GroupChannelList` UI or logic layers) that consume `GroupChannelList.Properties.Settings.Default` to read configuration values.
- The .NET configuration system (e.g., `App.config` or `user.config` files) that backs the settings properties.
---
### 5. **Gotchas**
- **Settings are not defined here**: The `Settings` class contains no explicit property declarations (e.g., `public string SomeSetting { get; set; }`). Actual settings must be defined in the corresponding `.settings` file (e.g., `Settings.settings`) and regenerated—this file is purely the generated wrapper.
- **No write support evident**: While `ApplicationSettingsBase` supports setting values, the provided source does not confirm whether write operations are enabled or used.
- **Thread-safety caveat**: The `Synchronized()` wrapper ensures thread-safe *access*, but atomicity of multi-step operations (e.g., read-modify-write) is not guaranteed.
- **Versioning risk**: The assembly version is fixed at `1.0.0.0` (both `AssemblyVersion` and `AssemblyFileVersion`), which may complicate upgrades or settings migration if settings evolve.
- **Auto-generated warning**: Manual edits to this file will be overwritten on rebuild—settings must be modified via the Visual Studio Settings Designer or `App.config`.
> **None identified from source alone** beyond the above.

View File

@@ -0,0 +1,77 @@
---
source_files:
- DataPRO/Modules/Groups/GroupChannelList/Resources/TranslateExtension.cs
- DataPRO/Modules/Groups/GroupChannelList/Resources/StringResources.Designer.cs
generated_at: "2026-04-16T04:46:25.212575+00:00"
model: "Qwen/Qwen3-Coder-Next-FP8"
schema_version: 1
sha256: "5dba578a99876d46"
---
# Documentation: `TranslateExtension` Markup Extension
## 1. Purpose
This module provides a WPF `MarkupExtension` (`TranslateExtension`) that enables localization of UI strings directly in XAML by resolving resource keys against a strongly-typed resource class (`StringResources`). It serves as a bridge between declarative UI markup and localized string resources, allowing developers to bind text content (e.g., labels, tooltips, headers) to culture-sensitive values without writing code-behind. Its role is critical for internationalization of the `GroupChannelList` module, ensuring that user-facing text adapts to the current UI culture.
## 2. Public Interface
### `TranslateExtension` class
- **Namespace**: `GroupChannelList`
- **Base class**: `System.Windows.Markup.MarkupExtension`
- **Attribute**: `[MarkupExtensionReturnType(typeof(string))]`
#### Constructor
```csharp
public TranslateExtension(string key)
```
- **Parameters**:
- `key` (`string`): The resource key (e.g., `"ChannelName"`) used to look up a localized string in `StringResources`.
- **Behavior**: Stores the key for later resolution during `ProvideValue`.
#### `ProvideValue` method
```csharp
public override object ProvideValue(IServiceProvider serviceProvider)
```
- **Parameters**:
- `serviceProvider` (`IServiceProvider`): WPF service provider (unused in current implementation).
- **Returns**:
- `string`: The localized string if the key exists and is non-null/non-empty; otherwise, one of two fallback values:
- If `_key` is `null` or empty → returns `"#stringnotfound#"`
- If `StringResources.ResourceManager.GetString(_key)` returns `null` → returns `"#stringnotfound# " + _key` (e.g., `"#stringnotfound# ChannelName"`)
- **Behavior**: Performs a culture-aware lookup using `StringResources.ResourceManager.GetString(_key)`.
## 3. Invariants
- **Key must be a valid resource key**: The `_key` passed to the constructor must match a property name in `StringResources` (e.g., `"ChannelName"`, `"AnalogParameters_Range"`). Mismatched keys will result in the fallback `"#stringnotfound# <key>"` string.
- **Null/empty key handling**: If `_key` is `null` or `string.Empty`, the extension *always* returns `"#stringnotfound#"` (no concatenation with key).
- **No culture override via extension**: The extension does not expose or support overriding the culture used for lookup; it relies on the current `Thread.CurrentUICulture` via `StringResources.Culture`.
- **No side effects**: `ProvideValue` is pure—no state mutation or I/O occurs beyond the resource lookup.
## 4. Dependencies
### Dependencies *of* this module:
- **`System.Windows.Markup`**: Required for `MarkupExtension` base class and `MarkupExtensionReturnTypeAttribute`.
- **`GroupChannelList.Resources.StringResources`**: Strongly-typed resource class generated from `.resx` files. Relies on:
- `System.Resources.ResourceManager`
- `System.Globalization.CultureInfo`
- **WPF runtime**: Required for `IServiceProvider` and XAML markup extension resolution.
### Dependencies *on* this module:
- **XAML files in `GroupChannelList` module**: Used via XMLNS (e.g., `xmlns:res="clr-namespace:GroupChannelList"`) to localize UI elements:
```xml
<TextBlock Text="{res:Translate ChannelName}" />
```
- **No other modules directly depend on `TranslateExtension`**—it is consumed only via XAML markup.
## 5. Gotchas
- **No runtime validation of key existence**: If a resource key is misspelled (e.g., `"ChannelNme"` instead of `"ChannelName"`), the extension silently returns `"#stringnotfound# ChannelNme"`, which may appear as visible text in the UI.
- **No fallback to default language**: If a key exists in the `.resx` but lacks a translation for the current culture, `ResourceManager.GetString` returns `null`, triggering the fallback string. This may cause inconsistent behavior if translations are incomplete.
- **Hardcoded fallback prefix**: The `"#stringnotfound#"` prefix is hardcoded and not configurable. This could conflict with legitimate strings if used as a key.
- **No support for parameterized strings**: While `StringResources` includes format strings (e.g., `"Sensor {0} can not be assigned..."`), `TranslateExtension` does not support passing arguments (e.g., `{res:Translate Key, Arg1, Arg2}`). Developers must use `String.Format` manually in code-behind or elsewhere.
- **Auto-generated resource class**: `StringResources.Designer.cs` is auto-generated; manual edits are overwritten. Adding new keys requires updating the `.resx` file and regenerating the class.
- **No thread-safety guarantees**: Though `ResourceManager` is thread-safe, the extension itself is stateful (via `_key`), but this is harmless since each usage creates a new instance.
None identified beyond the above.

View File

@@ -0,0 +1,83 @@
---
source_files:
- DataPRO/Modules/Groups/GroupChannelList/View/GroupChannelListView.xaml.cs
generated_at: "2026-04-16T04:46:59.687984+00:00"
model: "Qwen/Qwen3-Coder-Next-FP8"
schema_version: 1
sha256: "a7a7f5755050a82e"
---
# View
### **Purpose**
`GroupChannelListView` is the WPF view implementation for displaying and interacting with a list of channels associated with a test group or test setup. It provides UI controls for channel management—including reordering, deletion, clearing, filtering, and drag-and-drop assignment of sensors and hardware—while dynamically adjusting column visibility and layout based on the current view mode (`IsoViewMode`), group/test context, and global settings (e.g., `ShowGroups`). It acts as the presentation layer for `GroupChannelListViewModel`, synchronizing UI state with the view model via data binding and event-driven interactions.
---
### **Public Interface**
The class implements `IGroupChannelListView` (inferred from `partial class GroupChannelListView : IGroupChannelListView`) and exposes the following public members:
1. **`bool ReadOnlyChannelsMode { get; }`**
Returns `true` if user input controls in the channel list should be read-only. Delegates to `IGroupChannelListViewModel.ReadOnlyChannelsMode`, returning `false` if `DataContext` is unset or not a `IGroupChannelListViewModel`.
2. **`void HandleColumns(IsoViewMode viewMode)`**
Dynamically configures the columns of `ChannelListListView` (an `AutoSizedGridView`) based on `viewMode` and global settings (`ShowGroups`, `UseTestSetupOrder`, `ShowDallasIdColumn`). Adds/removes columns for:
- Group order (`GroupColumn`, `GroupOrderColumn`)
- Test setup order (`TestSetupOrderColumn`)
- User code/name (`UserCodeColumn`, `UserChannelNameColumn`)
- ISO code/name (`ISOCodeColumn`, `ISOChannelNameColumn`)
- Dallas ID (`DallasIdColumn`)
Ensures column ordering respects `usingTestSetup` context.
---
### **Invariants**
The following must hold during operation:
- **`ChannelListListView` must be initialized before `HandleColumns()` is called**, as all column operations assume `ChannelListListView.View` is an `AutoSizedGridView`.
- **`DataContext` must be set to a `GroupChannelListViewModel` (or `IGroupChannelListViewModel`) for most functionality**. If `DataContext` is `null` or of the wrong type, methods silently return (e.g., `OnListViewEvent`, `Clear_Click`, `Delete_Click`, `MoveUp_Click`, etc.).
- **Drag-and-drop operations require `e.Data` to be non-null and contain supported formats** (e.g., `DragAndDropPayload.FORMAT`, `DTS.Common.Classes.Hardware.DragAndDropPayload.FORMAT`). Unsupported formats result in `DragDropEffects.None`.
- **Channel deletion for blank channels requires valid ordering**:
- If `UseTestSetupOrder`, `channel.TestSetupOrder > 0` must hold.
- Otherwise, `channel.GroupChannelOrder > 0` must hold.
(Per issue #14546 comment in `Delete_Click`.)
- **`ReadOnlyChannelsMode` is `false` if `DataContext` is `null` or not a `IGroupChannelListViewModel`**.
- **Column insertion/removal preserves ordering**:
- `TestSetupOrderColumn`/`GroupOrderColumn` are always first (index `0`).
- User/ISO columns are inserted at indices `0` or `1` depending on `usingTestSetup`.
---
### **Dependencies**
**Imports/References (from source):**
- `DTS.Common.*`: Core domain classes (`Groups`, `Sensors`, `Controls`, `Enums`, `Events`, `Interface`, `Settings`, `Utils`).
- `Prism.Ioc`, `Prism.Events`: For `IEventAggregator`, `ContainerLocator`, and event publication/subscription (`ListViewStatusEvent`, `GroupChannelDeleteRequestEvent`, `PageNavigationRequestEvent`).
- WPF namespaces (`System.Windows.*`): For `ListView`, `DragEventArgs`, `Hyperlink`, etc.
**Key External Dependencies (inferred):**
- `GroupChannelListViewModel`: Required as `DataContext` for all interactive logic.
- `IGroupChannel`: Interface implemented by channel objects (e.g., `GroupChannel`).
- `AutoSizedGridView`: Custom WPF control (from `DTS.Common.Controls`) used as `ChannelListListView.View`.
- `MouseUtilities`: Used in `IsMouseOverTarget()` for drag-drop hit-testing (from `DTS.Common.Utils`).
- `DTS.SensorDB.SoftwareFilter`: Used in `ISOCode_LostFocus` to fetch filter classes.
- Global settings via `SettingsDB.GetGlobalValueBool("ShowGroups", true)`.
**Depended upon by:**
- `GroupChannelListViewModel` (via `IGroupChannelListView` interface).
- Event handlers (`ListViewStatusEvent`, `GroupChannelDeleteRequestEvent`, `PageNavigationRequestEvent`) suggest integration with broader Prism-based navigation and state management.
---
### **Gotchas**
- **Silent failures**: Most event handlers and property getters return early if `DataContext` is `null` or of incorrect type (e.g., `Clear_Click`, `Delete_Click`, `MoveUp_Click`, `HandleColumns`). No exceptions or logging occur—debugging requires inspecting call stacks.
- **Drag-drop modifier key handling is fragile**:
- `ALT`/`CTRL` key states are checked via bitwise operations on `DragDropKeyStates`, but formats are mutated *in-place* (e.g., `"ALT_FORMAT"`), which may cause issues if `e.Data.GetFormats()` is called multiple times.
- `TextBox_Drop` and `ChannelList_Drop` duplicate logic for format handling—risk of divergence if updated in one place but not the other.
- **`ReadOnlyChannelsMode` is read-only and computed**: It does not raise change notifications; UI consumers must re-query it when `DataContext` changes.
- **`HandleColumns` assumes `ChannelListListView.View` is always `AutoSizedGridView`**: If the view is changed (e.g., to `GridView`), column operations will silently fail.
- **`MoveUp`/`MoveDown` via keyboard (Alt+↑/↓) uses `ChannelListListView.SelectedItems` order**, but `GetSelectedChannelsOrdered()` sorts by *view index*—this may differ from selection order if items are selected non-contiguously.
- **`ISOCode_LostFocus` mutates `FilterClass` based on ISO code**, but only if `UseISOCodeFilterMapping` is `true`. No fallback or error handling if `GetFilterClassFromISOCode` fails.
- **`Delete_Click` allows deletion of blank channels with valid orders** (per issue #14546), but blocks deletion if order ≤ 0—this may confuse users expecting blank channels to be deletable unconditionally.
- **`TextBoxSourceUpdated` handles group name changes**, but the comment implies complex group reorganization logic in `GroupNameChanged`—behavior is not visible in this file.
*None identified from source alone.*

View File

@@ -0,0 +1,209 @@
---
source_files:
- DataPRO/Modules/Groups/GroupChannelList/ViewModel/GroupChannelListViewModel.cs
generated_at: "2026-04-16T04:46:34.697931+00:00"
model: "Qwen/Qwen3-Coder-Next-FP8"
schema_version: 1
sha256: "7af2406ccff6580f"
---
# ViewModel
**Documentation Page: `GroupChannelListViewModel`**
---
### **1. Purpose**
The `GroupChannelListViewModel` class serves as the core view model for managing channel assignments within the *GroupChannelList* UI module. It orchestrates the display, editing, and synchronization of channel data—linking logical channel definitions (e.g., ISO/user codes, sensor assignments, hardware mappings) to physical test hardware and sensor metadata. It supports operations such as bulk import via text paste, drag-and-drop assignment of sensors and hardware channels, range synchronization across channels on the same DAS, and real-time filtering/sorting. It acts as the intermediary between the view (`IGroupChannelListView`, `IGroupChannelSettingsListView`) and domain models (`IGroup`, `ITestSetup`, `ISensorData`, `IHardwareChannel`), publishing events for UI updates and system-wide state changes.
---
### **2. Public Interface**
#### **Constructor**
```csharp
public GroupChannelListViewModel(
IGroupChannelListView view,
IGroupChannelSettingsListView settingsView,
IRegionManager regionManager,
IEventAggregator eventAggregator,
IUnityContainer unityContainer)
```
- Initializes the view model, wires up views, sets up interaction requests (`NotificationRequest`, `ConfirmationRequest`), and subscribes to events (`RaiseNotification`, `BusyIndicatorChangeNotification`, `TextPastedEvent`).
- Initializes `SelectedChannelItems` as an `ObservableCollection<IGroupChannel>`.
#### **Properties**
| Property | Type | Description |
|---------|------|-------------|
| `View` | `IGroupChannelListView` | Reference to the main channel list view. |
| `SettingsView` | `IGroupChannelSettingsListView` | Reference to the channel settings view. |
| `NotificationRequest` | `InteractionRequest<Notification>` | Prism interaction request for displaying notifications. |
| `ConfirmationRequest` | `InteractionRequest<Confirmation>` | Prism interaction request for confirmation dialogs. |
| `PropertyChanged` | `event PropertyChangedEventHandler` | Standard `INotifyPropertyChanged` event. |
| `ChannelCount` | `int` | Derived from `AllChannels.Count`; triggers `GroupChannelsChangedEvent` on change. |
| `AllChannels` | `IList<IGroupChannel>` | *Internal* full list of channels (including blank trailing channel). |
| `Channels` | `IList<IGroupChannel>` | *Internal* list of non-blank channels (used for UI rendering). |
| `SettingChannels` | `ObservableCollection<IGroupChannel>` | Sorted, filtered list of non-blank channels for settings view. |
| `SelectedChannelItems` | `ObservableCollection<IGroupChannel>` | Currently selected channels (for multi-select operations). |
| `SearchTerm` | `string` | Current search filter term. |
| `BridgeFilter` | `PossibleFilters` | Bridge-type filter (e.g., `Analog`, `Squib`, `DigitalIn`). |
| `UseTestSetupOrder` | `bool` | Whether channels are ordered by `TestSetupOrder` (vs `GroupChannelOrder`). |
| `ISOViewMode` | `bool` | Controls display of ISO/user fields. |
| `ShowSensorChannelUserValues` | `bool` | Controls visibility of sensor/user-specific fields. |
| `AllowSensorPushAndPull` | `bool` | Enables sensor parameter comparison and push/pull UI. |
| `UserIsAdmin` | `bool` | Admin status for permission checks. |
| `AllowChannelDeletionByNonAdminUser` | `bool` | Whether non-admin users can delete channels. |
| `AllowChannelDeletionFromFixedGroup` | `bool` | Whether channels in fixed groups can be deleted. |
| `IsBusy` | `bool` | Bound to busy indicator via `BusyIndicatorChangeNotification`. |
| `Group` | `IGroup` | *Set externally*; the group currently being edited. |
| `TestSetup` | `ITestSetup` | *Set externally*; the test setup currently being edited. |
| `Page` | `object` | *Set externally*; the page context (e.g., `GroupsPage`, `TestSetupsPage`). |
#### **Key Methods**
| Method | Signature | Description |
|--------|-----------|-------------|
| `UpdateRangeLowG` | `public void UpdateRangeLowG(IGroupChannel channelChanged)` | Propagates `RangeLowG` to all other channels on the same DAS that have `RangeModifiableSensorLowG = true`. |
| `UpdateRangeARS` | `public void UpdateRangeARS(IGroupChannel channelChanged)` | Propagates `Range` to all other channels on the same DAS that have `RangeModifiableSensorARS = true`. |
| `UpdateACCouplingEnabled` | `public void UpdateACCouplingEnabled(IGroupChannel channelChanged)` | Propagates `ACCouplingEnabled` to other channels on the same DAS (casts to `GroupChannel`). |
| `DoSensorAssignment` | `public void DoSensorAssignment(IGroupChannel groupChannel, IDragAndDropItem[] sensors)` | Assigns sensors to consecutive channels starting at `groupChannel`. Skips assignment to TSR-AIR channels (except StreamOut/UART). Adds new channels if needed. |
| `DoHardwareAssignment` | `public void DoHardwareAssignment(IGroupChannel groupChannel, IHardwareChannel[] hardwareChannels)` | Assigns hardware channels to consecutive channels. Skips assignment to non-blank/non-StreamOut/UART channels if source is TSR-AIR. |
| `OnTextPasted` | `public void OnTextPasted(ITextPastedEventArgs args)` | Handles paste events (ID = `GroupChannel.PASTE_ID`). Parses tab/semicolon/comma-delimited text into channels, inserting/updating starting at the senders position. |
| `ParseText` | `private IEnumerable<IGroupChannel> ParseText(string text, object tag, out bool oneColumn)` | Parses raw text into `IGroupChannel` objects. Supports column mapping via `tag` (e.g., `"GroupName"`, `"ISOCode"`). Handles sensor/hardware lookup via dictionaries. |
| `GetSensorSerialNumber` | `private string GetSensorSerialNumber(string channelSensor)` | Extracts serial number from sensor string (e.g., `"strain gauge 1 (SG1)"``"SG1"`). |
| `CreateGroupIfNeeded` | `public IGroup CreateGroupIfNeeded(ITestSetup testSetup, string groupName)`<br>`private IGroup CreateGroupIfNeeded(string groupName)` | Creates a new group if none exists for `groupName` under `TestSetup`. |
| `OnSetActive` | `public void OnSetActive()` | Called when view becomes active. Updates channel group names, compares sensor parameters (`CompareAndMarkChannelParameters`), refreshes view settings/columns, and resets sort/filter state. |
| `CompareAndMarkChannelParameters` | `public bool CompareAndMarkChannelParameters(IGroupChannel ch)` | Compares channel parameters (e.g., `Range`, `FilterClass`, `SquibFireMode`) against sensor DB defaults. Marks channel as *different* if mismatched. Returns `true` if any change detected. |
| `ResetSettingChannels` | `private void ResetSettingChannels()` | Rebuilds `SettingChannels` from `Channels`, applying sort (via `GroupChannelComparer`), filtering blanks, and setting `DigitalOutDurationMax` for TOM hardware. |
| `Unset` | `public void Unset()` | Clears all internal state: `AllChannels`, `Channels`, sensor/hardware lookup dictionaries, filters. |
| `ClearAllFilters` | `public void ClearAllFilters()` | Resets `SearchTerm`, `BridgeFilter`, `_filterByField`, `_dontFilterList`, and publishes `ListViewStatusEvent.Unloaded`. |
| `PopulateChannels` | `public IDictionary<IGroup, IGroupChannel[]> PopulateChannels(...)` | **Core initialization method.** Loads channels from `Group` or `TestSetup`, populates lookup dictionaries (`_idToSensorDictionary`, `_displayToHardwareChannel`), sets up channel metadata (e.g., `RemoveSensorVisibility`, `DeleteShouldBeEnabled`), and returns `ChannelsForGroup` mapping. |
| `MarkModified` | `public void MarkModified(IGroupChannel channel, bool bNotifyChanged = true)` | Handles adding a new blank channel after `channel` (if its the last non-blank), updates ordering, and ensures group assignment. |
| `Remove` | `public void Remove(IGroupChannel channel, bool notifyChanged = true)` | Removes `channel`, adds a new blank channel if needed, updates group/channel mappings, and triggers `NotifyChannelsChanged`. |
| `NotifyChannelsChanged` | `public void NotifyChannelsChanged()` | Publishes `PageModifiedEvent` and `GroupUpdatedEvent`, and updates `ChannelCount`/`AssignedPhysicalChannelCount`. |
| `GroupNameChanged` | `public void GroupNameChanged(IGroupChannel channel)` | Updates `TestSetup.ChannelsForGroup` when a channels group name changes (removes from old group, adds to new). |
| `ReportErrors` | `public void ReportErrors(string[] errors)` | Publishes `PageErrorEvent` with error messages. |
| `Clear` | `public void Clear(IGroupChannel channel)` | Calls `channel.Clear()` (resets channel fields to defaults). |
| `OnBusyIndicatorNotification` | `private void OnBusyIndicatorNotification(bool eventArg)` | Event handler for `BusyIndicatorChangeNotification`; sets `IsBusy`. |
| `OnRaiseNotification` | `private void OnRaiseNotification(NotificationContentEventArgs eventArgsWithTitle)` | Event handler for `RaiseNotification`; wraps message and title into `Notification` for UI. |
#### **Event Handlers (Private)**
- `OnTextPasted`: Handles paste events.
- `OnBusyIndicatorNotification`: Updates `IsBusy`.
- `OnRaiseNotification`: Triggers `NotificationRequest`.
#### **Event Properties (Public)**
- `PropertyChanged`: Standard property change notification.
---
### **3. Invariants**
- **Channel Ordering**:
- `AllChannels` always ends with a blank `GroupChannel` (used for adding new channels).
- `Channels` is a subset of `AllChannels` containing only non-blank channels.
- Channel ordering is maintained via `TestSetupOrder` (if `UseTestSetupOrder`) or `GroupChannelOrder`.
- `DetermineButtonState()` enforces `CanMoveUp`/`CanMoveDown` constraints (first channel cannot move up; last non-blank cannot move down).
- **Group/Hardware Consistency**:
- Channels are associated with a `Group` (from `Group` or `TestSetup`) and `HardwareChannel`.
- `GroupChannel.GroupName` must match `Group.DisplayName`.
- Hardware assignments to TSR-AIR channels are restricted (only to blank, StreamOut, or UART channels).
- **Sensor/Hardware Lookup**:
- `_idToSensorDictionary`, `_serialNumberToSensorDictionary`, and `_displayToHardwareChannel` must be populated before `PopulateChannels` completes.
- Sensor serial numbers are extracted from strings (e.g., `"name (SN)"``"SN"`) using `GetSensorSerialNumber`.
- **Filtering**:
- `_dontFilterList` preserves channels during filtering (e.g., after drag/drop or paste).
- `Filter()` respects `BridgeFilter`, `SearchTerm`, and per-field filters (`_filterByField`).
- **Notification & Events**:
- `OnPropertyChanged("ChannelCount")` triggers `GroupChannelsChangedEvent`.
- `MarkModified`, `Remove`, `NotifyChannelsChanged` publish `PageModifiedEvent` and `GroupUpdatedEvent`.
---
### **4. Dependencies**
#### **External Dependencies (Imports/Usings)**
- **Prism Framework**: `IEventAggregator`, `IRegionManager`, `UnityContainer`, `InteractionRequest<T>`, `DelegateCommand`.
- **DTS Common Libraries**:
- `DTS.Common.Classes.Groups`
- `DTS.Common.Enums` (e.g., `PossibleFilters`, `Fields`, `HardwareTypes`)
- `DTS.Common.Interface.*` (e.g., `IGroupChannel`, `ISensorData`, `IHardwareChannel`)
- `DTS.Common.Events.*` (e.g., `TextPastedEvent`, `PageErrorEvent`)
- `DTS.Common.Storage`, `DTS.Common.Converters`, `DTS.Common.Interactivity`
#### **Key Dependencies**
- **Views**: `IGroupChannelListView`, `IGroupChannelSettingsListView`.
- **Services**: `IEventAggregator`, `IUnityContainer`, `IRegionManager`.
- **Data Sources**: `ITestSetup`, `IGroup`, `ISensorData`, `IDASHardware`, `IChannelSetting`.
- **Events Published**:
- `GroupChannelsChangedEvent`
- `PageModifiedEvent`
- `GroupUpdatedEvent`
- `PageErrorEvent`
- `ListViewStatusEvent`
- `AppStatusEvent`
- `RaiseNotification`
#### **Dependents**
- `GroupChannelList` view (binds to `View`, `SettingsView`).
- Other modules via `GroupChannelsChangedEvent`, `PageModifiedEvent`, `GroupUpdatedEvent`.
---
### **5. Gotchas**
- **TSR-AIR Channel Restrictions**:
- Sensor/hardware assignments are blocked for non-StreamOut/UART TSR-AIR channels (see `DoSensorAssignment`, `DoHardwareAssignment`).
- Embedded sensors in TSR-AIR units are only added if the unit is not already in the group.
- **Blank Channel Handling**:
- `AllChannels` always ends with a blank channel; `Channels` excludes it.
- `MarkModified` and `Remove` manage blank channel insertion/removal to maintain this invariant.
- **Paste Parsing Ambiguity**:
- `ParseText` splits on `,`, `\t`, or `;` (in order) if tokens < 2. Column mapping depends on `tag` (e.g., `"GroupName"`).
- `oneColumn` output indicates if only one column was present (used for single-field pastes).
- **Sensor Serial Number Parsing**:
- `GetSensorSerialNumber` assumes serial numbers are enclosed in parentheses (e.g., `"name (SN)"`). If no parentheses, the entire string is used.
- **Group Name Changes**:
- `GroupNameChanged` modifies `TestSetup.ChannelsForGroup` and may remove empty groups. Does *not* publish `PageModifiedEvent` (commented out).
- **Filtering Behavior**:
- `_dontFilterList` is cleared on user-initiated filter changes (`initiatedByUser = true`).
- Per-field filters (`_filterByField`) are applied *after* `BridgeFilter` and `SearchTerm`.
- **Hardware Channel Lookup**:
- `_displayToHardwareChannel` uses `channel.ToString(hardware)` as the key (implementation-dependent string representation).
- **Sensor Constants**:
- `CompareAndMarkChannelParameters` skips comparison for test-specific sensors (e.g., `TEST_SPECIFIC_ANALOG_SERIAL`).
- **Settings View Loading**:
- `SettingsViewLoaded` controls whether `SettingChannels` triggers `OnPropertyChanged`.
- `SettingChannelsLoaded` publishes `AppStatusEvent` (Busy → Available) during UI initialization.
- **No-Op Initialization Methods**:
- `Initialize`, `InitializeAsync`, `Activated`, `Cleanup`, `CleanupAsync` are stubbed (no implementation).
- **Range Propagation Scope**:
- `UpdateRangeLowG`/`UpdateRangeARS` only propagate within the same `DASId`.
- `UpdateACCouplingEnabled` requires `GroupChannel` cast (runtime risk if non-`GroupChannel` instances exist).
- **Channel Ordering After Paste**:
- After paste, `AllChannels[i].TestSetupOrder`/`GroupChannelOrder` is reset to `1 + i` for all non-blank channels.
- **Resource Strings**:
- Uses `Resources.StringResources.TestChannelsGroupName` for default group names in `TestSetup` mode.
- **Missing Validation**:
- `ParseText` does not validate sensor/hardware references beyond existence in dictionaries (e.g., invalid IDs are logged but do not halt processing).
---
*Note: All behaviors are derived strictly from the provided source. No external documentation or runtime behavior was assumed.*

View File

@@ -0,0 +1,113 @@
---
source_files:
- DataPRO/Modules/Groups/GroupImport/GroupImportModule.cs
generated_at: "2026-04-16T04:45:20.865762+00:00"
model: "Qwen/Qwen3-Coder-Next-FP8"
schema_version: 1
sha256: "99bf820620f80499"
---
# GroupImport
## Documentation: `GroupImportModule`
---
### 1. **Purpose**
The `GroupImportModule` is a Prism module responsible for registering the views and view models required for the *Group Import* functionality within the applications UI. It integrates with the Unity dependency injection container to expose the necessary components (`IGroupImportImportView`, `IGroupImportOptionsView`, `IGroupImportPreviewView`, and `IGroupImportViewModel`) as singleton registrations, enabling modular, testable, and loosely coupled UI construction. Additionally, it provides assembly-level metadata via the `GroupImageAttribute`, which is used by the main UI to display the modules name, group (e.g., “Prepare”), and associated icon.
---
### 2. **Public Interface**
#### `GroupImportModule` class
- **`GroupImportModule(IUnityContainer unityContainer)`**
Constructor. Accepts a Unity container via dependency injection and stores it for later use in type registration.
- **`void Initialize()`**
Registers the following types as singletons in the Unity container:
- `IGroupImportImportView``GroupImportImportView`
- `IGroupImportOptionsView``GroupImportOptionsView`
- `IGroupImportPreviewView``GroupImportPreviewView`
- `IGroupImportViewModel``GroupImportViewModel`
This method is called by both `Prism.Modularity.IModule.Initialize()` and `RegisterTypes()`.
- **`void OnInitialized(IContainerProvider containerProvider)`**
Currently empty; no initialization logic beyond type registration is performed.
- **`void RegisterTypes(IContainerRegistry containerRegistry)`**
Delegates to `Initialize()`. (Note: Despite accepting `IContainerRegistry`, it uses the injected `IUnityContainer` internally.)
#### `GroupImageAttribute` class
- **`GroupImageAttribute()`**
Default constructor; initializes image via `AssemblyInfo.GetImage(AssemblyNames.GroupImport.ToString())`.
- **`GroupImageAttribute(string s)`**
Constructor accepting a string argument (unused); initializes image identically to the default constructor.
- **`override BitmapImage AssemblyImage`**
Returns a `BitmapImage` loaded via `AssemblyInfo.GetImage(AssemblyNames.GroupImport.ToString())`.
- **`override string AssemblyName`**
Returns `"GroupImport"` (from `AssemblyNames.GroupImport.ToString()`).
- **`override string AssemblyGroup`**
Returns `"Prepare"` (from `eAssemblyGroups.Prepare.ToString()`).
- **`override eAssemblyRegion AssemblyRegion`**
Throws `NotImplementedException`. *Not implemented.*
- **`override Type GetAttributeType()`**
Returns `typeof(ImageAttribute)`.
- **`override BitmapImage GetAssemblyImage()`**
Returns the value of `AssemblyImage`.
- **`override string GetAssemblyName()`**
Returns the value of `AssemblyName`.
- **`override string GetAssemblyGroup()`**
Returns the value of `AssemblyGroup`.
- **`override eAssemblyRegion GetAssemblyRegion()`**
Throws `NotImplementedException`. *Not implemented.*
---
### 3. **Invariants**
- The `GroupImportModule` must be initialized *after* the Unity container is available (via DI), and before any views/view models are resolved.
- All registered types (`IGroupImport*View`, `IGroupImportViewModel`) are registered as *singletons* (default Unity lifetime).
- `GroupImageAttribute` is applied at the **assembly level** (via `[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = false)]`), and only one instance per assembly is allowed.
- `AssemblyImage`, `AssemblyName`, and `AssemblyGroup` are computed *at runtime* using static methods (`AssemblyInfo.GetImage`, `AssemblyNames.GroupImport`, `eAssemblyGroups.Prepare`). Their values depend on external definitions in `DTS.Common` and must be consistent.
- `AssemblyRegion` and `GetAssemblyRegion()` are **not implemented** and will throw `NotImplementedException` if invoked.
---
### 4. **Dependencies**
#### **Depends on**
- `DTS.Common` (specifically `AssemblyInfo`, `AssemblyNames`, `eAssemblyGroups`)
- `Prism.Modularity` (`IModule`, `IContainerProvider`, `IContainerRegistry`)
- `Unity` (`IUnityContainer`)
- `System.Windows.Media.Imaging` (`BitmapImage`)
#### **Depended on by**
- The Prism bootstrapper/container infrastructure (via `[Export(typeof(IModule))]` and `[Module(...)]` attributes).
- UI components that resolve `IGroupImport*View` or `IGroupImportViewModel`.
- The main application shell or module catalog system that consumes `GroupImageAttribute` to display module metadata.
---
### 5. **Gotchas**
- **`RegisterTypes` does not use `IContainerRegistry`**: Despite implementing `IContainerRegistry`, the method ignores it and calls `Initialize()`, which uses the injected `IUnityContainer`. This is inconsistent with Prisms recommended pattern (where `RegisterTypes` should use `containerRegistry`) and may cause confusion or breakage if the module is used in a non-Unity Prism setup.
- **`AssemblyRegion` is unimplemented**: Both `AssemblyRegion` and `GetAssemblyRegion()` throw `NotImplementedException`. If the UI or module loader relies on this property, it will crash at runtime.
- **Redundant constructor**: The `GroupImageAttribute(string s)` constructor accepts a parameter that is never used.
- **No validation of image loading**: `AssemblyInfo.GetImage(...)` may return `null` if the image resource is missing; this is not handled, potentially leading to `NullReferenceException` at runtime when the image is rendered.
- **No documentation on view/view model responsibilities**: While the types are registered, their roles (e.g., what `IGroupImportImportView` vs `IGroupImportPreviewView` does) are not described here and must be inferred from their implementations.
None identified beyond the above.

View File

@@ -0,0 +1,44 @@
---
source_files:
- DataPRO/Modules/Groups/GroupImport/Properties/AssemblyInfo.cs
generated_at: "2026-04-16T04:45:50.776697+00:00"
model: "Qwen/Qwen3-Coder-Next-FP8"
schema_version: 1
sha256: "06eddeeb5b49ca9c"
---
# Properties
## 1. Purpose
This module (`GroupImportModule`) is an assembly containing metadata and configuration for a .NET component responsible for group import functionality within the larger DataPRO system. Based solely on the file name (`GroupImport`) and assembly title, its role is to encapsulate logic (not visible in this file) related to importing group data—likely from external sources (e.g., CSV, XML, or legacy systems)—into the applications internal group management subsystem. This file itself contains only assembly-level attributes (e.g., versioning, COM visibility), not executable logic.
## 2. Public Interface
**No public API surface is defined in this file.**
This file (`AssemblyInfo.cs`) is strictly metadata and contains no types, functions, classes, or methods. It only declares assembly-level attributes via `System.Reflection` and `System.Runtime.InteropServices` attributes.
## 3. Invariants
- The assembly identity is fixed:
- `AssemblyTitle` = `"GroupImportModule"`
- `AssemblyVersion` = `"1.0.0.0"`
- `AssemblyFileVersion` = `"1.0.0.0"`
- `ComVisible` = `false`
- `Guid` = `"f3e176ef-8eaf-4277-95f2-c8546c254248"`
- The assembly is not intended for COM interop (per `ComVisible(false)`), and the GUID is reserved for typelib identification *only if* COM exposure were enabled in the future.
- No runtime invariants apply, as this file contributes no executable code.
## 4. Dependencies
- **Dependencies**:
- `System.Reflection`
- `System.Runtime.CompilerServices`
- `System.Runtime.InteropServices`
These are standard .NET Framework/BCL namespaces (no external dependencies inferred).
- **Dependents**:
- Unknown from this file alone. This assembly is likely referenced by other modules in the `DataPRO` solution (e.g., a core group management module or a UI layer), but no explicit references are declared here.
## 5. Gotchas
- **Assembly versioning is static**: Both `AssemblyVersion` and `AssemblyFileVersion` are hardcoded to `1.0.0.0`. This may indicate legacy or placeholder configuration; in practice, versioning should be updated for releases to avoid binding issues.
- **COM visibility is disabled**: If COM interop is required (e.g., for legacy automation), `ComVisible(true)` and explicit `Guid` on *types* (not just the assembly) would be necessary—this file alone does not enable COM.
- **No functional code**: Developers should not expect to find import logic here. The actual implementation resides in other files (e.g., `GroupImporter.cs`, `ImportHandler.cs`), which are not provided.
- **Copyright year is 2017**: May indicate outdated metadata if the module has been recently modified.
None identified beyond the above.

View File

@@ -0,0 +1,87 @@
---
source_files:
- DataPRO/Modules/Groups/GroupImport/Resources/TranslateExtension.cs
- DataPRO/Modules/Groups/GroupImport/Resources/StringResources.Designer.cs
generated_at: "2026-04-16T04:45:43.131858+00:00"
model: "Qwen/Qwen3-Coder-Next-FP8"
schema_version: 1
sha256: "d8e7eb58224237bd"
---
# Resources
## Documentation: `TranslateExtension` Markup Extension
---
### 1. **Purpose**
This module provides a WPF `MarkupExtension` (`TranslateExtension`) to enable declarative, localized string resolution in XAML. It allows UI elements to bind to localized resources using a string key, falling back to a visible placeholder if the key is missing or the resource is unavailable. Its role is to support internationalization (i18n) of the Group Import modules UI by abstracting resource lookup away from code-behind and into XAML markup.
---
### 2. **Public Interface**
#### `TranslateExtension` class
**Namespace:** `DBImportExport.Resources`
**Base class:** `System.Windows.Markup.MarkupExtension`
**Attribute:** `[MarkupExtensionReturnType(typeof(string))]`
##### Constructor
```csharp
public TranslateExtension(string key)
```
- **Parameters:**
- `key`: The resource key (e.g., `"GroupTags"`, `"Import_Importing"`) used to look up a localized string in `StringResources`.
- **Behavior:** Stores the key for later use in `ProvideValue`.
##### `ProvideValue` method
```csharp
public override object ProvideValue(IServiceProvider serviceProvider)
```
- **Returns:** `string`
- **Behavior:**
- If `_key` is `null` or empty → returns `"#stringnotfound#"`.
- Otherwise, attempts to retrieve the string via `StringResources.ResourceManager.GetString(_key)`.
- If found → returns the localized string.
- If not found (`null`) → returns `"#stringnotfound# " + _key` (e.g., `"#stringnotfound# Import_Importing"`).
- **Note:** The `serviceProvider` parameter is unused.
---
### 3. **Invariants**
- `_key` is immutable after construction (no setter, no mutation).
- `StringResources.ResourceManager.GetString(key)` is the *only* source of localized strings; no fallback logic beyond the `NotFound` constant is implemented.
- The `NotFound` constant (`"#stringnotfound#"`) is used consistently for both missing keys and missing values.
- If `StringResources.ResourceManager` fails to initialize (e.g., due to assembly/resource loading issues), `GetString` may throw — but this is not handled in the extension and would result in a runtime exception.
---
### 4. **Dependencies**
#### **Internal Dependencies**
- `GroupImport.Resources.StringResources`:
- Strongly-typed resource class generated from `.resx` files.
- Provides access to localized strings via `ResourceManager.GetString(key)`.
- Contains keys like `"GroupTags"`, `"Import_Importing"`, `"Preview_InvalidName"`, etc.
#### **External Dependencies**
- `System.Windows.Markup.MarkupExtension`: Base class for WPF markup extensions.
- `System`: Used for `string.IsNullOrEmpty`, `object.ReferenceEquals`, etc.
#### **Consumers (Inferred)**
- XAML files in the `GroupImport` module (e.g., `*.xaml`) that use `{local:Translate KeyName}` syntax to bind UI text to localized resources.
---
### 5. **Gotchas**
- **No null-safety for `ResourceManager.GetString()`**: If `StringResources.ResourceManager` is misconfigured (e.g., wrong base name `"GroupImport.Resources.StringResources"`), `GetString` may return `null` or throw — the extension only handles `null` by appending the key to `#stringnotfound#`.
- **Hardcoded `NotFound` string**: The placeholder `"#stringnotfound#"` is visible in the UI if a key is missing, which may confuse end users. No localization-aware fallback (e.g., returning the key itself) is implemented.
- **No caching of resolved values**: `ProvideValue` is called repeatedly (e.g., during layout updates), and each call re-invokes `ResourceManager.GetString`. While `ResourceManager` caches internally, repeated calls are still inefficient.
- **Namespace mismatch**: The class resides in `DBImportExport.Resources` but references `GroupImport.Resources.StringResources`. This may indicate legacy refactoring or intentional decoupling — developers should verify assembly/resource naming consistency.
- **No support for format strings**: While `StringResources` contains format strings (e.g., `"Importing {0}:{1}"`), `TranslateExtension` does not accept or apply arguments — it only resolves the raw key. Formatting must be handled elsewhere (e.g., in code-behind or via `String.Format` after resolution).
- **Auto-generated `StringResources.Designer.cs`**: Changes to `.resx` files require regeneration of `StringResources`. If keys are renamed/removed in `.resx` but not updated in XAML, `TranslateExtension` will return the `#stringnotfound#` fallback.
> **None identified from source alone** regarding thread-safety, disposal, or WPF-specific lifecycle quirks — but given the simplicity of the extension, it is likely safe for standard WPF usage.

View File

@@ -0,0 +1,100 @@
---
source_files:
- DataPRO/Modules/Groups/GroupImport/View/GroupImportImportView.xaml.cs
- DataPRO/Modules/Groups/GroupImport/View/GroupImportOptionsView.xaml.cs
- DataPRO/Modules/Groups/GroupImport/View/GroupImportPreviewView.xaml.cs
generated_at: "2026-04-16T04:46:11.528450+00:00"
model: "Qwen/Qwen3-Coder-Next-FP8"
schema_version: 1
sha256: "4920dece220dd9a5"
---
# View
### **Purpose**
This module provides the WPF UI views for the group import workflow within the DataPRO system, specifically handling user interaction for selecting source files, configuring import options, previewing imported groups and channels, and validating the import state before proceeding. It serves as the presentation layer for the group import functionality, coordinating with a shared `GroupImportViewModel` to manage state and business logic, while enforcing UI-specific validation rules and user privilege checks (e.g., admin-only tag editing).
---
### **Public Interface**
#### **`GroupImportImportView`**
- **Type**: `partial class` implementing `IGroupImportImportView`
- **Constructor**: `GroupImportImportView()`
- Initializes the WPF component via `InitializeComponent()`.
- *No additional logic or public members exposed beyond initialization.*
#### **`GroupImportOptionsView`**
- **Type**: `partial class` implementing `IGroupImportOptionsView`
- **Constructor**: `GroupImportOptionsView()`
- Initializes the WPF component via `InitializeComponent()`.
- **`Validate(out List<string> errors, out List<string> warnings)`**
- **Signature**: `public bool Validate(out List<string> errors, out List<string> warnings)`
- **Behavior**: Validates that at least one source file is selected.
- Returns `true` if `vm.SourceFiles.Length >= 1`.
- Returns `false` and populates `errors` with `StringResources.Preview_NoFilesSelected` if no files are selected.
- *Note*: Does not validate file contents or other options—only file presence.
#### **`GroupImportPreviewView`**
- **Type**: `partial class` implementing `IGroupImportPreviewView`
- **Constructor**: `GroupImportPreviewView()`
- Initializes the WPF component via `InitializeComponent()`.
- **Event Handlers** (WPF event wiring, not public API but critical for behavior):
- `GroupName_Changed(object sender, TextChangedEventArgs e)`
- Updates the `GroupName` property of the bound `GroupGRPImportGroup` instance.
- Calls `GroupNameInvalidate()` on all channels in the group.
- Triggers `vm.CheckGroupName()` on the `GroupImportViewModel`.
- `GroupTags_Changed(object sender, TextChangedEventArgs e)`
- Updates the `GroupTags` property of the bound `GroupGRPImportGroup` instance.
- `IncludedChecked(object sender, RoutedEventArgs e)`
- Calls `vm.InvalidateChannels()` on the `GroupImportViewModel`.
- `IncludedUnchecked(object sender, RoutedEventArgs e)`
- Calls `vm.InvalidateChannels()` on the `GroupImportViewModel`.
- **`Validate(bool userIsAdmin, out List<string> errors, out List<string> warnings)`**
- **Signature**: `public bool Validate(bool userIsAdmin, out List<string> errors, out List<string> warnings)`
- **Behavior**: Performs comprehensive validation of groups and channels for import.
- Checks admin-only tag compatibility: if `!userIsAdmin` and `GroupTags` contains tags not in `ImportingUserTags`, adds a formatted error and returns `false`.
- Checks `GroupNameHasError`: adds `StringResources.Preview_InvalidName` error and returns `false`.
- Checks `GroupErrors`: adds all `ExtraInfo` strings as errors and returns `false`.
- Checks if group exists (`vm.CheckGroupExists`) and `!Overwrite`: adds `StringResources.Preview_InvalidName` error and returns `false`.
- Iterates channels:
- On `ch.Error != null`:
- Adds warnings or errors based on `ErrorCode` (e.g., `InvalidISOCodeInput`, `InvalidFullScaleInput`, `SensorNotFound`).
- Some errors (`FileEmpty`, `InvalidISOCodeInput`) set `bAnyValidChannels = true` but do *not* fail validation.
- On `ch.Error == null`: sets `bValid = true` and `bAnyValidChannels = true`.
- If no valid channels exist (`!bAnyValidChannels`), adds `StringResources.Preview_NoGroupsToImport` to errors and returns `false`.
- Returns `true` only if no fatal errors occurred and at least one valid channel exists.
---
### **Invariants**
- **File Selection**: `GroupImportOptionsView.Validate()` requires `SourceFiles.Length >= 1` for success; otherwise, it fails with a specific error.
- **Admin Tag Enforcement**: Non-admin users cannot set `GroupTags` that include tags not present in `ImportingUserTags`; this causes immediate validation failure with a detailed error message.
- **Group Name Validity**: A group with `GroupNameHasError == true`, existing group name without `Overwrite == true`, or non-empty `GroupErrors` causes immediate validation failure in `GroupImportPreviewView.Validate()`.
- **Channel-Level Errors**:
- `FileEmpty` and `InvalidISOCodeInput` errors do *not* prevent import (but may indicate partial failure).
- `InvalidFullScaleInput`, `InvalidInvertInput`, `InvalidSensorInput`, and `SensorNotFound` produce warnings but do *not* block import.
- **No-Import Guard**: If no channels are valid (`bAnyValidChannels == false`), validation fails with `StringResources.Preview_NoGroupsToImport`.
---
### **Dependencies**
- **External Dependencies**:
- `DTS.Common.Interface.Groups`: Defines interfaces `IGroupImportImportView`, `IGroupImportOptionsView`, `IGroupImportPreviewView`.
- `DTS.Common.Classes.Groups`: Provides concrete types `GroupGRPImportGroup`, `GroupGRPImportError`.
- `DTS.Common.Strings`: Provides `StringResources` for localized error/warning messages.
- `GroupImport.Resources`: Contains `StringResources` (likely auto-generated `.resx`-based resources).
- **Internal Dependencies**:
- `GroupImportViewModel`: Used as `DataContext` in all three views; its methods (`CheckGroupName`, `InvalidateChannels`, `CheckGroupExists`) are called directly.
- WPF framework (`System.Windows.Controls`, `System.Windows.RoutedEventArgs`, etc.).
---
### **Gotchas**
- **Typo in XML comment**: `GroupImportPreviewView.xaml.cs` has `/// <summary> Interaction logic for GroupImportOptionsView.xaml </summary>` instead of `GroupImportPreviewView.xaml`.
- **Ambiguous `bValid` logic**: In `GroupImportPreviewView.Validate()`, `bValid` is initialized to `true`, then set to `false` *before* checking channels, and only set to `true` if a channel has no error. This may be confusing—`bValid` is ultimately determined by whether *at least one* channel is valid *and* no fatal errors occurred.
- **Tag validation is strict**: Non-admin users are blocked if *any* tag in `GroupTags` is not in `ImportingUserTags`. The error message lists both sets explicitly.
- **Duplicate error suppression**: Errors/warnings are deduplicated via `!errors.Contains(...)` checks, but this is linear-time and may be inefficient for large error sets.
- **No explicit handling for null `vm`**: All views cast `DataContext` to `GroupImportViewModel` without null checks; a null `DataContext` will cause a `NullReferenceException`.
- **`GroupNameInvalidate()` and `InvalidateChannels()` are called on UI thread changes**: These likely trigger re-validation or UI refreshes; callers must ensure thread-safety if invoked off-thread (though WPF data binding typically handles this).
- **`Split()` extension method**: Used on `ImportingUserTags` and `GroupTags` (e.g., `g.ImportingUserTags.Split()`), implying a custom extension method (not standard `string.Split()`); behavior depends on its implementation (e.g., whitespace delimiters? empty entries?).

View File

@@ -0,0 +1,188 @@
---
source_files:
- DataPRO/Modules/Groups/GroupImport/ViewModel/GroupImportViewModel.cs
generated_at: "2026-04-16T04:45:51.906727+00:00"
model: "Qwen/Qwen3-Coder-Next-FP8"
schema_version: 1
sha256: "f514e88931e0b70e"
---
# GroupImportViewModel Documentation
## 1. Purpose
`GroupImportViewModel` is the central view model for the group and channel import functionality in the DTS application. It orchestrates the end-to-end workflow of importing data from `.grp` files—parsing file contents, validating sensor and field data, allowing user review and selection of groups/channels for import, and committing validated data to the database via background processing. It serves as the data context for three distinct views (`IGroupImportOptionsView`, `IGroupImportPreviewView`, `IGroupImportImportView`) and integrates with Prisms region management, event aggregation, and Unity DI container to coordinate UI state and external service calls.
## 2. Public Interface
### Constructor
```csharp
public GroupImportViewModel(
IGroupImportOptionsView optionsView,
IGroupImportPreviewView previewView,
IGroupImportImportView importView,
IRegionManager regionManager,
IEventAggregator eventAggregator,
IUnityContainer unityContainer)
```
Initializes the view model, assigns views and their `DataContext`, sets up interaction requests (`NotificationRequest`, `ConfirmationRequest`), and subscribes to `RaiseNotification` and `BusyIndicatorChangeNotification` events.
### Methods
- **`void ParseSourceFiles(string userTags)`**
Reads and parses all `.grp` files listed in `SourceFiles`. For each file:
- Extracts group name from file name (strips extension).
- Assigns `userTags` to `GroupTags` and `ImportingUserTags`.
- Parses each non-empty line using `Parse()` (supports escaped commas via parentheses).
- Validates fields: expects 5 fields per line (`SensorSerialNumber`, `DisplayName`, `ISOCode`, `Invert`, `FullScale`).
- Reports errors for missing sensors (unless empty), invalid invert/full-scale values, wrong field count, or empty files.
- Populates `Groups` and calls `ProcessChannels()`.
- **`void Import()`**
Starts the import process on a background thread via `ThreadPool.QueueUserWorkItem(ImportFunc)`.
- **`private void ImportFunc(object o)`**
Background method that:
- Fetches default channel settings via `DbOperations.GetChannelSettingDefaults()`.
- Updates UI progress state (`ImportProgressValue`, `ImportProgressBarVisibility`, `ImportProgressColor`, `DisableUI`).
- Iterates over included groups and their channels:
- Skips channels with critical errors (`FileEmpty`, `InvalidSensorInput`, `SensorNotFound`).
- For non-critical errors, uses `null` for invalid `FullScale` or `Invert`.
- Calls `CreateGroup`, `AddChannel`, and finally `CommitGroups`.
- Restores UI state on completion (though commented-out lines suggest UI reset is incomplete).
- **`void SetStatus(string message, Color color)`**
Updates `ImportProgressText`, `ImportProgressColor`, `ImportProgressBarVisibility` (collapsed), and invokes `EnableUI`.
- **`void Reset()`**
Clears all imported data: `SourceFiles`, `Channels`, `Groups`, progress state, and resets `ImportProgressValue` to 0.
- **`void CheckGroupName()`**
Validates group names:
- Flags groups with duplicate names in the import set.
- Flags groups whose name already exists in the application *and* `Overwrite` is `false`.
- Sets `GroupNameHasError = true` on invalid groups.
- **`private static string[] Parse(string line)`**
Parses a single `.grp` line, respecting parenthetical escaping (e.g., `"this(,)is"` → one field `"this(,)is"`).
- **`private void ProcessChannels()`**
Flattens all channels from `Groups` into the `Channels` array.
- **`void InvalidateChannels()`**
Raises `PropertyChanged` for `IncompleteChannels` and `CompleteChannels` computed properties.
- **`void Cleanup()` / `Task CleanupAsync()` / `void Initialize()` / `Task InitializeAsync()` / `void Activated()`**
No-op stubs; likely required by Prism interfaces (`INavigationAware`, `IInitializeAsync`, etc.) but not implemented.
### Properties
- **`IGroupImportOptionsView ImportOptionsView`**, **`IGroupImportPreviewView ImportPreviewView`**, **`IGroupImportImportView ImportView`**
References to the three associated views.
- **`string[] SourceFiles`**
Array of `.grp` file paths to parse.
- **`GroupGRPImportGroup[] Groups`**
Parsed groups with their channels and errors.
- **`GroupGRPImportChannel[] Channels`**
Flattened list of all channels from `Groups`.
- **`GroupGRPImportChannel[] IncompleteChannels`**
Channels in included groups that have *non-recoverable* errors (e.g., `SensorNotFound`, `InvalidSensorInput`). These will *not* be imported.
- **`GroupGRPImportChannel[] CompleteChannels`**
Channels in included groups that either have no errors *or* have recoverable errors (`InvalidFullScaleInput`, `InvalidInvertInput`). These *will* be imported (with `null` for invalid fields).
- **`string ImportProgressText`**, **`Color ImportProgressColor`**, **`Visibility ImportProgressBarVisibility`**, **`double ImportProgressValue`**
UI state for the import progress bar.
- **`bool IsBusy`**
Bound to busy indicator; set via `OnBusyIndicatorNotification`.
- **`InteractionRequest<Notification> NotificationRequest`**, **`InteractionRequest<Confirmation> ConfirmationRequest`**
Prism interaction requests for showing notifications and confirmation dialogs.
- **`bool IsDirty`**
Read-only; always `false` (never set to `true` in source).
- **`bool IsMenuIncluded`**, **`bool IsNavigationIncluded`**
UI state flags (bound to menu/navigation visibility); no logic sets them beyond property change notifications.
- **`string HeaderInfo`**
Returns `"MainRegion"`.
### Commands
- **`DelegateCommand ImportBrowseCommand`**
Opens a file dialog (`OpenFileDialog`) with `Multiselect=true`, `Filter` from `StringResources.ImportFileFilter`, sets `SourceFiles`, `BrowseOk=true`, and navigates to `Steps.Preview` via `SwitchNavSteps`.
### Delegates (Settable Properties)
- **`CheckGroupExistsDelegate CheckGroupExists`**
Function to check if a group exists in the application.
- **`CheckSensorExistsDelegate CheckSensorExists`**
Function to check if a sensor exists.
- **`CreateGroupDelegate CreateGroup`**
Action to create a group.
- **`AddChannelToGroupDelegate AddChannel`**
Action to add a channel to a group.
- **`CommitGroupsDelegate CommitGroups`**
Action to commit groups to the database.
- **`Disable_UIDelegate DisableUI`**, **`Enable_UIDelegate EnableUI`**
Actions to disable/enable the UI during import.
- **`SwitchNavStepsDelegate SwitchNavSteps`**
Action to navigate between import steps (e.g., `Steps.Preview`).
- **`FileUtils.LogDelegate Logger`**
Optional logging delegate; invoked on exceptions.
## 3. Invariants
- **File Parsing**: Each `.grp` line must contain 5 comma-separated fields (or be empty/whitespace). Lines with fewer or more fields are marked with `InvalidSensorInput`.
- **Sensor Validation**: A channel is flagged `SensorNotFound` if `CheckSensorExists` is non-null, the sensor serial number is non-empty, and `CheckSensorExists(sensorSerialNumber)` returns `false`. Empty sensor serial numbers are *allowed* (per comment referencing FB13753).
- **Invert Field**: Accepts `"yes"/"no"` (case-insensitive) or `bool.TryParse`-compatible strings. Invalid values trigger `InvalidInvertInput`.
- **FullScale Field**: Must parse as `double`; otherwise, `InvalidFullScaleInput`.
- **Group Name Uniqueness**: Within the import set, duplicate group names are flagged. Across the import set and application, group names must be unique *unless* `Overwrite=true`.
- **Import Progress State**: During `ImportFunc`, `ImportProgressBarVisibility` is set to `Visible`, `DisableUI` is called, and progress is tracked. Completion logic is partially commented out, so UI may not fully reset.
- **Channel Inclusion Logic**: Only channels from groups where `Included=true` are considered for import. Channels with critical errors (`FileEmpty`, `InvalidSensorInput`, `SensorNotFound`) are skipped entirely; others may be imported with `null` for invalid fields.
## 4. Dependencies
### Imports/References
- **DTS.Common**:
- `Events` (`IEventAggregator`, `RaiseNotification`, `BusyIndicatorChangeNotification`)
- `Utils` (`FileUtils.LogDelegate`)
- `Classes.Groups` (`GroupGRPImportGroup`, `GroupGRPImportChannel`, `GroupGRPImportError`)
- `Enums.Groups` (`GroupImportEnums.Steps`, `GroupGRPImportError.Errors`)
- `Interface.Groups` (`IGroupImportOptionsView`, `IGroupImportPreviewView`, `IGroupImportImportView`, `IGroupImportViewModel`)
- `Storage` (`DbOperations`)
- `Interactivity` (`InteractionRequest<T>`)
- **Prism**:
- `Regions` (`IRegionManager`)
- `Events` (`IEventAggregator`)
- `DelegateCommand`
- **Unity** (`IUnityContainer`)
- **System.Windows** (`Visibility`, `Color`)
- **System.Windows.Forms** (`OpenFileDialog`)
- **StringResources** (resource strings for UI messages)
### Dependencies on External Services
- `DbOperations.GetChannelSettingDefaults()` — for default channel settings.
- `CheckSensorExists`, `CheckGroupExists`, `CreateGroup`, `AddChannel`, `CommitGroups` — injected delegates for database operations.
- `SwitchNavSteps` — navigation delegate (likely from a parent view model or shell).
## 5. Gotchas
- **Incomplete UI Reset After Import**: `ImportFunc` sets `ImportProgressBarVisibility = Visibility.Visible` and `DisableUI`, but the corresponding reset (`Visibility.Collapsed`, `EnableUI`) is commented out. This may leave the UI in a disabled state if import completes without error.
- **Empty Sensor Serial Numbers Are Allowed**: Despite `CheckSensorExists` validation, empty serial numbers bypass the `SensorNotFound` check (FB13753).
- **Partial Error Tolerance**: Channels with `InvalidFullScaleInput` or `InvalidInvertInput` are included in `CompleteChannels` and imported, but with `null` for the invalid field. This may cause downstream issues if the database or business logic does not handle `null` gracefully.
- **No Validation of Group Name Format**: Only existence and duplication are checked; no format validation (e.g., reserved characters, length limits) is implemented.
- **`Parse()` Escaping Logic**: Parentheses are treated as literal characters *only* when nested (via `leftParenCount`). A single `(` or `)` without matching pairs is still treated as a delimiter if not balanced.
- **`IsDirty` Never Set**: The `IsDirty` property is declared but never updated, so it always returns `false`.
- **`Initialize*` and `Cleanup*` Methods Are No-ops**: These likely exist to satisfy Prism interfaces but contain no logic.
- **`Channels` Property Triggers `InvalidateChannels()`**: Setting `Channels` raises property change notifications for `IncompleteChannels` and `CompleteChannels`, but these are computed properties—modifying `Channels` directly may not reflect changes in the UI if `Groups` is not updated.
- **Thread Safety**: `ImportFunc` runs on a background thread but updates UI properties directly (e.g., `ImportProgressValue`). While Prisms `INotifyPropertyChanged` may handle thread marshaling in some cases, this pattern is fragile and may cause cross-thread exceptions if the UI framework is strict.

View File

@@ -0,0 +1,108 @@
---
source_files:
- DataPRO/Modules/Groups/GroupList/GroupListModule.cs
generated_at: "2026-04-16T04:45:30.391660+00:00"
model: "Qwen/Qwen3-Coder-Next-FP8"
schema_version: 1
sha256: "eeab36663d49ff59"
---
# GroupList
### **1. Purpose**
The `GroupListModule` is a Prism-based modular component responsible for registering the view and view model for the group list UI functionality within the application. It integrates with the Unity dependency injection container to expose `IGroupListView` and `IGroupListViewModel` as singleton registrations, enabling the module to be discovered and loaded dynamically by the Prism modularity framework. Additionally, it defines assembly-level metadata attributes (`GroupListModuleNameAttribute` and `GroupListModuleImageAttribute`) that supply identifying information (name, image, group, and region) for UI presentation—specifically, to render the modules representation on the main screen under the *Prepare* group in the *GroupListRegion*.
---
### **2. Public Interface**
#### **Class: `GroupListModule`**
- **`GroupListModule(IUnityContainer unityContainer)`**
Constructor. Accepts a Unity container via dependency injection and stores it for later use in type registration.
- **`void Initialize()`**
Registers two interfaces with the Unity container as singleton mappings:
- `IGroupListView``GroupListView`
- `IGroupListViewModel``GroupListViewModel`
This method is called both directly during module initialization and indirectly via `RegisterTypes`.
- **`void OnInitialized(IContainerProvider containerProvider)`**
Currently empty; no logic implemented.
- **`void RegisterTypes(IContainerRegistry containerRegistry)`**
Delegates to `Initialize()` to perform the same type registrations. This is part of the Prism `IModule` lifecycle, though note it uses `IUnityContainer` internally rather than the `containerRegistry` parameter.
#### **Attribute: `GroupListModuleNameAttribute`**
- **`AssemblyName` (read-only `string`)**
Returns `"GroupList"` (via `AssemblyNames.GroupList.ToString()`).
- **`GetAttributeType()``Type`**
Returns `typeof(TextAttribute)`.
- **`GetAssemblyName()``string`**
Returns the value of `AssemblyName`.
#### **Attribute: `GroupListModuleImageAttribute`**
- **`AssemblyImage` (read-only `BitmapImage`)**
Loads and returns the image associated with the *GroupList* assembly via `AssemblyInfo.GetImage("GroupList")`.
- **`AssemblyName` (read-only `string`)**
Returns `"GroupList"`.
- **`AssemblyGroup` (read-only `string`)**
Returns `"Prepare"` (via `eAssemblyGroups.Prepare.ToString()`).
- **`AssemblyRegion` (read-only `eAssemblyRegion`)**
Returns `eAssemblyRegion.GroupListRegion`.
- **`GetAttributeType()``Type`**
Returns `typeof(ImageAttribute)`.
- **`GetAssemblyImage()``BitmapImage`**
Returns the value of `AssemblyImage`.
- **`GetAssemblyName()``string`**
Returns `"GroupList"`.
- **`GetAssemblyGroup()``string`**
Returns `"Prepare"`.
- **`GetAssemblyRegion()``eAssemblyRegion`**
Returns `eAssemblyRegion.GroupListRegion`.
---
### **3. Invariants**
- The module **must** be loaded *after* the Unity container is initialized and available for injection (as it depends on `IUnityContainer`).
- The `AssemblyNames.GroupList` enum value **must** resolve to `"GroupList"` for `AssemblyName` to be correct.
- The `eAssemblyGroups.Prepare` enum value **must** resolve to `"Prepare"` for `AssemblyGroup` to be correct.
- The `eAssemblyRegion.GroupListRegion` enum value **must** be defined and valid for `AssemblyRegion` to be correct.
- The `AssemblyInfo.GetImage(...)` call **must** succeed and return a non-null `BitmapImage`; otherwise, `AssemblyImage` may be null (no explicit null-checking is present).
- `Initialize()` is idempotent in practice (re-registering the same types is safe in Unity), but not explicitly guarded against duplicate registration.
---
### **4. Dependencies**
#### **Module Dependencies (Imports/Usings)**
- **`DTS.Common`** and **`DTS.Common.Interface`** — Provides core types like `AssemblyNames`, `eAssemblyGroups`, `eAssemblyRegion`, `AssemblyInfo`, and attribute base classes (`TextAttribute`, `ImageAttribute`).
- **`Prism.Modularity`** and **`Prism.Ioc`** — Required for implementing `IModule` and using Prisms module infrastructure.
- **`Unity`** — Specifically `IUnityContainer` and `IContainerRegistry`/`IContainerProvider` for DI.
- **`System.ComponentModel.Composition`** — Likely used for `[Export]` (though Unity is used for actual DI).
- **`System.Windows.Media.Imaging`** — For `BitmapImage` used in `AssemblyImage`.
#### **Module Consumers**
- The Prism bootstrapper/module catalog (e.g., `ModuleCatalog`) consumes this module via the `[Export(typeof(IModule))]` and `[Module(ModuleName = "GroupListModule")]` attributes.
- UI components (e.g., main shell) consume metadata from `GroupListModuleImageAttribute` to render the modules icon, name, group, and region.
- Other modules or services may depend on `IGroupListView` or `IGroupListViewModel` being registered in the container.
---
### **5. Gotchas**
- **`RegisterTypes` ignores its parameter**: The method receives `IContainerRegistry containerRegistry` but internally calls `Initialize()`, which uses the injected `_unityContainer` (of type `IUnityContainer`) instead. This may cause confusion or breakage if the container registry is expected to be used.
- **No error handling in image loading**: If `AssemblyInfo.GetImage("GroupList")` fails (e.g., missing image resource), `_img` may be `null`, and no fallback is provided.
- **Redundant attribute constructors**: Both attributes have a redundant `string s` constructor parameter that is unused (assigned but never read).
- **`OnInitialized` is empty**: Suggests incomplete implementation or legacy stub; no logic currently runs post-initialization.
- **No explicit validation of registration order**: If other modules depend on `IGroupListView`/`IGroupListViewModel`, they must ensure `GroupListModule` is loaded first.
- **Assembly-level attributes**: The attributes are applied at the assembly level (`[assembly: ...]`), meaning they are not tied to runtime module state—this is correct for metadata, but implies the image/name must be static and compile-time known.
- **None identified from source alone.** *(Note: The above are inferred from code structure and patterns, not documented quirks.)*

View File

@@ -0,0 +1,31 @@
---
source_files:
- DataPRO/Modules/Groups/GroupList/Model/ChannelSetting.cs
generated_at: "2026-04-16T04:47:17.887032+00:00"
model: "Qwen/Qwen3-Coder-Next-FP8"
schema_version: 1
sha256: "51a43b8567809d4b"
---
# Model
1. **Purpose**
This module file (`ChannelSetting.cs`) is a placeholder or stub within the `GroupList.Model` namespace and currently contains no implementation. As written, it serves no functional purpose in the system—it declares no types, properties, methods, or logic—and appears to be an incomplete or scaffolding file awaiting future development.
2. **Public Interface**
No public types, functions, classes, or methods are defined in this file. The namespace `GroupList.Model` and the file itself are empty aside from the `using` directive.
3. **Invariants**
No invariants can be determined, as no logic or state is present.
4. **Dependencies**
- **Imports**: The file imports `DTS.Common.Interface.Channels`, indicating a *potential* future dependency on channel-related abstractions defined in that namespace (e.g., interfaces like `IChannel`, `ChannelType`, etc.). However, no types from this namespace are currently used.
- **No internal dependencies**: No other modules, namespaces, or types within the codebase are referenced.
5. **Gotchas**
- **Empty file**: The file is functionally inert; any assumptions about its role (e.g., modeling channel settings for groups) are speculative.
- **Misleading namespace**: The `GroupList.Model` namespace suggests this file may eventually model channel-related configuration for groups, but no such contract exists yet.
- **No validation or safety guarantees**: Since no code exists, no correctness properties, thread-safety, or lifecycle rules apply.
- **Potential tech debt**: If this file was intended to be a model for channel settings but remains unimplemented, it may indicate pending work or incomplete refactoring.
None identified beyond what is evident from the empty structure.

View File

@@ -0,0 +1,65 @@
---
source_files:
- DataPRO/Modules/Groups/GroupList/Properties/Settings.Designer.cs
- DataPRO/Modules/Groups/GroupList/Properties/AssemblyInfo.cs
generated_at: "2026-04-16T04:47:23.586422+00:00"
model: "Qwen/Qwen3-Coder-Next-FP8"
schema_version: 1
sha256: "1fd4dfff93afcd30"
---
# Properties
## Documentation Page: `GroupList.Properties.Settings`
---
### 1. **Purpose**
This module defines the application settings class (`Settings`) for the `GroupList` module within the DataPRO system. It provides a strongly-typed, thread-safe accessor (`Default`) to application-level configuration values stored in the standard .NET configuration system (e.g., `app.config`). As a generated class, its purpose is to abstract access to user- or application-scoped settings, though **no settings properties are defined in the provided source**—they are presumably declared in the corresponding `.settings` file (not included here) and auto-generated into this class.
---
### 2. **Public Interface**
The following members are explicitly defined in the provided source:
- **`public static Settings Default { get; }`**
A static property returning the singleton instance of the `Settings` class. The instance is created via `ApplicationSettingsBase.Synchronized(...)`, ensuring thread-safe access to settings values.
- *Type*: `Settings` (a subclass of `System.Configuration.ApplicationSettingsBase`)
- *Behavior*: Returns the same instance on every call (singleton pattern). Values are loaded from configuration at first access.
> **Note**: No additional public properties, methods, or fields are visible in the provided source. Any settings (e.g., `string GroupFilter`, `int MaxGroups`) would be auto-generated by the Visual Studio Settings Designer and are *not present in this file*.
---
### 3. **Invariants**
- The `Settings` class is **sealed** and **partial**, with auto-generation metadata (`CompilerGeneratedAttribute`, `GeneratedCodeAttribute`).
- The `Default` instance is **synchronized** using `ApplicationSettingsBase.Synchronized(...)`, implying thread-safe reads/writes to settings values.
- The class inherits from `ApplicationSettingsBase`, so it adheres to standard .NET settings semantics:
- Settings are persisted per-user (user-scoped) and/or per-application (application-scoped), depending on their `UserScopedSetting` or `ApplicationScopedSetting` attributes (not visible here).
- No explicit validation or invariants are enforced in the provided code.
---
### 4. **Dependencies**
**Dependencies *of* this module**:
- `System.Configuration` (via `ApplicationSettingsBase`)
- `System.Runtime.CompilerServices` (for `CompilerGeneratedAttribute`)
- `System.CodeDom.Compiler` (for `GeneratedCodeAttribute`)
**Dependencies *on* this module**:
- Not inferable from this file alone. However, any consumer of `GroupList.Properties.Settings.Default` would depend on this module.
- The assembly `GroupList` (as per `AssemblyInfo.cs`) is the containing assembly for this class.
---
### 5. **Gotchas**
- **No settings are defined in this file** — the `Settings` class is empty *in the provided source*. Actual settings (e.g., `public string SomeSetting { get; set; }`) must be declared in the `.settings` designer file (e.g., `Settings.settings`) and regenerated into this class.
- **Do not manually edit this file** — the auto-generated header explicitly warns: *"Changes to this file may cause incorrect behavior and will be lost if the code is regenerated."*
- The `defaultInstance` is initialized synchronously via `Synchronized(...)`, but **no default values are set here** — defaults come from the `Settings.settings` designer or `app.config`.
- The assembly GUID (`95d3c318-8333-4d0b-b508-21d654404443`) and version (`1.0.0.0`) suggest this is an early or placeholder version; verify versioning strategy if upgrading or refactoring.
- **None identified from source alone** — beyond the above, behavior is standard for .NET `ApplicationSettingsBase` subclasses.
---
> **Recommendation for Developers**:
> To inspect or modify settings, open the `Settings.settings` file in Visual Studio (if present in the project). This will regenerate `Settings.Designer.cs`. Always verify the actual settings schema in the designer or `app.config` before relying on specific property names or values.

View File

@@ -0,0 +1,84 @@
---
source_files:
- DataPRO/Modules/Groups/GroupList/Resources/TranslateExtension.cs
- DataPRO/Modules/Groups/GroupList/Resources/StringResources.Designer.cs
generated_at: "2026-04-16T04:47:13.405699+00:00"
model: "Qwen/Qwen3-Coder-Next-FP8"
schema_version: 1
sha256: "30656f2a830a5a31"
---
# Resources
## Documentation: `TranslateExtension` Markup Extension
---
### 1. **Purpose**
This module provides a WPF `MarkupExtension` (`TranslateExtension`) to enable declarative, data-bound localization of UI strings directly in XAML. It allows developers to reference localized string resources by key (e.g., `{local:Translate Name}`) and automatically retrieve the appropriate localized value at runtime from the `StringResources` resource class. Its role is to bridge XAML markup with the applications resource management system, supporting multi-language UI without requiring code-behind or manual string lookups.
---
### 2. **Public Interface**
#### `TranslateExtension` class
- **Namespace**: `GroupList`
- **Base class**: `System.Windows.Markup.MarkupExtension`
- **Attributes**:
- `[MarkupExtensionReturnType(typeof(string))]` — Indicates the extension returns a `string`.
##### Constructor
- **`TranslateExtension(string key)`**
Initializes a new instance with the specified resource key. The key is stored in the private readonly field `_key`.
- *Parameter*: `key` — The string key used to look up a localized resource (e.g., `"Name"`, `"Description"`).
- *Behavior*: Validates `key` is non-null/non-empty at *runtime* during `ProvideValue` (not in constructor).
##### Overridden Method
- **`public override object ProvideValue(IServiceProvider serviceProvider)`**
Returns the localized string for `_key`, or a fallback value if the key is missing or lookup fails.
- *Behavior*:
- If `_key` is `null` or empty → returns `"#stringnotfound#"`.
- Otherwise, calls `StringResources.ResourceManager.GetString(_key)`.
- If the result is non-null → returns the localized string.
- If the result is `null` → returns `"#stringnotfound# " + _key` (e.g., `"#stringnotfound# MissingKey"`).
- *Note*: The `serviceProvider` parameter is unused in the implementation.
---
### 3. **Invariants**
- **Resource key must be a valid string resource name** (e.g., `"Name"`, `"Channels"`). Invalid keys (e.g., `"UnknownKey"`) will not throw but will return the fallback string `"#stringnotfound# UnknownKey"`.
- **Case sensitivity**: Resource keys are case-sensitive (e.g., `"name"``"Name"`).
- **No runtime exceptions** are thrown for missing keys; the extension gracefully degrades to the `NotFound` prefix.
- **Thread-safety**: Relies on `StringResources.ResourceManager`, which is thread-safe per .NET documentation (uses lazy initialization and locking internally).
- **XAML-only usage**: Intended for use in XAML markup only; not designed for programmatic invocation outside markup extension context.
---
### 4. **Dependencies**
#### Dependencies *on* this module:
- **WPF framework**: Requires `System.Windows.Markup` and `System.Windows` (for `MarkupExtension`).
- **`StringResources` class**:
- Defined in `GroupList.Resources` namespace.
- Auto-generated from `.resx` files (not shown here, but implied by `StringResources.Designer.cs`).
- Provides the underlying `ResourceManager` and strongly-typed properties (e.g., `StringResources.Name`).
- **No external third-party dependencies** beyond .NET Framework 4.0+ (per runtime version in `StringResources.Designer.cs`).
#### Dependencies *of* this module:
- None beyond standard WPF/.NET Framework types.
- **Consumers**: Any XAML file in the `GroupList` module (e.g., `GroupList/Resources/TranslateExtension.cs` is likely used in `*.xaml` files in the same module) to localize UI elements.
---
### 5. **Gotchas**
- **No compile-time safety for keys**: Using an incorrect key (e.g., `"Nam"` instead of `"Name"`) will not cause a compile error—only a runtime fallback string (`#stringnotfound# Nam`).
- **No culture switching support in extension itself**: While `StringResources.Culture` can be set globally (e.g., via `Thread.CurrentThread.CurrentUICulture`), the extension does not expose or manage culture selection.
- **Hardcoded fallback string**: The `"#stringnotfound#"` prefix is fixed and may be visible to end-users if keys are missing.
- **No validation of key format**: Keys like `""` (empty string) or `" "` (whitespace) are treated as invalid and return `NotFound`, but no explicit error logging occurs.
- **Auto-generated resource class**: `StringResources.Designer.cs` is auto-generated; manual edits will be overwritten. Resource keys must match entries in the corresponding `.resx` file (not visible here).
- **No support for parameterized strings**: The extension only supports simple key-based lookups (e.g., `"{local:Translate Name}"`). It cannot handle format strings like `"{0} is {1}"` (e.g., `String.Format`-style substitution).
None identified beyond the above.

View File

@@ -0,0 +1,71 @@
---
source_files:
- DataPRO/Modules/Groups/GroupList/View/GroupListView.xaml.cs
generated_at: "2026-04-16T04:47:32.695881+00:00"
model: "Qwen/Qwen3-Coder-Next-FP8"
schema_version: 1
sha256: "b80e86a41868129e"
---
# View
### **Purpose**
This module implements the WPF UI view (`GroupListView`) for displaying a list of groups in a tabular format using a `ListView` with `GridView`. It serves as the concrete implementation of the `IGroupListView` interface, bridging user interactions (sorting, filtering, double-clicking) with the corresponding `IGroupListViewModel` logic. Its role is to render group data and translate UI events into view model commands, adhering to the MVVM pattern.
---
### **Public Interface**
The class itself is public and implements `IGroupListView` (interface defined elsewhere), but no explicit public methods are declared in this file. All behavior is exposed via event handlers wired in XAML (not shown). The following private methods handle UI events:
- **`ListViewHeader_Click(object sender, RoutedEventArgs e)`**
Handles clicks on standard `GridViewColumnHeader` elements. Extracts the `Tag` (column identifier) and `DataContext` (view model), then invokes `viewModel.Sort(columnTag, true)` to sort ascending.
- **`GridViewColumnHeaderSearchable_OnSearch(object sender, RoutedEventArgs e)`**
Handles search input events on a custom `GridViewColumnHeaderSearchable` control. Extracts the `searchTerm` from `e.OriginalSource` and the `columnTag` from the senders `Tag`, then calls `vm.Filter(columnTag, searchTerm)`.
- **`GridViewColumnHeader_OnClick(object sender, RoutedEventArgs e)`**
Handles clicks on `GridViewColumnHeaderSearchable` controls (fallback to standard headers). Attempts to extract `columnTag` from the sender or via `Utils.FindChild`, then calls `vm?.Sort(columnTag, true)`.
- **`MouseDoubleClick(object sender, MouseButtonEventArgs e)`**
Handles double-clicks on the `ListView`. Determines the clicked item index using `GetCurrentIndex`, and if valid, invokes `vm.MouseDoubleClick(index)` on the view model.
- **`GetCurrentIndex(GetPositionDelegate getPosition, ListView lv)`** *(private static)*
Iterates over `ListView` items, checks if the mouse position (via delegate) is within the bounds of each `ListViewItem`, and returns the zero-based index of the first matching item, or `-1` if none.
- **`GetListViewItem(int index, ListView lv)`** *(private static)*
Retrieves the `ListViewItem` container for a given item index using `ItemContainerGenerator.ContainerFromIndex`.
- **`IsMouseOverTarget(Visual target, GetPositionDelegate getPosition)`** *(private static)*
Computes the visual bounds of a `Visual` and checks if the mouse position (from the delegate) lies within those bounds.
---
### **Invariants**
- The `DataContext` of `GroupListView` **must** be an instance implementing `IGroupListViewModel`; otherwise, casting in event handlers (`(IGroupListViewModel)DataContext`) will fail at runtime.
- Column identifiers (`Tag` properties on `GridViewColumnHeader` or `GridViewColumnHeaderSearchable`) **must** be non-null and consistent with expected keys used by `IGroupListViewModel.Sort` and `IGroupListViewModel.Filter`.
- The `ListView` must contain items compatible with the view models data source; `GetCurrentIndex` assumes `lv.Items.Count` is stable during iteration (no concurrent modifications).
- Double-click events only trigger `MouseDoubleClick` on the view model if the mouse is over a valid, generated `ListViewItem`.
---
### **Dependencies**
**Imports/References (from source):**
- `DTS.Common.Interface.Groups.GroupTemplateList` (namespace, likely contains `IGroupListView` and related interfaces)
- `DTS.Common.Interface.Groups.GroupList` (contains `IGroupListViewModel`)
- `DTS.Common.Controls` (contains `GridViewColumnHeaderSearchable`)
- `DTS.Common.Utils` (contains `Utils.FindChild<T>`)
- Standard WPF namespaces (`System.Windows.*`)
**Depended upon by:**
- XAML file `GroupListView.xaml` (not provided, but implied by `InitializeComponent()` and event bindings).
- Likely consumed by a DI container or view factory that resolves `IGroupListView` and sets its `DataContext` to an `IGroupListViewModel` implementation.
---
### **Gotchas**
- **Ambiguous event routing**: `ListViewHeader_Click` and `GridViewColumnHeader_OnClick` both handle header clicks but for different header types. If a `GridViewColumnHeaderSearchable` is clicked, `GridViewColumnHeader_OnClick` runs, but `ListViewHeader_Click` may also fire depending on XAML event wiring—risk of duplicate sorting/filtering if not coordinated.
- **Unsafe cast in `ListViewHeader_Click`**: Assumes `colHeader.DataContext` is `IGroupListViewModel`; if the view is reused or bound incorrectly, this throws `InvalidCastException`.
- **Fallback logic in `GridViewColumnHeader_OnClick`**: Uses `Utils.FindChild<GridViewColumnHeaderSearchable>` to extract `Tag` if the sender is not already a `GridViewColumnHeaderSearchable`. This implies tight coupling to a specific visual tree structure and may break if the control template changes.
- **Mouse position calculation**: `GetCurrentIndex` uses `VisualTreeHelper.GetDescendantBounds`, which may be expensive or inaccurate if items are virtualized (e.g., with `VirtualizingStackPanel`).
- **No null-safety for `vm`**: In `MouseDoubleClick`, `vm?.Sort(...)` uses null-conditional, but `vm.MouseDoubleClick(index)` does not—risk of `NullReferenceException` if `DataContext` is unset.
- **Search term source**: `GridViewColumnHeaderSearchable_OnSearch` casts `e.OriginalSource` to `string` for `searchTerm`, implying the event args `OriginalSource` *is* the search string—this is non-standard and highly dependent on how `GridViewColumnHeaderSearchable` raises the event.

View File

@@ -0,0 +1,149 @@
---
source_files:
- DataPRO/Modules/Groups/GroupList/ViewModel/GroupListViewModel.cs
generated_at: "2026-04-16T04:47:12.822358+00:00"
model: "Qwen/Qwen3-Coder-Next-FP8"
schema_version: 1
sha256: "efb48e4bf6e2c1c7"
---
# ViewModel
## Documentation: `GroupListViewModel`
---
### 1. Purpose
`GroupListViewModel` is the core view model for the Group List UI module. It manages the display, filtering, sorting, selection, and lifecycle of `IGroup` entities within the application. It acts as the intermediary between the `IGroupListView` view and the underlying group data model (`Group`), handling user interactions (e.g., double-click to edit, filtering/sorting), coordinating background operations (e.g., loading test setup lists), and publishing/subscribing to domain events via `IEventAggregator`. It supports both single- and multi-select group operations and integrates with the Prism region management and Unity DI container.
---
### 2. Public Interface
#### Constructors
- **`GroupListViewModel(IGroupListView view, IRegionManager regionManager, IEventAggregator eventAggregator, IUnityContainer unityContainer)`**
Initializes the view model. Sets up the view binding, event subscriptions (e.g., `RaiseNotification`, `BusyIndicatorChangeNotification`), and initializes `SelectedGroupItems` as an `ObservableCollection<IGroup>`.
#### Public Methods
- **`void ClearAllFilters()`**
Clears all per-field filter terms stored in `_filterByField`.
- **`void MouseDoubleClick(int index)`**
If a single group is selected and `index` is valid, publishes a `GroupListEditGroupEvent` with the selected groups `Id`.
- **`void Filter(string term)`**
Sets `CurrentSearchTerm` to `term`, then re-applies sorting (which triggers filtering and re-sorting of `Groups`).
- **`IGroup GetGroup(int? id, bool updateTags = true)`**
Returns the group with the given `id` (if `id >= 0`). If `updateTags` is `true`, updates the global tag cache before fetching. Returns a new empty `Group()` if not found.
- **`IGroup GetGroup(string displayName)`**
Returns the *non-embedded* group matching `displayName`, preferring non-embedded over embedded groups (for backward compatibility with pre-2.0 group types). Returns `null` if none found.
- **`IGroup[] GetGroups(int[] ids)`**
Returns an array of groups whose `Id`s are in `ids`, preserving order only in that groups *not* in `ids` are removed (i.e., order is not guaranteed to match `ids` order).
- **`IGroup[] GetAllGroups()`**
Returns *all* groups (via `Group.GetAllGroups()`), unfiltered and unsorted.
- **`void DeleteGroups(int[] ids)`**
Deletes groups with given `ids`. For each group, nullifies `StaticGroupId` on all related embedded groups, deletes the group, updates `AllGroups`, and re-applies filters. Publishes errors via `PageErrorEvent`.
- **`IGroup CreateGroup()`**
Returns a new unsaved `Group(true)` (likely a new transient instance).
- **`IGroup CreateGroup(SqlDataReader, List<string>, List<int>)`**
Constructs a `Group` from a data reader and hardware/test setup lists.
- **`IGroup CreateGroup(IGroupDbRecord, List<string>, List<int>)`**
Constructs a `Group` from a database record and hardware/test setup lists.
- **`IGroup CreateGroup(List<string>)`**
Constructs a new `Group` with the given `includedHardwareStringList`.
- **`void Filter(object tag, string term)`**
Parses `tag` as a `GroupFields` enum value; if successful, stores `term` in `_filterByField[tag]`, sets `_sortField`, and calls `Filter(term)`.
- **`void OnSetActive(object page, bool groupTile, object o)`**
Sets `Page`, filters `AllGroups` to groups compatible with the current users role/tags, and either calls `GetTestSetupListsAsync()` (if `groupTile == true`) or `Sort()`.
- **`private void GetTestSetupListsAsync()`**
Runs asynchronously: sets app status to busy, iterates over `AllGroups` in parallel to call `g.SetTestSetupLists()`, then re-sorts on the UI thread.
- **`void Unset()`**
Clears `AllGroups`, `Groups`, filters, and publishes `ListViewStatusEvent` with status `Unloaded`.
- **`void Sort(object o, bool bColumnClick)`**
Sorts `AllGroups` by `GroupFields` (parsed from `o`), applies `CurrentSearchTerm` and per-field filters (`GroupFilter`), and sets `Groups`. Updates sort direction on column click.
- **`void Cleanup()` / `Task CleanupAsync()` / `void Initialize()` / `Task InitializeAsync()` / `void Activated()`**
No-op stubs; likely required by Prism or custom interfaces but not implemented.
#### Public Properties
- **`IGroupListView View`** Bound view instance.
- **`InteractionRequest<Notification> NotificationRequest`** For displaying notification popups.
- **`InteractionRequest<Confirmation> ConfirmationRequest`** For confirmation dialogs.
- **`event PropertyChangedEventHandler PropertyChanged`** Implements `INotifyPropertyChanged`.
- **`ObservableCollection<IGroup> SelectedGroupItems`** Tracks selected groups; raises `GroupListGroupSelectedEvent` on change.
- **`IGroup[] Groups`** Currently displayed (filtered + sorted) groups.
- **`bool IsBusy`** Bound to busy indicator; updated via `OnBusyIndicatorNotification`.
- **`bool IsMenuIncluded` / `bool IsNavigationIncluded`** UI toggle flags.
- **`string ListViewId`** `"GroupListView"`; used for event scoping.
- **`int SelectedGroupIndex`** Index of selected group in `Groups`; default `-1`.
#### Private Properties (used in logic)
- **`_filterByField: Dictionary<GroupFields, string>`** Per-field filter terms.
- **`CurrentSearchTerm: string`** Global search term.
- **`_sortField: GroupFields`** Current sort field (default `LastModified`).
- **`_sortAscending: bool`** Sort direction (default `false` = descending).
- **`_comparer: GroupComparer`** Comparison logic for sorting.
---
### 3. Invariants
- `Groups` is always a filtered and sorted subset of `AllGroups`.
- `AllGroups` is always filtered by user role and tag compatibility (via `currentUser.TagCompatible(@group.TagIDs)`).
- `SelectedGroupItems` is an `ObservableCollection`; changes trigger `GroupListGroupSelectedEvent`.
- `_filterByField` stores per-field filter terms; `CurrentSearchTerm` is a global term applied in `Sort`.
- `Groups` is sorted by `_sortField` in `_sortAscending` order using `GroupComparer`.
- `IsBusy` is updated via `BusyIndicatorChangeNotification` event (thread-safe subscription on `PublisherThread`).
- `SelectedGroupItems.CollectionChanged` handler skips updates during flagged "updating" states (via `DTS.Common.Enums.SelectedItemsStatus.GetUpdating`).
---
### 4. Dependencies
#### Imports / Dependencies Used
- **Prism**: `IEventAggregator`, `IRegionManager`, `Unity`, `Prism.Events`, `Prism.Regions`, `Prism.Interactivity`.
- **DTS Common Libraries**:
- `DTS.Common.Events.*` (e.g., `RaiseNotification`, `BusyIndicatorChangeNotification`, `PageErrorEvent`, `AppStatusEvent`, `ProgressBarEvent`, `ListViewStatusEvent`, `GroupListEditGroupEvent`, `GroupListGroupSelectedEvent`)
- `DTS.Common.Interface.Groups.*` (`IGroup`, `IGroupListView`, `IGroupListViewModel`)
- `DTS.Common.Enums.Groups.GroupList.*` (`GroupFields`, `ListViewStatusArg`)
- `DTS.Common.Storage.*` (`DbOperations`)
- `DTS.Slice.Users.User`
- `DTS.Common.Classes.Tags.TagsInstance`
- **.NET**: `System.ComponentModel`, `System.Collections.ObjectModel`, `System.Threading.Tasks`, `System.Linq`, `System.Collections.Specialized`, `System.Windows`, `System.Data.SqlClient.SqlDataReader`.
#### Dependencies on This Module
- `GroupListViewModel` is exported via MEF (`[PartCreationPolicy(CreationPolicy.Shared)]`) and likely consumed by DI container resolution for `IGroupListViewModel`.
- `IGroupListView` must be provided at construction (likely via Prism view injection).
- Other modules subscribe to events it publishes: `GroupListEditGroupEvent`, `GroupListGroupSelectedEvent`, `PageErrorEvent`, `ListViewStatusEvent`.
---
### 5. Gotchas
- **Sorting logic comment**: The `Sort` method contains commented-out logic suggesting a potential bug or inconsistency in handling already-sorted lists (especially for `LastModified`). This may cause unexpected sort direction toggling.
- **`GetGroup(string displayName)` behavior**: Returns only non-embedded groups *if* any exist; otherwise returns `null`. This may hide embedded groups unexpectedly.
- **`GetGroups(int[] ids)` order**: Output order is not guaranteed to match input `ids` order (uses `Contains` in reverse iteration).
- **`SelectedGroupItems` setter**: Unsubscribes from `CollectionChanged` only if `_selectedGroupItems` was non-null *before* assignment. If `SelectedGroupItems` is set multiple times, old handlers may leak if not handled carefully.
- **`OnSetActive` filtering**: Filtering by user role/tag compatibility happens *only* in `OnSetActive`, not in `Filter` or `Sort`. Thus, filtering/sorting operates on already-filtered `AllGroups`.
- **`GetTestSetupListsAsync`**: Runs `SetTestSetupLists()` in parallel (`AsParallel().ForAll`), but UI thread re-sort is done via `Dispatcher.Invoke`. Potential for race conditions if `SetTestSetupLists()` modifies group state accessed during sort.
- **`GroupFilter` case sensitivity**: Uses `CompareOptions.OrdinalIgnoreCase` for all fields, including `LastModified` (stringified) — may yield unexpected matches (e.g., "12" matches "12" but also "12" in "12/12/2023").
- **No validation on `DeleteGroups`**: Does not check if groups are in use (e.g., by tests); relies on underlying `Group.Delete(id)` to handle errors (which are then published via `PageErrorEvent`).
- **`IsDirty` property**: Declared but never set; always `false`.
- **`Cleanup()`/`Initialize()` stubs**: All `Initialize*`, `Cleanup*`, and `Activated()` methods are empty — may indicate incomplete implementation or reliance on external lifecycle management.
None identified beyond those above.