Files
2026-04-17 14:55:32 -04:00

11 KiB
Raw Permalink Blame History

source_files, generated_at, model, schema_version, sha256
source_files generated_at model schema_version sha256
Common/DTS.CommonCore/Behaviors/StringMetaDataAttr.cs
Common/DTS.CommonCore/Behaviors/TrimTextBoxBehavior.cs
Common/DTS.CommonCore/Behaviors/InteractivityTemplate.cs
Common/DTS.CommonCore/Behaviors/TextBoxPasteBehavior.cs
Common/DTS.CommonCore/Behaviors/MultiSelectionBehavior.cs
2026-04-16T02:15:57.710410+00:00 Qwen/Qwen3-Coder-Next-FP8 1 c2b7aea8fc779731

Behaviors

Documentation: DTS.Common.Behaviors Module


1. Purpose

This module provides a set of WPF-specific behaviors and utility attributes to extend UI control functionality and support metadata annotations on types. It enables declarative attachment of cross-cutting concerns—such as text trimming on TextBox loss of focus, paste interception with custom command execution, multi-selection synchronization between a ListBox and an external IList, and dynamic injection of interactivity elements via templates—without modifying control logic directly. The module also includes a generic attribute (StringMetaDataAttr) for associating arbitrary string metadata with types or enum values, primarily for documentation or UI labeling purposes.


2. Public Interface

StringMetaDataAttr

  • public string MetaData { get; }
    Read-only property storing the metadata string provided at construction.
  • internal StringMetaDataAttr(string attr)
    Internal constructor; sets MetaData to attr.
  • public static string GetStringMetaData(object o)
    Retrieves the MetaData string from a StringMetaDataAttr applied to the enum value or type represented by o.
    • Uses reflection on o.GetType().GetMember(o.ToString()) to find the member.
    • Returns null if no attribute is found or if o is null/member not found.

TrimTextBoxBehavior

  • public class TrimTextBoxBehavior : Behavior<TextBox>
    Attaches to a TextBox and trims its Text property on LostFocus.
    • On attachment: subscribes to LostFocus.
    • On LostFocus: trims Text, updates binding source if changed.
    • On detachment: unsubscribes from LostFocus.

InteractivityItems

  • public class InteractivityItems : FrameworkElement
    Holds collections of behaviors and triggers for dynamic injection.
    • public List<TriggerBase> Triggers { get; }
      Lazy-initialized list of triggers.
    • public List<Behavior> Behaviors { get; }
      Lazy-initialized list of behaviors.
    • Attached Property: Template (InteractivityTemplate)
      When set on a DependencyObject, loads content from the InteractivityTemplate, extracts its behaviors/triggers, and adds them to the target objects Interaction.GetBehaviors() and Interaction.GetTriggers() collections.

TextBoxPasteBehavior

  • public static readonly DependencyProperty PasteCommandProperty
    Attached dependency property for PasteCommand.
  • public static ICommand GetPasteCommand(DependencyObject target)
    Gets the PasteCommand value.
  • public static void SetPasteCommand(DependencyObject target, ICommand value)
    Sets the PasteCommand value.
  • private static void PasteCommandChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
    Attaches/detaches handler for CommandManager.ExecutedEvent on the target TextBox (or its MainEditBox if ChannelCodeBuilder/ChannelNameBuilder).
  • private static void CommandExecuted(object sender, RoutedEventArgs e)
    Handles ApplicationCommands.Paste (or custom RoutedUICommand named "Paste").
    • Retrieves text from Clipboard.
    • Calls command.Execute(textBox) if command.CanExecute(null) is true.
    • Sets e.Handled = true to suppress default paste.
    • On exception, publishes PageErrorEvent via IEventAggregator.

MultiSelectionBehavior

  • public class MultiSelectionBehavior : Behavior<ListBox>
    Synchronizes SelectedItems of a ListBox with an external IList.
    • public IList SelectedItems { get; set; }
      Dependency property backing the external list to sync with.
    • public static readonly DependencyProperty SelectedItemsProperty
      Registered dependency property.
    • private static void SelectedItemsChanged(...)
      Handles changes to SelectedItems:
      • Clears ListBox.SelectedItems and repopulates from new value.
      • Subscribes/unsubscribes to CollectionChanged on the new/old value and SelectionChanged on AssociatedObject.
    • private void SourceCollectionChanged(...)
      Updates AssociatedObject.SelectedItems when the external IList changes (e.g., via INotifyCollectionChanged).
    • private void ListBoxSelectionChanged(...)
      Updates the external SelectedItems list when ListBox selection changes.
      • Uses SelectedItemsStatus.SetUpdating(...) to suppress notifications during bulk updates.
      • Skips adding items if already present.
      • Logs exceptions via APILogger.Log(ex).

3. Invariants

  • StringMetaDataAttr.GetStringMetaData(object o)

    • Only inspects the member name derived from o.ToString(). For enums, this is the enum name (e.g., "Red"), not the value.
    • Returns null if o is null, or if o.GetType().GetMember(...) yields no results.
    • Does not search base types or interfaces—only the exact member named by o.ToString().
  • TrimTextBoxBehavior

    • Only trims on LostFocus; no trimming occurs during typing or on TextChanged.
    • Binding update is triggered via UpdateSource() only if trimming actually changed the text.
  • InteractivityItems.Template

    • The InteractivityTemplate.LoadContent() must return a FrameworkElement containing InteractivityItems (or compatible structure); otherwise, behavior injection may fail silently.
    • OnTemplateChanged is invoked after the property is set; no guarantee about timing relative to visual tree load.
  • TextBoxPasteBehavior

    • Only intercepts Paste if PasteCommand is set before paste occurs (via PasteCommandChanged subscription logic).
    • Assumes sender is either a TextBox, ChannelCodeBuilder, or ChannelNameBuilder; otherwise, textBox may be null (though PasteCommandChanged and CommandExecuted both attempt fallbacks).
    • Clipboard.GetText() may throw; exceptions are caught and published as PageErrorEvent.
  • MultiSelectionBehavior

    • SelectedItems must be an IList (not IEnumerable) to support Add/Remove.
    • Type compatibility is checked via type.IsAssignableFrom(item.GetType()); items failing this are silently skipped (exception logged).
    • _isUpdatingSource and _isUpdatingTarget flags prevent infinite loops during bidirectional sync.
    • SelectedItemsStatus.SetUpdating(...) is used but not defined in this module—implies dependency on DTS.Common.Utilities (see Dependencies).

4. Dependencies

Internal Dependencies (from imports)

  • WPF Framework:
    • System.Windows, System.Windows.Controls, System.Windows.Interactivity (for Behavior<T>, Interaction, TriggerBase).
    • System.Windows.Input (for ICommand, RoutedEventHandler, ExecutedRoutedEventArgs, ApplicationCommands).
  • Prism Library:
    • Microsoft.Practices.Prism.Events (IEventAggregator, EventAggregator).
    • Microsoft.Practices.ServiceLocation (ServiceLocator).
  • Custom Types:
    • DTS.Common.Controls.ChannelCodeBuilder, DTS.Common.Controls.ChannelNameBuilder (access MainEditBox).
    • DTS.Common.Utilities.Logging.APILogger (used in MultiSelectionBehavior).
    • DTS.Common.Events.PageErrorEvent, PageErrorArg (used in TextBoxPasteBehavior).
    • DTS.Common.Utilities.SelectedItemsStatus (used in MultiSelectionBehavior for SetUpdating).

External Dependencies

  • Behavioral:
    • TrimTextBoxBehavior, MultiSelectionBehavior, InteractivityItems require System.Windows.Interactivity (Blend SDK).
    • TextBoxPasteBehavior requires System.Windows.Interactivity indirectly via Interaction usage (though not directly instantiated here).

Consumers (inferred)

  • Likely used in XAML via Interaction.Behaviors, InteractivityItems.Template, or attached properties (TextBoxPasteBehavior.PasteCommand).
  • StringMetaDataAttr is likely applied to enums or classes in other modules (e.g., enum Status { [StringMetaData("Active")] Active }).

5. Gotchas

  • StringMetaDataAttr.GetStringMetaData(object o):

    • Fails for enum values with Flags attribute if o.ToString() returns a combined string (e.g., "Red | Blue"), as GetMember("Red | Blue") will not match any member.
    • Does not handle nested types—only top-level members named by o.ToString().
  • TrimTextBoxBehavior:

    • Trimming may cause caret position loss or unexpected selection changes (not mitigated).
    • UpdateSource() may trigger validation errors or other side effects in the binding pipeline.
  • InteractivityItems.Template:

    • OnTemplateChanged assumes interactivityTemplate.LoadContent() returns an InteractivityItems instance. If not, casting to (InteractivityItems) will throw.
    • No deduplication: adding the same behavior/trigger multiple times via Template is possible.
  • TextBoxPasteBehavior:

    • Critical bug in CommandExecuted:
      TextBox textBox = sender as TextBox;
      if (null == sender && sender is ChannelCodeBuilder ccb) // ← Always false: sender cannot be null *and* be a ChannelCodeBuilder
      
      Should be if (null == textBox && sender is ChannelCodeBuilder ccb). Same for ChannelNameBuilder. This will cause textBox to remain null, leading to NullReferenceException when accessing textBox.RemoveHandler(...) or textBox.AddHandler(...).
    • Clipboard.GetText() may throw ExternalException (e.g., clipboard in use); caught but logged generically.
  • MultiSelectionBehavior:

    • SelectedItemsStatus.SetUpdating(...) is used but not defined in this module—if SelectedItemsStatus is missing or misconfigured, notification suppression may fail, causing performance issues or infinite loops.
    • itemsToAdd.Last() check is inefficient for large lists (O(n) per item).
    • selectedItems.Contains(item) is O(n) per item; for large lists, this may be a performance bottleneck.
    • No handling of NotifyCollectionChangedAction.Replace—only Add, Remove, and Reset.
  • General:

    • All behaviors assume WPF UI thread context (no dispatcher marshaling).
    • No null-safety for AssociatedObject in OnDetaching/OnAttached (relies on Behavior<T> base class to guard).
    • StringMetaDataAttr is internal—cannot be instantiated outside this module, limiting extensibility.

None identified beyond the above.