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,793 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.Linq;
using System.Text.RegularExpressions;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Markup;
using System.Windows.Threading;
using DTS.Common.Base;
using DTS.Common.Classes.ChannelCodes;
using DTS.Common.Classes.Groups;
using DTS.Common.Converters;
using DTS.Common.Enums;
using DTS.Common.Enums.Channels;
using DTS.Common.Events;
using DTS.Common.Events.Groups.GroupChannelList;
using DTS.Common.Interface.Channels.ChannelCodes;
using Microsoft.Practices.Prism.Commands;
using Microsoft.Practices.Prism.Events;
using Microsoft.Practices.ServiceLocation;
namespace DTS.Common.Controls
{
/// <summary>
/// Interaction logic for ChannelCodeBuilder.xaml
/// </summary>
public partial class ChannelCodeBuilder : UserControl, IBasePropertyChanged
{
private ISOPopup _isoPopup;
private LookupPopup _lookupPopup;
private DispatcherTimer _possiblesTimer;
public ChannelCodeBuilder()
{
InitializeComponent();
Loaded += (sender, args) => { if (!_registered) RegisterCommands(); };
}
public static readonly DependencyProperty CodeTypeProperty =
DependencyProperty.Register(
"CodeType", // The name of the DependencyProperty
typeof(ChannelEnumsAndConstants.ChannelCodeType), // The type of the DependencyProperty
typeof(ChannelCodeBuilder), // The type of the owner of the DependencyProperty
new PropertyMetadata( // OnHeaderTitleChanged will be called when HeaderTitle changes
ChannelEnumsAndConstants.ChannelCodeType.User // The default value of the DependencyProperty
)
);
public ChannelEnumsAndConstants.ChannelCodeType CodeType
{
get => (ChannelEnumsAndConstants.ChannelCodeType) GetValue(CodeTypeProperty);
set => SetValue(CodeTypeProperty, value);
}
private static readonly DependencyProperty CodeProperty =
DependencyProperty.Register(
"Code", // The name of the DependencyProperty
typeof(string), // The type of the DependencyProperty
typeof(ChannelCodeBuilder), // The type of the owner of the DependencyProperty
new PropertyMetadata( // OnHeaderTitleChanged will be called when HeaderTitle changes
"", // The default value of the DependencyProperty
OnCodeChanged,
CoerceCode
)
);
public static readonly DependencyProperty CoerceISOCodeProperty =
DependencyProperty.Register(
"CoerceISOCode",
typeof(CoerceISOCodeDelegate),
typeof(ChannelCodeBuilder),
new PropertyMetadata(null));
/// <summary>
/// defines a function to use to coerce iso codes
/// </summary>
public CoerceISOCodeDelegate CoerceISOCode
{
get => (CoerceISOCodeDelegate)GetValue(CoerceISOCodeProperty);
set => SetValue(CoerceISOCodeProperty, value);
}
private static object CoerceCode(DependencyObject d, object baseValue)
{
string val = baseValue?.ToString() ?? string.Empty;
val = Regex.Replace(val, @"[^\u0000-\u007F]+", "?");
ChannelCodeBuilder ccb = (ChannelCodeBuilder)d;
//if we have a function defined on how to coerce, use that, otherwise use existing coerce functions
if (null != ccb.CoerceISOCode && !ccb.MainEditBox.IsKeyboardFocused)
{
return ccb.CoerceISOCode(val, ccb.UniqueISOCodesRequired, ccb.UseISOCodeFilterMapping);
}
if (ccb?.CodeType == ChannelEnumsAndConstants.ChannelCodeType.ISO && ccb.UniqueISOCodesRequired && !ccb.MainEditBox.IsKeyboardFocused)
{
val = val.ToUpper().PadRight(ChannelEnumsAndConstants.ISO_CODE_LENGTH, '?');
/*
13902
Cannot save test setup when leaving out one or more characters for a channels' ISO code
part of the issue is the CoerceValueCallback is called AFTER the original source is updated, meaning the property and the value on the screen are out of date with each other.
this code attempts to fix that by updating the property with the coerced value.
*/
if( ccb.Code != val )
{
ccb.SetCodeProperty(val);
}
}
return val;
}
private volatile bool _bInSet = false;
/*
13902
Cannot save test setup when leaving out one or more characters for a channels' ISO code
part of the issue is the CoerceValueCallback is called AFTER the original source is updated, meaning the property and the value on the screen are out of date with each other.
this code attempts to fix that by updating the property with the coerced value.
*/
public void SetCodeProperty(string value)
{
if( _bInSet ){ return; }
_bInSet = true;
SetValue(CodeProperty, value);
_bInSet = false;
}
private static void OnCodeChanged(
DependencyObject d,
DependencyPropertyChangedEventArgs e
)
{
ChannelCodeBuilder ccb = (ChannelCodeBuilder)d;
if (ccb?.CodeType == ChannelEnumsAndConstants.ChannelCodeType.ISO)
{
ccb.OnPropertyChanged("TestObject");
ccb.OnPropertyChanged("Position");
ccb.OnPropertyChanged("MainLocation");
ccb.OnPropertyChanged("FineLocation1");
ccb.OnPropertyChanged("FineLocation2");
ccb.OnPropertyChanged("FineLocation3");
ccb.OnPropertyChanged("Dimension");
ccb.OnPropertyChanged("Direction");
ccb.OnPropertyChanged("FilterClass");
}
ccb.UpdatePossibleChannels();//.OnPropertyChanged("PossibleChannels");
}
public string Code
{
get => (string) GetValue(CodeProperty);
set => SetValue(CodeProperty, value);
}
public static readonly DependencyProperty ChannelCodesFuncProperty =
DependencyProperty.Register(
"ChannelCodesFunc",
typeof(Func<IList<IChannelCode>>),
typeof(ChannelCodeBuilder),
new PropertyMetadata(null)
);
public Func<IList<IChannelCode>> ChannelCodesFunc
{
get => (Func<IList<IChannelCode>>) GetValue(ChannelCodesFuncProperty);
set => SetValue(ChannelCodesFuncProperty, value);
}
#region ISO builder properties
public static readonly DependencyProperty ShowISOStringBuilderProperty =
DependencyProperty.Register(
"ShowISOStringBuilder", // The name of the DependencyProperty
typeof(bool), // The type of the DependencyProperty
typeof(ChannelCodeBuilder), // The type of the owner of the DependencyProperty
new PropertyMetadata( // OnHeaderTitleChanged will be called when HeaderTitle changes
true, // The default value of the DependencyProperty
OnShowISOCodeBuilderChanged
)
);
private static void OnShowISOCodeBuilderChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
ChannelCodeBuilder ccb = (ChannelCodeBuilder)d;
if (!(bool)e.NewValue)
{
ccb._isoPopup = null;
}
}
public bool ShowISOStringBuilder
{
get => (bool)GetValue(ShowISOStringBuilderProperty);
set => SetValue(ShowISOStringBuilderProperty, value);
}
private ICommand _pasteCommand;
public ICommand PasteCommand
{
get => _pasteCommand;
set { _pasteCommand = value; OnPropertyChanged("PasteCommand"); }
}
private static readonly DependencyProperty PasteIdProperty =
DependencyProperty.Register(
"PasteId", // The name of the DependencyProperty
typeof(string), // The type of the DependencyProperty
typeof(ChannelCodeBuilder), // The type of the owner of the DependencyProperty
new PropertyMetadata( // OnHeaderTitleChanged will be called when HeaderTitle changes
DTS.Common.Classes.Groups.GroupChannel.PASTE_ID // The default value of the DependencyProperty
)
);
public string PasteId
{
get => (string)GetValue(PasteIdProperty);
set => SetValue(PasteIdProperty, value);
}
private bool _registered = false;
private void RegisterCommands()
{
// FB13293: In order to add CCB to Channel Codes tile, we have to expand support for paste types
switch (PasteId)
{
case DTS.Common.Classes.ChannelCodes.ChannelCode.PASTE_ID:
PasteCommand = new DTS.Common.Classes.ChannelCodes.PasteCommandClass(PasteId);
break;
case DTS.Common.Classes.Groups.GroupChannel.PASTE_ID:
default:
PasteCommand = new DTS.Common.Classes.Groups.PasteCommandClass(PasteId);
break;
}
CommandManager.RegisterClassCommandBinding(GetType(),
new CommandBinding(PasteCommand, Paste));
_registered = true;
}
private void Paste(object sender, ExecutedRoutedEventArgs e)
{
}
public static readonly DependencyProperty UniqueISOCodesRequiredProperty =
DependencyProperty.Register(
"UniqueISOCodesRequired", // The name of the DependencyProperty
typeof(bool), // The type of the DependencyProperty
typeof(ChannelCodeBuilder), // The type of the owner of the DependencyProperty
new PropertyMetadata( // OnHeaderTitleChanged will be called when HeaderTitle changes
false // The default value of the DependencyProperty
)
);
public bool UniqueISOCodesRequired
{
get => (bool)GetValue(UniqueISOCodesRequiredProperty);
set => SetValue(UniqueISOCodesRequiredProperty, value);
}
public static readonly DependencyProperty UseISOCodeFilterMappingProperty =
DependencyProperty.Register(
"UseISOCodeFilterMapping", // The name of the DependencyProperty
typeof(bool), // The type of the DependencyProperty
typeof(ChannelCodeBuilder), // The type of the owner of the DependencyProperty
new PropertyMetadata( // OnHeaderTitleChanged will be called when HeaderTitle changes
false // The default value of the DependencyProperty
)
);
public bool UseISOCodeFilterMapping
{
get => (bool)GetValue(UseISOCodeFilterMappingProperty);
set => SetValue(UseISOCodeFilterMappingProperty, value);
}
private string ISOPartFilter(string value, uint length)
{
if (value != value.PadRight((int)length, '?') && UniqueISOCodesRequired)
{
value = value.PadRight((int)length, '?');
}
return value.ToUpper().Substring(0, (int)length);
}
private void SpliceIntoCode(string value, uint position)
{
//pad code if necessary
var paddedCode = Code.PadRight((int)position + value.Length, '?');
//splice in
var pre = paddedCode.Substring(0, (int) position);
var post = position + value.Length < paddedCode.Length
? paddedCode.Substring((int) position + value.Length)
: string.Empty;
Code = string.Concat(pre, value, post);
}
public string TestObject
{
get
{
if (CodeType == ChannelEnumsAndConstants.ChannelCodeType.ISO && Code.Length > 0)
{
return Code.Substring(0, 1);
}
return "";
}
set
{
if (null == value) return;
value = ISOPartFilter(value, 1);
SpliceIntoCode(value, 0);
}
}
public string Position
{
get
{
if (CodeType == ChannelEnumsAndConstants.ChannelCodeType.ISO && Code.Length > 1)
{
return Code.Substring(1, 1);
}
return "";
}
set
{
if (null == value) return;
value = ISOPartFilter(value, 1);
SpliceIntoCode(value, 1);
}
}
public string MainLocation
{
get
{
if (CodeType == ChannelEnumsAndConstants.ChannelCodeType.ISO && Code.Length > 2)
{
return Code.PadRight(6, '?').Substring(2, 4);
}
return "";
}
set
{
if (null == value) return;
value = ISOPartFilter(value, 4);
SpliceIntoCode(value, 2);
}
}
public string FineLocation1
{
get
{
if (CodeType == ChannelEnumsAndConstants.ChannelCodeType.ISO && Code.Length > 6)
{
return Code.PadRight(8, '?').Substring(6, 2);
}
return "";
}
set
{
if (null == value) return;
value = ISOPartFilter(value, 2);
SpliceIntoCode(value, 6);
}
}
public string FineLocation2
{
get
{
if (CodeType == ChannelEnumsAndConstants.ChannelCodeType.ISO && Code.Length > 8)
{
return Code.PadRight(10, '?').Substring(8, 2);
}
return "";
}
set
{
if (null == value) return;
value = ISOPartFilter(value, 2);
SpliceIntoCode(value, 8);
}
}
public string FineLocation3
{
get
{
if (CodeType == ChannelEnumsAndConstants.ChannelCodeType.ISO && Code.Length > 10)
{
return Code.PadRight(12, '?').Substring(10, 2);
}
return "";
}
set
{
if (null == value) return;
value = ISOPartFilter(value, 2);
SpliceIntoCode(value, 10);
}
}
public string Dimension
{
get
{
if (CodeType == ChannelEnumsAndConstants.ChannelCodeType.ISO && Code.Length > 12)
{
return Code.PadRight(14, '?').Substring(12, 2);
}
return "";
}
set
{
if (null == value) return;
value = ISOPartFilter(value, 2);
SpliceIntoCode(value, 12);
}
}
public string Direction
{
get
{
if (CodeType == ChannelEnumsAndConstants.ChannelCodeType.ISO && Code.Length > 14)
{
return Code.Substring(14, 1);
}
return "";
}
set
{
if (null == value) return;
value = ISOPartFilter(value, 1);
SpliceIntoCode(value, 14);
}
}
public string FilterClass
{
get
{
if (CodeType == ChannelEnumsAndConstants.ChannelCodeType.ISO && Code.Length > 15)
{
return Code.Substring(15, 1);
}
return "";
}
set
{
if (null == value) return;
value = ISOPartFilter(value, 1);
SpliceIntoCode(value, 15);
}
}
#endregion
#region ChannelCode select properties
public static readonly DependencyProperty AllChannelCodesProperty =
DependencyProperty.Register(
"AllChannelCodes", // The name of the DependencyProperty
typeof(IEnumerable<IChannelCode>), // The type of the DependencyProperty
typeof(ChannelCodeBuilder), // The type of the owner of the DependencyProperty
new PropertyMetadata( // OnHeaderTitleChanged will be called when HeaderTitle changes
new List<IChannelCode>(), // The default value of the DependencyProperty
OnAllChannelCodesChanged
)
);
private static void OnAllChannelCodesChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
ChannelCodeBuilder ccb = (ChannelCodeBuilder) d;
ccb?.UpdatePossibleChannels();
}
public IList<IChannelCode> AllChannelCodes
{
get => (IList<IChannelCode>)GetValue(AllChannelCodesProperty);
set => SetValue(AllChannelCodesProperty, value);
}
public void UpdatePossibleChannels(bool immediateUpdate = false)
{
if (null == _lookupPopup) return;
if (immediateUpdate)
{
_possiblesTimer?.Stop();
_lookupPopup.PossibleChannels = ChannelCodesFunc?.Invoke().Where(code => code.CodeType == CodeType).Select(code => new {code.Code, code.Name}).Where(m => Utils.Utils.IsLike(m.Code, Code)).ToList() ??
AllChannelCodes?.Where(code => code.CodeType == CodeType).Select(code => new {code.Code, code.Name}).Where(m => Utils.Utils.IsLike(m.Code, Code)).ToList() ??
Enumerable.Repeat(new {Code = "", Name = ""}, 0).ToList();
}
else
{
if (null == _possiblesTimer)
{
_possiblesTimer = new DispatcherTimer()
{
Interval = TimeSpan.FromMilliseconds(LookupHelperUpdateDelay),
IsEnabled = false
};
_possiblesTimer.Tick += (sender, args) =>
{
_possiblesTimer.IsEnabled = false;
if(_lookupPopup != null)
{
_lookupPopup.PossibleChannels = ChannelCodesFunc?.Invoke().Where(code => code.CodeType == CodeType).Select(code => new {code.Code, code.Name}).Where(m => Utils.Utils.IsLike(m.Code, Code)).ToList() ??
AllChannelCodes?.Where(code => code.CodeType == CodeType).Select(code => new {code.Code, code.Name}).Where(m => Utils.Utils.IsLike(m.Code, Code)).ToList() ??
Enumerable.Repeat(new {Code = "", Name = ""}, 0).ToList();
}
};
}
if (_possiblesTimer.IsEnabled)
{
_possiblesTimer.Stop();
}
_possiblesTimer.Start();
}
}
public static readonly DependencyProperty ShowChannelCodeLookupHelperProperty =
DependencyProperty.Register(
"ShowChannelCodeLookupHelper", // The name of the DependencyProperty
typeof(bool), // The type of the DependencyProperty
typeof(ChannelCodeBuilder), // The type of the owner of the DependencyProperty
new PropertyMetadata( // OnHeaderTitleChanged will be called when HeaderTitle changes
true, // The default value of the DependencyProperty
OnShowChannelCodeLookupHelperChanged
)
);
private static void OnShowChannelCodeLookupHelperChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
ChannelCodeBuilder ccb = (ChannelCodeBuilder)d;
if (!(bool) e.NewValue)
{
ccb._lookupPopup = null;
}
}
public bool ShowChannelCodeLookupHelper
{
get => (bool)GetValue(ShowChannelCodeLookupHelperProperty);
set => SetValue(ShowChannelCodeLookupHelperProperty, value);
}
public static readonly DependencyProperty LookupHelperUpdateDelayProperty =
DependencyProperty.Register(
"LookupHelperUpdateDelay", // The name of the DependencyProperty
typeof(int), // The type of the DependencyProperty
typeof(ChannelCodeBuilder), // The type of the owner of the DependencyProperty
new PropertyMetadata(
1000,
LookupHelperUpdateDelayChanged,
CoerceLookupHelperUpdateDelay
)
);
private static object CoerceLookupHelperUpdateDelay(DependencyObject d, object baseValue)
{
return (int) baseValue < 0 ? 0 : (int) baseValue;
}
private static void LookupHelperUpdateDelayChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (!(d is ChannelCodeBuilder ccb)) return;
ccb._possiblesTimer?.Stop();
ccb._possiblesTimer = null;
}
public int LookupHelperUpdateDelay
{
get => (int) GetValue(LookupHelperUpdateDelayProperty);
set => SetValue(LookupHelperUpdateDelayProperty, value);
}
#endregion
public event PropertyChangedEventHandler PropertyChanged;
public delegate void ChannelCodeSelectedEventHandler(object sender, string code, string name, ChannelEnumsAndConstants.ChannelCodeType codeType);
public event ChannelCodeSelectedEventHandler ChannelCodeSelected;
public void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private void MainEditBox_OnGotKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
{
if (sender.Equals(MainEditBox))
{
MultiBinding lookupMB = null, isoMB = null;
if (ShowChannelCodeLookupHelper && null == _lookupPopup)
{
_lookupPopup = new LookupPopup()
{
PlacementTarget = MainEditBox,
HorizontalOffset = 10,
Placement = PlacementMode.Custom,
CustomPopupPlacementCallback = (size, targetSize, offset) =>
{
CustomPopupPlacement bottom = new CustomPopupPlacement(new Point(0, targetSize.Height), PopupPrimaryAxis.Horizontal);
CustomPopupPlacement left = new CustomPopupPlacement(new Point(-size.Width - offset.X, 0), PopupPrimaryAxis.Vertical);
var topoffset = ShowISOStringBuilder && CodeType == ChannelEnumsAndConstants.ChannelCodeType.ISO ? 48 : 0;
CustomPopupPlacement top = new CustomPopupPlacement(new Point(0, -size.Height - topoffset), PopupPrimaryAxis.Horizontal);
return new CustomPopupPlacement[] { bottom, left, top };
},
};
_lookupPopup.ChannelCodeSelected += LookupPopupOnChannelCodeSelected;
lookupMB = new MultiBinding();
lookupMB.Converter = new BooleanOrMultiConverter();
lookupMB.Bindings.Add(new Binding("IsKeyboardFocused") { Source = MainEditBox, Mode=BindingMode.OneWay });
lookupMB.Bindings.Add(new Binding("IsKeyboardFocusWithin") { Source = _lookupPopup, Mode = BindingMode.OneWay });
lookupMB.NotifyOnSourceUpdated = true;
}
if (CodeType == ChannelEnumsAndConstants.ChannelCodeType.ISO && ShowISOStringBuilder &&
null == _isoPopup)
{
_isoPopup = new ISOPopup()
{
PlacementTarget = MainEditBox,
Placement = PlacementMode.Top,
HorizontalOffset = 10
};
isoMB = new MultiBinding();
isoMB.Converter = new BooleanOrMultiConverter();
isoMB.Bindings.Add(new Binding("IsKeyboardFocused") { Source = MainEditBox, Mode = BindingMode.OneWay });
isoMB.Bindings.Add(new Binding("IsMouseOver") { Source = _isoPopup, Mode = BindingMode.OneWay });
isoMB.Bindings.Add(new Binding("IsKeyboardFocusWithin") { Source = _isoPopup, Mode = BindingMode.OneWay });
isoMB.NotifyOnSourceUpdated = true;
}
if (null != lookupMB)
{
if (null != _isoPopup)
{
lookupMB.Bindings.Add(new Binding("IsMouseOver") {Source = _isoPopup, Mode = BindingMode.OneWay});
lookupMB.Bindings.Add(new Binding("IsKeyboardFocusWithin") {Source = _isoPopup, Mode = BindingMode.OneWay});
}
_lookupPopup.SetBinding(Popup.IsOpenProperty, lookupMB);
}
if (null != _lookupPopup)
{
UpdatePossibleChannels(true);
}
if (null != isoMB)
{
if (null != _lookupPopup)
{
isoMB.Bindings.Add(new Binding("IsKeyboardFocusWithin") {Source = _lookupPopup, Mode = BindingMode.OneWay});
}
_isoPopup.SetBinding(Popup.IsOpenProperty, isoMB);
}
}
if(sender is TextBox tb)
{
tb.SelectAll();
}
}
private void LookupPopupOnChannelCodeSelected(object sender, string code, string name)
{
Code = code;
ChannelCodeSelected?.Invoke(this, code, name, CodeType);
Keyboard.ClearFocus();
}
private void MainEditBox_OnPreviewKeyDown(object sender, KeyEventArgs e)
{
if (CodeType != ChannelEnumsAndConstants.ChannelCodeType.ISO) { return; }
bool isAlpha = (e.Key >= Key.A && e.Key <= Key.Z);
bool isNumPadNumeric = (e.Key >= Key.NumPad0 && e.Key <= Key.NumPad9);
bool isNumeric = (e.Key >= Key.D0 && e.Key <= Key.D9);
bool isControl = e.Key == Key.Enter || e.Key == Key.Return || e.Key == Key.Tab || e.Key == Key.OemBackTab ||
e.Key == Key.Delete || e.Key == Key.Back || e.Key == Key.Home || e.Key == Key.End;
bool isDirection = e.Key == Key.Up || e.Key == Key.Down || e.Key == Key.Left || e.Key == Key.Right;
e.Handled = !(e.Key == Key.OemQuestion || isAlpha || isNumPadNumeric || isNumeric || isControl || isDirection);
}
private void MainEditBox_OnLostFocus(object sender, RoutedEventArgs e)
{
if (!IsKeyboardFocusWithin && (!_isoPopup?.IsKeyboardFocusWithin ?? true) && (!_lookupPopup?.IsKeyboardFocusWithin ?? true))
{
//now that we've lost focus, we can coerce the code
Code = Code;
}
}
private void ChannelCodeBuilder_OnLoaded(object sender, RoutedEventArgs e)
{
Window w = Window.GetWindow(this);
if (w != null)
{
w.LocationChanged += delegate
{
if (IsKeyboardFocusWithin || (_isoPopup?.IsKeyboardFocusWithin ?? false))
{
Keyboard.ClearFocus();
}
};
}
var scrollParent = Utils.Utils.FindParent<ScrollViewer>(this);
if (scrollParent != null)
{
scrollParent.ScrollChanged += delegate(object o, ScrollChangedEventArgs args)
{
if ((IsKeyboardFocusWithin || (_isoPopup?.IsKeyboardFocusWithin ?? false)) &&
(args.HorizontalChange != 0 || args.VerticalChange != 0))
{
Keyboard.ClearFocus();
}
};
}
}
private void TextBoxSourceUpdated(object sender, DataTransferEventArgs e)
{
var eventAggregator = ServiceLocator.Current.GetInstance<IEventAggregator>();
eventAggregator.GetEvent<PageModifiedEvent>()
.Publish(new PageModifiedArg(PageModifiedArg.Status.Modified, null));
eventAggregator.GetEvent<GroupUpdatedEvent>()
.Publish(new GroupUpdatedEventArgs(null, GroupUpdatedEventArgs.Status.AssignmentsMade));
}
private void MainEditBox_OnMouseDoubleClick(object sender, MouseButtonEventArgs e)
{
if (sender is TextBox tb)
{
tb.SelectAll();
}
}
public event TextChangedEventHandler TextChanged;
private void MainEditBox_OnTextChanged(object sender, TextChangedEventArgs e)
{
TextChanged?.Invoke(this, e);
}
//FB 25725 ISO selection has been changed
public event RoutedEventHandler SelectionChanged;
private void MainEditBox_SelectionChanged(object sender, RoutedEventArgs e)
{
SelectionChanged?.Invoke(this, e);
}
}
internal class CodeTypeToMaxLengthConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is ChannelEnumsAndConstants.ChannelCodeType.ISO)
{
return ChannelEnumsAndConstants.ISO_CODE_LENGTH;
}
return ChannelEnumsAndConstants.USER_CODE_LENGTH;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
internal class CodeTypeToCharacterCasingConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is ChannelEnumsAndConstants.ChannelCodeType.ISO)
{
return CharacterCasing.Upper;
}
return CharacterCasing.Normal;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
public class UpdatePropertySourceWhenEnterPressedExtension : MarkupExtension
{
public override object ProvideValue(IServiceProvider serviceProvider)
{
return new DelegateCommand<TextBox>(textbox => textbox.GetBindingExpression(TextBox.TextProperty)?.UpdateSource());
}
}
}

View File

@@ -0,0 +1,21 @@
using System.Collections.Generic;
using DTS.Common.Base;
namespace DTS.Common.Interface.Groups
{
/// <summary>
/// controls parsing out channels from .GRP file, displaying results, and allowing selection of groups and renaming of groups
/// </summary>
public interface IGroupImportPreviewView:IBaseView
{
/// <summary>
/// validates currently selected groups, including channels and group names
/// </summary>
/// <param name="errors">any errors that were detected during validation. Errors indicate you can not continue</param>
/// <param name="warnings">any warnings that were detected during validation. Warnings are not fatal and user can continue
/// if they want
/// </param>
/// <returns>true if all groups selected in preview are valid, false if there are invalid groups selected</returns>
bool Validate(bool userIsAdmin, out List<string>errors, out List<string> warnings);
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -0,0 +1,250 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using DTS.Common.Interface.Tags;
using DTS.Common.Utilities.Logging;
// ReSharper disable InconsistentNaming
// ReSharper disable once CheckNamespace
namespace DTS.Common.Classes.Tags
{
public class TagsInstance
{
private static TagsInstance _tagsInstance;
public static TagsInstance GetTagsInstance(TagsGetDelegate tagsGet)
{
if (null == _tagsInstance)
{
_tagsInstance = new TagsInstance(tagsGet);
}
return _tagsInstance;
}
private static readonly object LOCK_OBJECT = new object();
public TagsInstance(TagsGetDelegate tagsGet)
{
_tagsLookup = new Dictionary<string, ITag>();
UpdateList(tagsGet);
}
/// <summary>
/// holds a cached collection of tags. This collection however is currently only populated on startup
/// and not updated except explicitly when a user adds a tag
/// </summary>
// ReSharper disable once RedundantDefaultMemberInitializer
private readonly Dictionary<string, ITag> _tagsLookup = null;
/// <summary>
/// Adds a tag if not present in memory cache to db
/// </summary>
/// <param name="tagText"></param>
/// <returns></returns>
public static bool AddTag(string tagText, GetSqlCommandDelegate getSqlCommand, TagsGetDelegate tagsGet,
TagsGetIdDelegate tagsGetId, TagsInsertDelegate tagsInsert)
{
if (string.IsNullOrEmpty(tagText)) { return false; }
// is it already in the dictionary?
var tags = GetTagsInstance(tagsGet);
if (tags.ContainsTag(tagText)) return false;
tags.Commit(new Tag(tagText, Tag.INVALID_ID), tagsGet, tagsGetId, tagsInsert);
return true;
}
/// <summary>
/// Changes the ID of a tag during database migration
/// </summary>
/// <param name="tagText"></param>
/// <returns></returns>
public static bool MigrateTag(string tagText, GetSqlCommandDelegate getSqlCommand, TagsGetDelegate tagsGet,
TagsGetIdDelegate tagsGetId, TagsInsertDelegate tagsInsert)
{
if (string.IsNullOrEmpty(tagText)) { return false; }
GetTagsInstance(tagsGet).Commit(new Tag(tagText, Tag.INVALID_ID), tagsGet, tagsGetId, tagsInsert);
return true;
}
/// <summary>
/// commits a tag to db if doesn't already exist in db
/// </summary>
/// <param name="tag"></param>
private void Commit(Tag tag, TagsGetDelegate tagsGet,
TagsGetIdDelegate tagsGetId, TagsInsertDelegate tagsInsert)
{
try
{
if (-1 == GetIDFromTagText(tag.Text, tagsGet, tagsGetId))
{
Insert(tag, tagsInsert);
}
else { UpdateAll(tag, tagsGetId); }
lock (LOCK_OBJECT)
{
_tagsLookup[tag.Text] = tag;
}
}
catch (Exception ex)
{
APILogger.Log(ex);
}
}
private void UpdateAll(Tag tag, TagsGetIdDelegate tagsGetId)
{
//nothing to do currently? (we don't let you rename or edit, or obsolete, or delete ...)
tag.ID = GetTagIdFromText(tag.Text, tagsGetId);
}
public delegate ulong TagAssignmentsGet(TagTypes? tagType, out ITagAssignment[] records);
public delegate SqlCommand GetSqlCommandDelegate(bool bNewConnection);
public delegate ulong TagAssignmentsDelete(TagTypes objectType, int objectId);
public delegate ulong TagAssignmentsInsert(ITagAssignment tagAssignment);
public delegate ulong TagsGetIdDelegate(string text, out int? id);
public delegate ulong TagsGetDelegate(int? id, out ITag[] tags);
public delegate ulong TagsInsertDelegate(ref ITag tag);
/// <summary>
/// inserts a tag into the db
/// </summary>
/// <param name="tag"></param>
private void Insert(Tag tag, TagsInsertDelegate tagsInsert)
{
var itag = (ITag)tag;
_ = tagsInsert(ref itag);
}
/// <summary>
/// retrieves a string text associated with an ID FROM CACHED copies
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
private string GetTagTextFromId(int id)
{
lock (LOCK_OBJECT)
{
if (_tagsLookup == null) return null;
var e = _tagsLookup.GetEnumerator();
while (e.MoveNext())
{
if (e.Current.Value.ID != id) continue;
var returnText = e.Current.Value.Text;
e.Dispose();
return returnText;
}
}
return null;
}
/// <summary>
/// Gets an ID for a given tag text FROM DB
/// returns InvalidID if not found
/// </summary>
/// <param name="text"></param>
/// <returns></returns>
private int GetTagIdFromText(string text, TagsGetIdDelegate tagsGetId)
{
var hr = tagsGetId(text, out var id);
if( 0 != hr || null == id)
{
return Tag.INVALID_ID;
}
return (int)id;
}
/// <summary>
/// returns true if a given tag text is contained in cached in memory tags
/// </summary>
/// <param name="text"></param>
/// <returns></returns>
public bool ContainsTag(string text)
{
lock (LOCK_OBJECT)
{
return _tagsLookup.ContainsKey(text);
}
}
/// <summary>
/// adds multiple tags at once
/// note that tags will have their start trimmed before commiting
/// </summary>
/// <param name="tagText"></param>
/// <returns></returns>
public static bool[] AddRange(string[] tagText, GetSqlCommandDelegate getSqlCommand,
TagsGetDelegate tagsGet, TagsGetIdDelegate tagsGetId, TagsInsertDelegate tagsInsert)
{
List<bool> rv = new List<bool>();
if (null == tagText || 0 == tagText.Length) { return null; }
foreach (string s in tagText)
{
var tag = s.TrimStart();
rv.Add(AddTag(tag, getSqlCommand, tagsGet, tagsGetId, tagsInsert));
}
return rv.ToArray();
}
/// <summary>
/// gets an ID for a given tag text FROM DB
/// </summary>
/// <param name="tagText"></param>
/// <returns></returns>
public static int GetIDFromTagText(string tagText, TagsGetDelegate tagsGet,
TagsGetIdDelegate tagsGetId)
{
return GetTagsInstance(tagsGet).GetTagIdFromText(tagText, tagsGetId);
}
/// <summary>
/// gets an array of ids given an array of tag texts
/// </summary>
/// <param name="tagText"></param>
/// <returns></returns>
public static int[] GetIDsFromTagText(string[] tagText, TagsGetDelegate tagsGet,
TagsGetIdDelegate tagsGetId)
{
if (null == tagText || 0 == tagText.Length) { return null; }
return tagText.Select(s => s.TrimStart()).Select(text => GetIDFromTagText(text, tagsGet, tagsGetId))
.Where(id => id != Tag.INVALID_ID).ToArray();
}
/// <summary>
/// returns a string for a given id from memory cache
/// returns null if it doesn't exist or is an invalid id
/// </summary>
/// <param name="tagID"></param>
/// <returns></returns>
public static string GetTagTextFromID(int tagID, TagsGetDelegate tagsGet)
{
if (0 > tagID || tagID == Tag.INVALID_ID)
{
// Not a valid ID
return null;
}
return GetTagsInstance(tagsGet).GetTagTextFromId(tagID);
}
/// <summary>
/// returns an array of tag text given an array of tag ids.
/// skips invalid tags or tag text
/// </summary>
/// <param name="tagId"></param>
/// <returns></returns>
public static string[] GetTagTextFromIDs(int[] tagId, TagsGetDelegate tagsGet)
{
if (null == tagId || 0 == tagId.Length) { return new string [0]; }
return tagId.Where(i => i != Tag.INVALID_ID).Select(i => GetTagTextFromID(i,tagsGet)).Where(tag => !string.IsNullOrWhiteSpace(tag)).ToArray();
}
/// <summary>
/// retrieves all tags and updates the cached dictionary of tags
/// </summary>
public void UpdateList(TagsGetDelegate tagsGet)
{
var hr = tagsGet(null, out var tags);
if( 0 == hr && null != tags && tags.Any())
{
lock (LOCK_OBJECT)
{
_tagsLookup.Clear();
foreach( var tag in tags)
{
_tagsLookup[tag.Text] = tag;
}
}
}
}
}
}