433 lines
18 KiB
C#
433 lines
18 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.ComponentModel;
|
|
using System.Linq;
|
|
using System.Windows;
|
|
using System.Windows.Controls;
|
|
using System.Windows.Controls.Primitives;
|
|
using System.Windows.Data;
|
|
using System.Windows.Input;
|
|
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.Channels;
|
|
using DTS.Common.Events;
|
|
using DTS.Common.Events.Groups.GroupChannelList;
|
|
using DTS.Common.Interface.Channels.ChannelCodes;
|
|
using Prism.Events;
|
|
using Prism.Ioc;
|
|
|
|
namespace DTS.Common.Controls
|
|
{
|
|
/// <summary>
|
|
/// Interaction logic for ChannelNameBuilder.xaml
|
|
/// built from ChannelCodeBuilder then simplified a little
|
|
/// http://manuscript.dts.local/f/cases/17565/
|
|
/// </summary>
|
|
public partial class ChannelNameBuilder : UserControl, IBasePropertyChanged
|
|
{
|
|
private LookupPopup _lookupPopup;
|
|
|
|
private DispatcherTimer _possiblesTimer;
|
|
|
|
public ChannelNameBuilder()
|
|
{
|
|
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(ChannelNameBuilder), // 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 ChannelNameProperty =
|
|
DependencyProperty.Register(
|
|
"ChannelName", // The name of the DependencyProperty
|
|
typeof(string), // The type of the DependencyProperty
|
|
typeof(ChannelNameBuilder), // The type of the owner of the DependencyProperty
|
|
new PropertyMetadata( // OnHeaderTitleChanged will be called when HeaderTitle changes
|
|
"", // The default value of the DependencyProperty
|
|
OnChannelNameChanged
|
|
)
|
|
);
|
|
|
|
private volatile bool _bInSet = false;
|
|
|
|
public void SetNameProperty(string value)
|
|
{
|
|
if (_bInSet) { return; }
|
|
_bInSet = true;
|
|
SetValue(ChannelNameProperty, value);
|
|
_bInSet = false;
|
|
}
|
|
private static void OnChannelNameChanged(
|
|
DependencyObject d,
|
|
DependencyPropertyChangedEventArgs e
|
|
)
|
|
{
|
|
ChannelNameBuilder cnb = (ChannelNameBuilder)d;
|
|
cnb.SetValue(ChannelNameProperty, e.NewValue);
|
|
cnb.UpdatePossibleChannels();
|
|
}
|
|
|
|
public string ChannelName
|
|
{
|
|
get => (string)GetValue(ChannelNameProperty);
|
|
set => SetValue(ChannelNameProperty, value);
|
|
}
|
|
|
|
|
|
public static readonly DependencyProperty ChannelNamesFuncProperty =
|
|
DependencyProperty.Register(
|
|
"ChannelNamesFunc",
|
|
typeof(Func<IList<IChannelCode>>),
|
|
typeof(ChannelNameBuilder),
|
|
new PropertyMetadata(null)
|
|
);
|
|
|
|
public Func<IList<IChannelCode>> ChannelNamesFunc
|
|
{
|
|
get => (Func<IList<IChannelCode>>)GetValue(ChannelNamesFuncProperty);
|
|
set => SetValue(ChannelNamesFuncProperty, value);
|
|
}
|
|
|
|
#region ISO builder properties
|
|
|
|
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(ChannelNameBuilder), // The type of the owner of the DependencyProperty
|
|
new PropertyMetadata( // OnHeaderTitleChanged will be called when HeaderTitle changes
|
|
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 ChannelCode.PASTE_ID:
|
|
PasteCommand = new Classes.ChannelCodes.PasteCommandClass(PasteId);
|
|
break;
|
|
case GroupChannel.PASTE_ID:
|
|
default:
|
|
PasteCommand = new Classes.Groups.PasteCommandClass(PasteId);
|
|
break;
|
|
}
|
|
CommandManager.RegisterClassCommandBinding(GetType(),
|
|
new CommandBinding(PasteCommand, Paste));
|
|
_registered = true;
|
|
}
|
|
private void Paste(object sender, ExecutedRoutedEventArgs e)
|
|
{
|
|
}
|
|
#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(ChannelNameBuilder), // 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)
|
|
{
|
|
ChannelNameBuilder ccb = (ChannelNameBuilder)d;
|
|
|
|
ccb?.UpdatePossibleChannels();
|
|
}
|
|
|
|
public IList<IChannelCode> AllChannelCodes
|
|
{
|
|
get => (IList<IChannelCode>)GetValue(AllChannelCodesProperty);
|
|
set => SetValue(AllChannelCodesProperty, value);
|
|
}
|
|
/// <summary>
|
|
/// lock to enforce updatepossiblechannels doesn't become re-entrant
|
|
/// </summary>
|
|
private static object MyLock = new object();
|
|
/// <summary>
|
|
/// this is the method that updates the popup with channel codes
|
|
/// immediate means do it now, (as opposed to whenever the user stops typing)
|
|
/// this is done by using a timer, as long as the user keeps typing the timer gets reset
|
|
/// </summary>
|
|
/// <param name="immediateUpdate"></param>
|
|
public void UpdatePossibleChannels(bool immediateUpdate = false)
|
|
{
|
|
if (null == _lookupPopup) return;
|
|
lock (MyLock)
|
|
{
|
|
var nameUpple = ChannelName.ToUpper();
|
|
if (immediateUpdate)
|
|
{
|
|
_possiblesTimer?.Stop();
|
|
_possiblesTimer = null;
|
|
_lookupPopup.PossibleChannels = ChannelNamesFunc?.Invoke()
|
|
.Where(ch =>
|
|
ch.CodeType == CodeType &&
|
|
ch.Name.ToUpper().Contains(nameUpple)).Select(
|
|
code => new { code.Code, code.Name }).ToList() ??
|
|
AllChannelCodes
|
|
.Where(ch =>
|
|
ch.CodeType == CodeType &&
|
|
ch.Name.ToUpper().Contains(nameUpple))
|
|
.Select(code => new { code.Code, code.Name }).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) => { UpdatePossibleChannels(true); };
|
|
_possiblesTimer.Start();
|
|
}
|
|
else
|
|
{
|
|
_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(ChannelNameBuilder), // 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)
|
|
{
|
|
ChannelNameBuilder ccb = (ChannelNameBuilder)d;
|
|
if (!(bool)e.NewValue)
|
|
{
|
|
ccb._lookupPopup = null;
|
|
}
|
|
}
|
|
|
|
public bool ShowChannelCodeLookupHelper
|
|
{
|
|
get => (bool)GetValue(ShowChannelCodeLookupHelperProperty);
|
|
set => SetValue(ShowChannelCodeLookupHelperProperty, value);
|
|
}
|
|
|
|
public static readonly DependencyProperty LookupHelperUpdateDelay2Property =
|
|
DependencyProperty.Register(
|
|
"LookupHelperUpdateDelay2", // The name of the DependencyProperty
|
|
typeof(int), // The type of the DependencyProperty
|
|
typeof(ChannelNameBuilder), // The type of the owner of the DependencyProperty
|
|
new PropertyMetadata(
|
|
400,
|
|
LookupHelperUpdateDelay2Changed,
|
|
CoerceLookupHelperUpdateDelay2
|
|
)
|
|
);
|
|
|
|
private static object CoerceLookupHelperUpdateDelay2(DependencyObject d, object baseValue)
|
|
{
|
|
return (int)baseValue < 0 ? 0 : (int)baseValue;
|
|
}
|
|
|
|
private static void LookupHelperUpdateDelay2Changed(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
|
{
|
|
if (!(d is ChannelNameBuilder ccb)) return;
|
|
ccb._possiblesTimer?.Stop();
|
|
ccb._possiblesTimer = null;
|
|
}
|
|
|
|
public int LookupHelperUpdateDelay
|
|
{
|
|
get => (int)GetValue(LookupHelperUpdateDelay2Property);
|
|
set => SetValue(LookupHelperUpdateDelay2Property, 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 = 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 (null != lookupMB)
|
|
{
|
|
_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 });
|
|
}
|
|
}
|
|
}
|
|
if (sender is TextBox tb)
|
|
{
|
|
tb.SelectAll();
|
|
}
|
|
}
|
|
|
|
private void LookupPopupOnChannelCodeSelected(object sender, string code, string name)
|
|
{
|
|
ChannelName = name;
|
|
ChannelCodeSelected?.Invoke(this, code, name, CodeType);
|
|
Keyboard.ClearFocus();
|
|
}
|
|
|
|
private void MainEditBox_OnPreviewKeyDown(object sender, KeyEventArgs e)
|
|
{
|
|
if (CodeType != ChannelEnumsAndConstants.ChannelCodeType.ISO) { return; }
|
|
//might be faster to have a preset regex for this, but seems to run really fast anyhow...
|
|
bool isAlpha = (e.Key >= Key.A && e.Key <= Key.Z) || e.Key == Key.Space; //need to allow space for names.
|
|
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 && (!_lookupPopup?.IsKeyboardFocusWithin ?? true))
|
|
{
|
|
//now that we've lost focus, we can coerce the code
|
|
//Code = Code;
|
|
}
|
|
}
|
|
|
|
private void ChannelNameBuilder_OnLoaded(object sender, RoutedEventArgs e)
|
|
{
|
|
Window w = Window.GetWindow(this);
|
|
if (w != null)
|
|
{
|
|
w.LocationChanged += delegate
|
|
{
|
|
if (IsKeyboardFocusWithin)
|
|
{
|
|
Keyboard.ClearFocus();
|
|
}
|
|
};
|
|
}
|
|
var scrollParent = Utils.Utils.FindParent<ScrollViewer>(this);
|
|
if (scrollParent != null)
|
|
{
|
|
scrollParent.ScrollChanged += delegate (object o, ScrollChangedEventArgs args)
|
|
{
|
|
if (IsKeyboardFocusWithin &&
|
|
(args.HorizontalChange != 0 || args.VerticalChange != 0))
|
|
{
|
|
Keyboard.ClearFocus();
|
|
}
|
|
};
|
|
}
|
|
}
|
|
|
|
private void TextBoxSourceUpdated(object sender, DataTransferEventArgs e)
|
|
{
|
|
var eventAggregator = ContainerLocator.Container.Resolve<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);
|
|
}
|
|
}
|
|
}
|