--- source_files: - DTS Viewer/DTS.Viewer.Modules/DTS.Viewer.GraphList/Classes/VirtualToggleButton.cs generated_at: "2026-04-16T11:10:12.391241+00:00" model: "zai-org/GLM-5-FP8" schema_version: 1 sha256: "001b39a3411ec3ba" --- # Documentation: VirtualToggleButton.cs ## 1. Purpose `VirtualToggleButton` is a static utility class that provides attached dependency properties to imbue any WPF element with toggle button behavior without requiring inheritance from `ToggleButton`. It enables elements to respond to mouse clicks and keyboard input (Space/Enter) to toggle between checked/unchecked/indeterminate states, and raises the corresponding routed events. This is useful for templated controls or custom elements that need toggle semantics without the overhead of full `ToggleButton` derivation. --- ## 2. Public Interface ### Attached Properties | Property | Type | Default | Metadata Flags | |----------|------|---------|----------------| | `IsLockedProperty` | `Nullable` | `false` | `BindsTwoWayByDefault`, `Journal` | | `IsThreeStateProperty` | `bool` | `false` | None | | `IsVirtualToggleButtonProperty` | `bool` | `false` | None | ### Public Methods #### `GetIsLocked(DependencyObject d) -> Nullable` Retrieves the current locked/checked state of the target element. #### `SetIsLocked(DependencyObject d, Nullable value)` Sets the locked/checked state. Setting to `true` raises `ToggleButton.CheckedEvent`; `false` raises `ToggleButton.UncheckedEvent`; `null` raises `ToggleButton.IndeterminateEvent`. #### `GetIsThreeState(DependencyObject d) -> bool` Returns whether the target supports three-state behavior (true/false/null). #### `SetIsThreeState(DependencyObject d, bool value)` Enables or disables three-state mode. When `true`, `IsLocked` can be set to `null` as a third state. #### `GetIsVirtualToggleButton(DependencyObject d) -> bool` Returns whether the target is currently acting as a virtual toggle button. #### `SetIsVirtualToggleButton(DependencyObject d, bool value)` When set to `true`, attaches `MouseLeftButtonDown` and `KeyDown` handlers to the target (must implement `IInputElement`). When set to `false`, detaches these handlers. ### Internal Methods #### `RaiseCheckedEvent(UIElement target) -> RoutedEventArgs` Raises `ToggleButton.CheckedEvent` on the target element. Returns `null` if target is null. #### `RaiseUncheckedEvent(UIElement target) -> RoutedEventArgs` Raises `ToggleButton.UncheckedEvent` on the target element. Returns `null` if target is null. #### `RaiseIndeterminateEvent(UIElement target) -> RoutedEventArgs` Raises `ToggleButton.IndeterminateEvent` on the target element. Returns `null` if target is null. --- ## 3. Invariants 1. **Two-way binding by default**: `IsLockedProperty` is registered with `BindsTwoWayByDefault`, so bindings will update the source automatically. 2. **Event attachment requirement**: `IsVirtualToggleButton` only attaches event handlers if the target implements `IInputElement`. Non-`IInputElement` targets are silently ignored. 3. **Event raising requirement**: `RaiseEvent()` only works for targets that are either `UIElement` or `ContentElement`. Other `DependencyObject` types will not raise events. 4. **Toggle cycle behavior**: - When `IsThreeState` is `false`: toggles between `true` and `false` only. - When `IsThreeState` is `true`: cycles through `false` → `true` → `null` → `false`. 5. **Keyboard handling constraints**: - Space key is ignored when Alt modifier is present (to avoid interfering with system menu). - Enter key only triggers toggle if `KeyboardNavigation.AcceptsReturnProperty` is `true` on the sender. 6. **Event source filtering**: `OnKeyDown` only processes events where `e.OriginalSource == sender`, preventing handling of bubbled events from child elements. --- ## 4. Dependencies ### This module depends on: - `System` (core types) - `System.Windows` (`DependencyObject`, `DependencyProperty`, `FrameworkPropertyMetadata`, `UIElement`, `ContentElement`, `RoutedEventArgs`, `IInputElement`) - `System.Windows.Controls.Primitives` (`ToggleButton` - for `CheckedEvent`, `UncheckedEvent`, `IndeterminateEvent`) - `System.Windows.Input` (`Key`, `KeyEventArgs`, `Keyboard`, `KeyboardNavigation`, `ModifierKeys`, `MouseButtonEventArgs`) ### Consumers: - Unclear from source alone. Any XAML or code in the `DTS.Viewer.GraphList` namespace or referencing it may use these attached properties. --- ## 5. Gotchas 1. **Misleading XML documentation**: The XML comments for `IsLocked` state "indicates whether the toggle button is checked" — this appears to be copy-pasted from a standard `ToggleButton` implementation. The property is named `IsLocked`, not `IsChecked`, which may indicate domain-specific semantics (e.g., locking a graph item vs. selecting it). 2. **Type mismatch between event raising methods**: `RaiseCheckedEvent`, `RaiseUncheckedEvent`, and `RaiseIndeterminateEvent` accept only `UIElement`, but the private `RaiseEvent` helper supports both `UIElement` and `ContentElement`. If a `ContentElement` has `IsLocked` set, the change handler will attempt to raise events on it via `RaiseEvent`, but the public `RaiseXxxEvent` methods cannot be called with `ContentElement` directly. 3. **Null return from event methods**: The `RaiseXxxEvent` methods return `null` when the target is null, but they do not throw. Callers should check for null if they intend to use the returned `RoutedEventArgs`. 4. **Local variable shadows property name**: In `UpdateIsLocked()`, the local variable is named `IsLocked`, which shadows the property accessor method naming convention. This is legal but could cause confusion during debugging. 5. **No validation of IsThreeState consistency**: Setting `IsLocked` to `null` is always possible regardless of `IsThreeState` value. The `IsThreeState` property only affects toggle behavior in `UpdateIsLocked()`, not direct programmatic assignment.