172 lines
9.8 KiB
Markdown
172 lines
9.8 KiB
Markdown
|
|
---
|
|||
|
|
source_files:
|
|||
|
|
- DataPRO/Modules/RegionOfInterest/RegionOfInterestChannels/View/StateListIndexConverter.cs
|
|||
|
|
- DataPRO/Modules/RegionOfInterest/RegionOfInterestChannels/View/RegionOfInterestChannelsView.xaml.cs
|
|||
|
|
- DataPRO/Modules/RegionOfInterest/RegionOfInterestChannels/View/RegionOfInterestChannelsDataTemplateSelector.cs
|
|||
|
|
- DataPRO/Modules/RegionOfInterest/RegionOfInterestChannels/View/GridViewColumns.cs
|
|||
|
|
generated_at: "2026-04-16T04:34:31.801575+00:00"
|
|||
|
|
model: "Qwen/Qwen3-Coder-Next-FP8"
|
|||
|
|
schema_version: 1
|
|||
|
|
sha256: "b5797f8ba3342db7"
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
# View
|
|||
|
|
|
|||
|
|
## Documentation: RegionOfInterestChannels View Layer
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### 1. Purpose
|
|||
|
|
|
|||
|
|
This module provides WPF UI infrastructure for rendering and interacting with a `GridView`-based view of region-of-interest (ROI) channel data. It enables dynamic column generation from a data source, per-column type-aware cell templating (e.g., `TextBlock` vs. `CheckBox`), column header interactivity (sorting, filtering, multi-select), and automatic column width adjustment for content-fit. It serves as the view layer in an MVVM pattern, implementing `IRegionOfInterestChannelsView` and delegating user actions to an associated `IRegionOfInterestChannelsViewModel`.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### 2. Public Interface
|
|||
|
|
|
|||
|
|
#### `StateListIndexConverter`
|
|||
|
|
**Namespace:** `RegionOfInterestChannels`
|
|||
|
|
**Implements:** `IMultiValueConverter`
|
|||
|
|
|
|||
|
|
- **`Convert(object[] values, Type targetType, object parameter, CultureInfo culture)`**
|
|||
|
|
Converts a 2-element multi-value binding: expects `[0] = IEnumerable<State>`, `[1] = int index`. Returns the `State` at the given index in the collection, or `null` if inputs are invalid or index is out of bounds.
|
|||
|
|
**Note:** Does *not* support `ConvertBack`.
|
|||
|
|
|
|||
|
|
#### `RegionOfInterestChannelsView`
|
|||
|
|
**Namespace:** `RegionOfInterestChannels`
|
|||
|
|
**Implements:** `IRegionOfInterestChannelsView` (partial class)
|
|||
|
|
|
|||
|
|
- **`RegionOfInterestChannelsView()`**
|
|||
|
|
Constructor; calls `InitializeComponent()`.
|
|||
|
|
|
|||
|
|
- **`GridViewColumnHeader_OnClick(object sender, RoutedEventArgs e)`**
|
|||
|
|
Handles column header click events. Extracts the `Tag` from the clicked header (supporting `GridViewColumnHeaderSearchable` or `GridViewColumnHeaderSelectable` types), then invokes `vm.Sort(columnTag, true)` on the `DataContext` if it implements `IRegionOfInterestChannelsViewModel`.
|
|||
|
|
|
|||
|
|
- **`TextBlock_Loaded(object sender, RoutedEventArgs e)`**
|
|||
|
|
Adjusts column width dynamically when a `TextBlock` in a cell is loaded and its content width exceeds the column’s current width (only if `Width` is `double.NaN`). Finds the column index via visual tree traversal and temporarily sets `Width = ActualWidth`, then resets to `NaN` to trigger remeasurement.
|
|||
|
|
|
|||
|
|
#### `RegionOfInterestChannelsDataTemplateSelector`
|
|||
|
|
**Namespace:** `RegionOfInterestChannels`
|
|||
|
|
**Inherits:** `DataTemplateSelector`
|
|||
|
|
|
|||
|
|
- **`TextBlockDataTemplate` / `CheckBoxDataTemplate`**
|
|||
|
|
Public properties for the two templates to select between.
|
|||
|
|
|
|||
|
|
- **`SelectTemplate(object item, DependencyObject container)`**
|
|||
|
|
Selects a `DataTemplate` based on the cell’s underlying data type:
|
|||
|
|
- If `item` is a `ChannelEnabler`, inspects the column’s header (as property name) to determine the property type on `item`.
|
|||
|
|
- If type is `string` → returns `TextBlockDataTemplate`; sets `ContentPresenter.Tag` to the property value.
|
|||
|
|
- If type is `boolean` (or other) → returns `CheckBoxDataTemplate`; sets `ContentPresenter.Tag` to the *relative index* of the column among boolean columns (`i - checkStartColumn`).
|
|||
|
|
- If `item` is not a `ChannelEnabler`, defaults to `TextBlockDataTemplate`.
|
|||
|
|
**Note:** Uses reflection (`GetProperty`, `GetValue`, `GetType`) on the `item` and `presenter.Columns[i].Header`. Assumes header text matches property names (with spaces removed).
|
|||
|
|
|
|||
|
|
#### `GridViewColumns` (Static Class)
|
|||
|
|
**Namespace:** `RegionOfInterestChannels`
|
|||
|
|
**Purpose:** Attached property provider for configuring `GridView` columns dynamically from an `ICollectionView` source.
|
|||
|
|
|
|||
|
|
##### Attached Properties:
|
|||
|
|
- **`ColumnsSource`** (`object`)
|
|||
|
|
Source collection (typically `ICollectionView`) whose items define columns. Changing this clears and recreates columns.
|
|||
|
|
|
|||
|
|
- **`CellDataTemplateSelector`** (`DataTemplateSelector`)
|
|||
|
|
Optional selector used to choose per-cell templates.
|
|||
|
|
|
|||
|
|
- **`CellDataTemplate`** (`DataTemplate`)
|
|||
|
|
Fallback template for cells if `CellDataTemplateSelector` is `null`.
|
|||
|
|
|
|||
|
|
- **`HeaderTextMember`** (`string`)
|
|||
|
|
Property name on column source items whose value is used for column header text.
|
|||
|
|
|
|||
|
|
- **`DisplayMember`** (`string`)
|
|||
|
|
Property name on column source items whose value is used as the binding path for cell content (currently commented out in `CreateColumn`).
|
|||
|
|
|
|||
|
|
##### Key Methods (Internal/Static):
|
|||
|
|
- **`CreateColumn(GridView gridView, object columnSource)`**
|
|||
|
|
Constructs a `GridViewColumn` from a `columnSource` item:
|
|||
|
|
- Reads `HeaderTextMember` and `DisplayMember` from `gridView`.
|
|||
|
|
- Determines column header type based on the *column source item’s* `MemberType` property (retrieved via `GetPropertyValue(columnSource, "MemberType")`):
|
|||
|
|
- `typeof(bool)` → `GridViewColumnHeaderSelectable` with `SelectAll` event handler.
|
|||
|
|
- `typeof(string)` → `GridViewColumnHeaderSearchable` with `Search` and `ClickHandler` events.
|
|||
|
|
- Otherwise → plain `string` header.
|
|||
|
|
- Assigns `CellTemplateSelector`, `CellTemplate`, and (commented-out) `DisplayMemberBinding`.
|
|||
|
|
|
|||
|
|
- **`GetPropertyValue(object obj, string propertyName)`**
|
|||
|
|
Helper: uses reflection to get `propertyName` from `obj`.
|
|||
|
|
|
|||
|
|
- **`GetArrayIndex(string fromString)`**
|
|||
|
|
Parses an integer from a string like `"SomeProperty[3]"`, returning `-1` on failure.
|
|||
|
|
|
|||
|
|
##### Event Handlers (Internal):
|
|||
|
|
- **`ColumnsSource_CollectionChanged(...)`**
|
|||
|
|
Syncs `gridView.Columns` with changes (`Add`, `Remove`, `Replace`, `Move`, `Reset`) in the `ColumnsSource` collection.
|
|||
|
|
|
|||
|
|
- **`GridViewColumnHeaderSearchable_OnSearch(...)`**
|
|||
|
|
Invokes `vm.Filter(columnTag, searchTerm)`.
|
|||
|
|
|
|||
|
|
- **`GridViewColumnHeader_OnClick(...)`**
|
|||
|
|
Invokes `vm.Sort(columnTag, true)` (same behavior as `RegionOfInterestChannelsView.GridViewColumnHeader_OnClick`).
|
|||
|
|
|
|||
|
|
- **`GridViewColumnHeaderSelectable_OnSelectAll(...)`**
|
|||
|
|
Parses column index from `columnTag` (e.g., `"Enabled[2]"`), then invokes `vm.SelectAll(columnIndex, isSelected)`.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### 3. Invariants
|
|||
|
|
|
|||
|
|
- **Column Source Contract:**
|
|||
|
|
Items in the `ColumnsSource` collection must have:
|
|||
|
|
- A property named `"MemberType"` (e.g., `typeof(string)` or `typeof(bool)`), used to determine header type.
|
|||
|
|
- Properties named by `HeaderTextMember` and `DisplayMember` (if set), or default to `null`.
|
|||
|
|
|
|||
|
|
- **Header Tag Format:**
|
|||
|
|
For `GridViewColumnHeaderSelectable`, the `Tag` must be a string containing an array index in brackets (e.g., `"Enabled[0]"`) to be parsed by `GetArrayIndex`.
|
|||
|
|
|
|||
|
|
- **`StateListIndexConverter` Input Expectation:**
|
|||
|
|
Exactly two values: an `IEnumerable<State>` and an `int`. Any deviation yields `null`.
|
|||
|
|
|
|||
|
|
- **`ChannelEnabler` Assumption:**
|
|||
|
|
`RegionOfInterestChannelsDataTemplateSelector.SelectTemplate` assumes `item` is a `ChannelEnabler` when inspecting property types. Behavior for other types is not defined in source.
|
|||
|
|
|
|||
|
|
- **Column Width Adjustment Scope:**
|
|||
|
|
`TextBlock_Loaded` only adjusts the *first* matching `ContentPresenter` in the row. If multiple `TextBlock`s exist per row, only the first is considered.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### 4. Dependencies
|
|||
|
|
|
|||
|
|
#### Dependencies *of* this module:
|
|||
|
|
- **WPF Framework:** `System.Windows`, `System.Windows.Controls`, `System.Windows.Data`, `System.Windows.Media`.
|
|||
|
|
- **Common Libraries:**
|
|||
|
|
- `DTS.Common.Controls` (for `GridViewColumnHeaderSearchable`, `GridViewColumnHeaderSelectable`)
|
|||
|
|
- `DTS.Common.Interface.RegionOfInterest.RegionOfInterestChannels` (for `IRegionOfInterestChannelsView`, `IRegionOfInterestChannelsViewModel`)
|
|||
|
|
- `DTS.Common.Utils` (for `Utils.FindChild<T>`)
|
|||
|
|
|
|||
|
|
#### Dependencies *on* this module:
|
|||
|
|
- **`IRegionOfInterestChannelsViewModel`** must be implemented by the `DataContext` for `RegionOfInterestChannelsView` to handle sorting, filtering, and selection.
|
|||
|
|
- **`State` type** is used by `StateListIndexConverter` (imported from elsewhere in the codebase).
|
|||
|
|
- **`ChannelEnabler` type** is used by `RegionOfInterestChannelsDataTemplateSelector` (imported from elsewhere).
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### 5. Gotchas
|
|||
|
|
|
|||
|
|
- **`ConvertBack` is Unimplemented:**
|
|||
|
|
`StateListIndexConverter.ConvertBack` throws `NotImplementedException`. It is strictly a one-way converter.
|
|||
|
|
|
|||
|
|
- **`DisplayMemberBinding` is Commented Out:**
|
|||
|
|
In `CreateColumn`, the line setting `column.DisplayMemberBinding` is commented. Cell binding relies entirely on `CellDataTemplateSelector` or `CellDataTemplate`. This may cause confusion if `DisplayMember` is set but unused.
|
|||
|
|
|
|||
|
|
- **Header Property Name Matching:**
|
|||
|
|
`RegionOfInterestChannelsDataTemplateSelector.SelectTemplate` uses `presenter.Columns[i].Header.ToString().Replace(" ", "")` to match property names. If headers contain non-alphanumeric characters beyond spaces, reflection will fail.
|
|||
|
|
|
|||
|
|
- **`MemberType` Property Requirement:**
|
|||
|
|
Column source items *must* expose a `"MemberType"` property (via reflection). If missing or non-`Type`, `columnPropertyType` becomes `null`, causing `checkStartColumn` to be set and defaulting to `CheckBoxDataTemplate`.
|
|||
|
|
|
|||
|
|
- **Duplicate Event Handlers Risk:**
|
|||
|
|
`ColumnsSource_CollectionChanged` adds handlers to `ICollectionView.CollectionChanged` but does not deduplicate if `ColumnsSource` is reassigned to the *same* collection instance. This could lead to multiple handler invocations per event.
|
|||
|
|
|
|||
|
|
- **`TextBlock_Loaded` Fragility:**
|
|||
|
|
Relies on a specific visual tree structure (`GridViewRowPresenter` → `ContentPresenter` → `TextBlock`). Changes to control templates may break the width adjustment logic.
|
|||
|
|
|
|||
|
|
- **No Null-Safety for `e?.OriginalSource`:**
|
|||
|
|
In `GridViewColumnHeaderSelectable_OnSelectAll`, `(bool)(e?.OriginalSource ?? false)` may throw `InvalidCastException` if `e.OriginalSource` is not a `bool` or `null`.
|