--- 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`, `[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` 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`) #### 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`.