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,61 @@
using System.Collections.Generic;
using System.Linq;
using System.Windows.Controls;
using System.Windows.Data;
namespace DTS.Common.Controls
{
public class AutoSizedGridView : GridView
{
HashSet<int> _allColumns;
HashSet<int> _autoWidthColumns;
Dictionary<int, BindingExpression> _widthBoundColumns;
protected override void PrepareItem(ListViewItem item)
{
if (null == _allColumns || !Columns.Select(col => col.GetHashCode()).SequenceEqual(_allColumns))
{
_allColumns = new HashSet<int>();
_autoWidthColumns = new HashSet<int>();
_widthBoundColumns = new Dictionary<int, BindingExpression>();
foreach (var column in Columns)
{
_allColumns.Add(column.GetHashCode());
if (double.IsNaN(column.Width) && //Width == "Auto"
null == BindingOperations.GetBindingExpression(column, GridViewColumn.WidthProperty)) //Width isn't bound
{
_autoWidthColumns.Add(column.GetHashCode());
}
else if (null != BindingOperations.GetBindingExpression(column, GridViewColumn.WidthProperty))
{
_widthBoundColumns.Add(column.GetHashCode(), BindingOperations.GetBindingExpression(column, GridViewColumn.WidthProperty));
}
}
}
foreach (var column in Columns)
{
if (_autoWidthColumns.Contains(column.GetHashCode()))
{
//force remeasure
if (double.IsNaN(column.Width))
{
column.Width = column.ActualWidth;
}
column.Width = double.NaN;
}
else if (_widthBoundColumns.ContainsKey(column.GetHashCode()))
{
//check if binding lost, re-bind
if (null == BindingOperations.GetBindingExpression(column, GridViewColumn.WidthProperty))
{
BindingOperations.SetBinding(column, GridViewColumn.WidthProperty, _widthBoundColumns[column.GetHashCode()].ParentBindingBase);
}
}
}
base.PrepareItem(item);
}
}
}

View File

@@ -0,0 +1,39 @@
<UserControl x:Class="DTS.Common.Controls.ChannelCodeBuilder"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:DTS.Common.Controls"
xmlns:strings="clr-namespace:DTS.Common.Strings"
xmlns:behaviors="clr-namespace:DTS.Common.Behaviors"
mc:Ignorable="d"
x:Name="dtsChannelCodeBuilder"
d:DesignHeight="450" d:DesignWidth="800" Loaded="ChannelCodeBuilder_OnLoaded"
AutomationProperties.AutomationId="CodeChannelBuilderUserControl"
>
<UserControl.Resources>
<local:CodeTypeToMaxLengthConverter x:Key="CodeTypeToMaxLengthConverter" />
<local:CodeTypeToCharacterCasingConverter x:Key="CodeTypeToCharacterCasingConverter" />
</UserControl.Resources>
<TextBox x:Name="MainEditBox" FontFamily="Courier New" x:FieldModifier="public" HorizontalAlignment="Stretch" DataContext="{Binding ElementName=dtsChannelCodeBuilder}"
MaxLength="{Binding CodeType, Converter={StaticResource CodeTypeToMaxLengthConverter}}"
LostFocus="MainEditBox_OnLostFocus"
GotKeyboardFocus="MainEditBox_OnGotKeyboardFocus"
MouseDoubleClick="MainEditBox_OnMouseDoubleClick"
PreviewKeyDown="MainEditBox_OnPreviewKeyDown"
AutomationProperties.AutomationId="CCB_MainEditTextBox"
behaviors:TextBoxPasteBehavior.PasteCommand="{Binding PasteCommand}"
Binding.SourceUpdated="TextBoxSourceUpdated" CharacterCasing="{Binding ElementName=dtsChannelCodeBuilder,Path=CodeType, Converter={StaticResource CodeTypeToCharacterCasingConverter}}"
TextChanged="MainEditBox_OnTextChanged"
SelectionChanged="MainEditBox_SelectionChanged"
Tag="{Binding Tag}">
<TextBox.Text>
<Binding NotifyOnSourceUpdated="True" Path="Code" UpdateSourceTrigger="PropertyChanged" />
</TextBox.Text>
<TextBox.InputBindings>
<KeyBinding Key="Enter"
Command="{local:UpdatePropertySourceWhenEnterPressed}"
CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor,AncestorType={x:Type TextBox}}}" />
</TextBox.InputBindings>
</TextBox>
</UserControl>

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 Prism.Commands;
using Prism.Events;
using Prism.Ioc;
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 = 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);
}
//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,36 @@
<UserControl x:Class="DTS.Common.Controls.ChannelNameBuilder"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:DTS.Common.Controls"
xmlns:strings="clr-namespace:DTS.Common.Strings"
xmlns:behaviors="clr-namespace:DTS.Common.Behaviors"
mc:Ignorable="d"
x:Name="dtsChannelNameBuilder"
d:DesignHeight="450" d:DesignWidth="800" Loaded="ChannelNameBuilder_OnLoaded"
AutomationProperties.AutomationId="NameChannelBuilderUserControl"
>
<UserControl.Resources>
<!--<local:CodeTypeToCharacterCasingConverter x:Key="CodeTypeToCharacterCasingConverter" />-->
</UserControl.Resources>
<TextBox x:Name="MainEditBox" x:FieldModifier="public" HorizontalAlignment="Stretch" DataContext="{Binding ElementName=dtsChannelNameBuilder}"
LostFocus="MainEditBox_OnLostFocus"
GotKeyboardFocus="MainEditBox_OnGotKeyboardFocus"
MouseDoubleClick="MainEditBox_OnMouseDoubleClick"
PreviewKeyDown="MainEditBox_OnPreviewKeyDown"
AutomationProperties.AutomationId="CNB_MainEditTextBox"
behaviors:TextBoxPasteBehavior.PasteCommand="{Binding PasteCommand}"
Binding.SourceUpdated="TextBoxSourceUpdated"
TextChanged="MainEditBox_OnTextChanged"
Tag="{Binding Tag}">
<TextBox.Text>
<Binding NotifyOnSourceUpdated="True" Path="ChannelName" UpdateSourceTrigger="PropertyChanged" />
</TextBox.Text>
<TextBox.InputBindings>
<KeyBinding Key="Enter"
Command="{local:UpdatePropertySourceWhenEnterPressed}"
CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor,AncestorType={x:Type TextBox}}}" />
</TextBox.InputBindings>
</TextBox>
</UserControl>

View File

@@ -0,0 +1,432 @@
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);
}
}
}

View File

@@ -0,0 +1,93 @@
<UserControl x:Class="DTS.Common.Controls.CommonStatusRibbon"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d" d:DesignHeight="80" d:DesignWidth="1366" x:Name="commonStatusRibbon">
<Grid>
<Grid HorizontalAlignment="Stretch" Visibility="Visible" DataContext="{Binding ElementName=commonStatusRibbon}">
<Grid.Background>
<SolidColorBrush Color="{Binding AggregateStatusColor, FallbackValue=Yellow}" />
</Grid.Background>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid Grid.Column="0">
<Rectangle Fill="{DynamicResource Brush_StatusRibbonErrorBackgroundDark}" Visibility="{Binding AlertVisibility, FallbackValue=Hidden}" />
<Grid Background="{DynamicResource Brush_StatusRibbonErrorBackgroundLight}" Visibility="{Binding AlertVisibility, FallbackValue=Visible}">
<Grid.Style>
<Style TargetType="{x:Type Grid}">
<Style.Resources>
<Storyboard x:Key="flashAnimation" >
<DoubleAnimation Storyboard.TargetProperty="Opacity" From="0.2" To="1" AutoReverse="True" Duration="0:0:0.3" RepeatBehavior="Forever" />
</Storyboard>
</Style.Resources>
<Style.Triggers>
<EventTrigger RoutedEvent="Window.Loaded">
<BeginStoryboard Name="flash" Storyboard="{StaticResource flashAnimation}" />
</EventTrigger>
</Style.Triggers>
</Style>
</Grid.Style>
</Grid>
<TextBlock FontFamily="Segoe UI" FontSize="11"
Foreground="{Binding TextColor}" Text="{Binding AggregateStatusText, FallbackValue=Connecting}" Margin="0" Width="200"/>
</Grid>
<ProgressBar Grid.Column="1" Margin="0" HorizontalContentAlignment="Stretch" Visibility="{Binding ProgressBarVisibility}" Value="{Binding ProgressBarValue,FallbackValue=30}"/>
</Grid>
<Grid MinHeight="80" HorizontalAlignment="Stretch" Visibility="Collapsed" DataContext="{Binding ElementName=commonStatusRibbon}">
<Grid.Background>
<SolidColorBrush Color="{Binding AggregateStatusColor, FallbackValue=Yellow}" />
</Grid.Background>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Rectangle
Fill="{DynamicResource Brush_StatusRibbonErrorBackgroundDark}"
Grid.Row="0" Grid.RowSpan="2"
Visibility="{Binding AlertVisibility, FallbackValue=Hidden}"
/>
<Grid
Background="{DynamicResource Brush_StatusRibbonErrorBackgroundLight}"
Grid.Row="0" Grid.RowSpan="2"
Visibility="{Binding AlertVisibility, FallbackValue=Visible}">
<Grid.Style>
<Style TargetType="{x:Type Grid}">
<Style.Resources>
<Storyboard x:Key="flashAnimation" >
<DoubleAnimation Storyboard.TargetProperty="Opacity" From="0.2" To="1" AutoReverse="True" Duration="0:0:0.3" RepeatBehavior="Forever" />
</Storyboard>
</Style.Resources>
<Style.Triggers>
<EventTrigger RoutedEvent="Window.Loaded">
<BeginStoryboard Name="flash" Storyboard="{StaticResource flashAnimation}" />
</EventTrigger>
</Style.Triggers>
</Style>
</Grid.Style>
</Grid>
<TextBlock
Grid.Row="0"
VerticalAlignment="Center"
Foreground="{Binding TextColor}"
Text="{Binding AggregateStatusText, FallbackValue=Connecting}"
HorizontalAlignment="Center"
Margin="0"
x:Name="lblAggregateStatusText"
/>
<ProgressBar
Margin="0"
Grid.Row="1"
Height="40"
VerticalAlignment="Bottom"
HorizontalContentAlignment="Stretch"
Visibility="{Binding ProgressBarVisibility}"
Value="{Binding ProgressBarValue,FallbackValue=30}"
/>
</Grid>
</Grid>
</UserControl>

View File

@@ -0,0 +1,321 @@
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.ComponentModel;
using DTS.Common.Events;
using Prism.Events;
using Prism.Ioc;
using Unity;
namespace DTS.Common.Controls
{
/// <summary>
/// Interaction logic for CommonStatusRibbon.xaml
/// this is a helper class for the status ribbon + status text shown on pages that have status feedback
/// </summary>
public partial class CommonStatusRibbon : UserControl, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected bool SetProperty<T>(ref T storage, T value, string propertyName = null)
{
if (Equals(storage, value)) return false;
storage = value;
OnPropertyChanged(propertyName);
return true;
}
protected void OnPropertyChanged(string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private IUnityContainer _unityContainer { get; }
private IEventAggregator _eventAggregator { get; }
public CommonStatusRibbon()
{
InitializeComponent();
if (_unityContainer == null) { _unityContainer = ContainerLocator.Container.Resolve<IUnityContainer>(); }
if (_eventAggregator == null)
{
_eventAggregator = ContainerLocator.Container.Resolve<IEventAggregator>();
}
_eventAggregator.GetEvent<ProgressBarEvent>().Subscribe(OnProgressBarEvent, ThreadOption.PublisherThread);
}
private void OnProgressBarEvent(ProgressBarEventArg arg)
{
if (arg.ProgressBarName != ProgressBarName) { return; }
if (null != arg.ProgressBarText)
{
SetAggregateStatusText(arg.ProgressBarText);
}
if (!double.IsNaN(arg.ProgressBarPercentage))
{
SetProgressValue(Convert.ToInt32(arg.ProgressBarPercentage));
}
SetProgressBarVisibility(arg.ProgressBarVisibility);
SetAggregateStatusColor(arg.ProgressBarColor);
}
#region ProgressBarValue
public static readonly DependencyProperty SetProgressBarValueProperty =
DependencyProperty.Register("ProgressBarValue", typeof(int), typeof(CommonStatusRibbon),
new PropertyMetadata(0, OnProgressValueChanged));
private static void OnProgressValueChanged(DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
var sr = d as CommonStatusRibbon;
sr?.OnProgressBarValueChanged(e);
}
private void OnProgressBarValueChanged(DependencyPropertyChangedEventArgs e)
{
SetProgressValue(Convert.ToInt32(e.NewValue));
}
private delegate void SetProgressValueDelegate(int value);
private int _progressBarValue;
/// <summary>
/// sets the progress bar progress. Not thread friendly, call SetProgressValue if coming from a background thread
/// </summary>
public int ProgressBarValue
{
get => _progressBarValue;
set => SetProperty(ref _progressBarValue, value, "ProgressBarValue");
}
/// <summary>
/// sets the progress bar progress
/// thread friendly
/// </summary>
/// <param name="value">progress from 0-100. values less than 0 or greater than 100 are normalized to 0 or 100</param>
public void SetProgressValue(int value)
{
if (!Dispatcher.CheckAccess())
{
Dispatcher.BeginInvoke(new SetProgressValueDelegate(SetProgressValue), value);
return;
}
if (value < 0) { value = 0; }
else if (value > 100) { value = 100; }
ProgressBarValue = value;
}
#endregion
#region ProgressBarVisibility
public static readonly DependencyProperty SetProgressBarVisibilityProperty =
DependencyProperty.Register("ProgressBarVisibility", typeof(Visibility), typeof(CommonStatusRibbon),
new PropertyMetadata(Visibility.Collapsed, OnProgressVisibilityChanged));
private static void OnProgressVisibilityChanged(DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
var sr = d as CommonStatusRibbon;
sr?.OnProgressBarVisibilityChanged(e);
}
private void OnProgressBarVisibilityChanged(DependencyPropertyChangedEventArgs e)
{
SetProgressBarVisibility((Visibility)e.NewValue);
}
/// <summary>
/// controls the progress bar visibility.
/// SetProgressBarVisibility is thread friendly
/// </summary>
private Visibility _progressBarVisibility = Visibility.Hidden;
public Visibility ProgressBarVisibility
{
get => _progressBarVisibility;
set => SetProperty(ref _progressBarVisibility, value, "ProgressBarVisibility");
}
private delegate void SetProgressBarVisibilityDelegate(Visibility v);
/// <summary>
/// sets the progress bar visibility
/// thread friendly
/// </summary>
/// <param name="v"></param>
public void SetProgressBarVisibility(Visibility v)
{
if (!Dispatcher.CheckAccess())
{
Dispatcher.BeginInvoke(new SetProgressBarVisibilityDelegate(SetProgressBarVisibility), v);
}
else
{
ProgressBarVisibility = v;
}
}
#endregion
#region ProgressBarName
public static readonly DependencyProperty ProgressBarNameProperty =
DependencyProperty.Register("ProgressBarName", typeof(string), typeof(CommonStatusRibbon),
new PropertyMetadata("OverallStatus", OnProgressBarNameChanged));
private static void OnProgressBarNameChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var sr = d as CommonStatusRibbon;
sr?.OnProgressBarNameChanged(e);
}
private void OnProgressBarNameChanged(DependencyPropertyChangedEventArgs e)
{
SetProgressBarName(Convert.ToString(e.NewValue));
}
public void SetProgressBarName(string s)
{
if (!Dispatcher.CheckAccess())
{
Dispatcher.BeginInvoke(new Action(() => SetProgressBarName(s)));
}
else
{
ProgressBarName = s;
}
}
private string _progressBarName = "OverallStatus";
public string ProgressBarName
{
get => _progressBarName;
set => SetProperty(ref _progressBarName, value, "ProgressBarName");
}
#endregion ProgressBarName
#region AggregateStatusText
public static readonly DependencyProperty SetAggregateStatusTextProperty =
DependencyProperty.Register("AggregateStatusText", typeof(string), typeof(CommonStatusRibbon),
new PropertyMetadata("", OnAggregateStatusTextChanged));
private static void OnAggregateStatusTextChanged(DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
var sr = d as CommonStatusRibbon;
sr?.OnAggregateStatusTextChanged(e);
}
private void OnAggregateStatusTextChanged(DependencyPropertyChangedEventArgs e)
{
SetAggregateStatusText(Convert.ToString(e.NewValue));
}
/// <summary>
/// retrieves the status text
/// to set status text use SetStatusTextNoTranslate or the property Status
/// </summary>
private string _aggregateStatusText = "---";
public string AggregateStatusText
{
get => _aggregateStatusText;
set => SetProperty(ref _aggregateStatusText, value, "AggregateStatusText");
}
public void SetAggregateStatusText(string s)
{
if (!Dispatcher.CheckAccess())
{
Dispatcher.BeginInvoke(new Action(() => SetAggregateStatusText(s)));
}
else
{
AggregateStatusText = s;
}
}
#endregion
#region AggregateStatusColor
public static readonly DependencyProperty SetAggregateStatusColorProperty =
DependencyProperty.Register("AggregateStatusColor", typeof(Color), typeof(CommonStatusRibbon),
new PropertyMetadata(Colors.AliceBlue, OnAggregateStatusColorChanged));
private static void OnAggregateStatusColorChanged(DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
var sr = d as CommonStatusRibbon;
sr?.OnAggregateStatusColorChanged(e);
}
private void OnAggregateStatusColorChanged(DependencyPropertyChangedEventArgs e)
{
SetAggregateStatusColor((Color)e.NewValue);
}
private delegate void SetStatusColorDelegate(Color c);
/// <summary>
/// sets the color of the ribbon background
/// thread friendly
/// </summary>
/// <param name="c"></param>
public void SetAggregateStatusColor(Color c)
{
if (!Dispatcher.CheckAccess())
{
Dispatcher.BeginInvoke(new SetStatusColorDelegate(SetAggregateStatusColor), c);
return;
}
AggregateStatusColor = c;
}
private Color _aggregateStatusColor = Colors.AliceBlue;
/// <summary>
/// determines the color of the status background
/// not thread friendly
/// SetAggregateStatusColor is thread friendly
/// </summary>
public Color AggregateStatusColor
{
get => _aggregateStatusColor;
set
{
SetAlert(false);
SetProperty(ref _aggregateStatusColor, value, "AggregateStatusColor");
}
}
#endregion
private Visibility _alertVisibility = Visibility.Hidden;
public Visibility AlertVisibility
{
get => _alertVisibility;
set => SetProperty(ref _alertVisibility, value, "AlertVisibility");
}
private SolidColorBrush _textColor = new SolidColorBrush(Colors.Black);
public SolidColorBrush TextColor
{
get => _textColor;
set
{
SetProperty(ref _textColor, value, "TextColor");
OnPropertyChanged("TextColor");
}
}
private bool _isAlertSet;
public delegate void SetAlertDelegate(bool set);
public void SetAlert(bool set)
{
if (!Dispatcher.CheckAccess())
{
Dispatcher.BeginInvoke(new SetAlertDelegate(SetAlert), set);
return;
}
if (set == _isAlertSet) { return; }
_isAlertSet = set;
if (set)
{
//turn on alert
TextColor = BrushesAndColors.Brush_ArmSystemForeground;
AlertVisibility = Visibility.Visible;
}
else
{
//turn off alert
TextColor = new SolidColorBrush(Colors.Black);
AlertVisibility = Visibility.Hidden;
}
}
}
}

View File

@@ -0,0 +1,102 @@
using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
namespace DTS.Common.Controls
{
public class DynamicGrid : Grid, INotifyPropertyChanged
{
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
protected bool SetProperty<T>(ref T storage, T value, string propertyName = null)
{
if (Equals(storage, value)) return false;
storage = value;
OnPropertyChanged(propertyName);
return true;
}
protected void OnPropertyChanged(string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
public DynamicGrid()
: base()
{
Refresh();
}
protected override void OnVisualChildrenChanged(DependencyObject visualAdded, DependencyObject visualRemoved)
{
base.OnVisualChildrenChanged(visualAdded, visualRemoved);
Refresh();
}
public void Refresh()
{
ColumnDefinitions.Clear();
for (byte i = 0; i < GridColumns; i++)
{
ColumnDefinitions.Add(new ColumnDefinition());
if (i + 1 != GridColumns)
{
ColumnDefinitions[i].Width = new GridLength(1, GridUnitType.Auto);
}
else
{
ColumnDefinitions[i].Width = new GridLength(1, GridUnitType.Star);
}
}
var curRow = 0;
var curCol = 0;
RowDefinitions.Clear();
if (Children != null)
{
foreach (UIElement curChild in Children)
{
if (0 == curCol)
{
// We're on the first column, we need a new row for the child
RowDefinitions.Add(new RowDefinition() { Height = new GridLength(1, GridUnitType.Auto) });
}
// Set the child to its row and column
SetRow(curChild, curRow);
SetColumn(curChild, curCol);
// Iderate
if (curCol < GridColumns - 1)
{
// We're moving to the next column
curCol++;
}
else
{
// We're at the end, go back to clumn 0
curCol = 0;
curRow++;
}
}
}
RowDefinitions.Add(new RowDefinition() { Height = new GridLength(1, GridUnitType.Star) });
}
private byte _columns = 2;
public byte GridColumns
{
get => _columns;
set { _columns = value; Refresh(); }
}
}
}

View File

@@ -0,0 +1,51 @@
using System;
using System.Windows;
using System.Windows.Media.Animation;
namespace DTS.Common.Controls
{
public class GridLengthAnimation : AnimationTimeline
{
public GridLengthAnimation()
{
// no-op
}
public GridLength From
{
get => (GridLength)GetValue(FromProperty);
set => SetValue(FromProperty, value);
}
public static readonly DependencyProperty FromProperty =
DependencyProperty.Register("From", typeof(GridLength), typeof(GridLengthAnimation));
public GridLength To
{
get => (GridLength)GetValue(ToProperty);
set => SetValue(ToProperty, value);
}
public static readonly DependencyProperty ToProperty =
DependencyProperty.Register("To", typeof(GridLength), typeof(GridLengthAnimation));
public override Type TargetPropertyType => typeof(GridLength);
protected override Freezable CreateInstanceCore()
{
return new GridLengthAnimation();
}
public override object GetCurrentValue(object defaultOriginValue, object defaultDestinationValue, AnimationClock animationClock)
{
var fromValue = From.Value;
var toValue = To.Value;
if (fromValue > toValue)
{
return new GridLength((1 - animationClock.CurrentProgress.Value) * (fromValue - toValue) + toValue, To.IsStar ? GridUnitType.Star : GridUnitType.Pixel);
}
return new GridLength(animationClock.CurrentProgress.Value * (toValue - fromValue) + fromValue, To.IsStar ? GridUnitType.Star : GridUnitType.Pixel);
}
}
}

View File

@@ -0,0 +1,47 @@
<UserControl x:Class="DTS.Common.Controls.GridViewColumnHeaderSearchable"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:DTS.Common.Controls"
mc:Ignorable="d"
x:Name="dtsGridViewColumnHeader"
d:DesignHeight="20" d:DesignWidth="200" PreviewMouseLeftButtonDown="PreviewLeftButtonUp">
<UserControl.Resources>
<ResourceDictionary>
<Geometry x:Key="FilterIconGeometry">M2.9999999,0.99999994 L2.9999999,2 3.9999999,2 3.9999999,3 4.9999999,3 4.9999999,4 5.9999999,4 5.9999999,3 6.9999999,3 6.9999999,2 7.9999999,2 7.9999999,0.99999994 6.9999999,0.99999994 5.9999999,0.99999994 4.9999999,0.99999994 3.9999999,0.99999994 z M0,0 L11,0 11,0.99999994 9.9999999,0.99999994 9.9999999,2 8.9999999,2 8.9999999,3 7.9999999,3 7.9999999,4 6.9999999,4 6.9999999,12 3.9999999,12 3.9999999,4 2.9999999,4 2.9999999,3 1.9999999,3 1.9999999,2 0.99999994,2 0.99999994,0.99999994 0,0.99999994 z</Geometry>
<Geometry x:Key="DownArrowIconGeometry">F0 M 0,0L 10,0L 5,7L 0,0 Z</Geometry>
<local:BoolToInvertedBoolConverter x:Key="BoolToInvertedBoolConverter"/>
<Geometry x:Key="MagnifierIconGeometry">M53.049224,12 C41.049224,12 31.049224,22 31.049224,34 31.049224,46 41.049224,56 53.049224,56 65.049224,56 75.049224,46 75.049224,34 75.049224,22 65.049224,12 53.049224,12 z M53.049224,0 C72.049224,0 87.049224,15 87.049224,34 87.049224,53 72.049224,68 53.049224,68 49.049224,68 45.549224,67.5 42.049224,66 L41.549224,66 13.5,103.5 3.1554435E-30,92.092051 27.049224,55.5 26.987233,55.354591 C22.987233,49.354591 19.049224,42 19.049224,34 19.049224,15 34.049224,0 53.049224,0 z</Geometry>
</ResourceDictionary>
</UserControl.Resources>
<Grid DataContext="{Binding ElementName=dtsGridViewColumnHeader}" x:Name="mainGrid" Height="20" MaxHeight="20">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Border BorderThickness="0" Grid.Column="0">
<TextBlock Text="{Binding Path=HeaderTitle, FallbackValue='I Should Not Be Here'}" HorizontalAlignment="Left" Margin="5,0,5,0" AutomationProperties.AutomationId="ColumnTitle"/>
</Border>
<!--<Button Grid.Column="0" Content="{Binding Path=HeaderTitle, FallbackValue='I should not be here'}" HorizontalAlignment="Left" Margin="5,0,5,0" Click="ButtonClick" /> -->
<ToggleButton Grid.Column="1" x:Name="TogglePopupButton" Width="20" Height="20" MaxWidth="20" MaxHeight="20" IsEnabled="{Binding ElementName=ToggledPopup, Path=IsOpen, Converter={StaticResource BoolToInvertedBoolConverter}}"
IsChecked="{Binding ToggleButtonIsChecked, Mode=TwoWay, FallbackValue=True}" >
<Path x:Name="BtnArrow" VerticalAlignment="Center" HorizontalAlignment="Center" Width="8" Fill="#FF527DB5" Stretch="Uniform"
Data="{Binding ToggleIconGeometry, FallbackValue={StaticResource DownArrowIconGeometry}}"/>
</ToggleButton>
<Popup Grid.Column="0" Grid.ColumnSpan="2" x:Name="ToggledPopup" HorizontalAlignment="Stretch" Placement="Bottom" VerticalAlignment="Stretch" StaysOpen="False" IsOpen="{Binding ToggleButtonIsChecked, Mode=TwoWay}">
<Border Background="#FFEEEEEE" BorderBrush="#FF888888" BorderThickness="1">
<Grid HorizontalAlignment="Stretch" Margin="3">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Path Grid.Column="0" Data="{StaticResource MagnifierIconGeometry}" Margin="5" Width="10" Height="10" Fill="#FF888888" Stretch="Fill" />
<TextBox Grid.Column="1" Width="150" Text="{Binding HeaderSearchTerm}" KeyUp="HeaderSearchTerm_KeyDown" />
</Grid>
</Border>
</Popup>
</Grid>
<!--</GridViewColumnHeader>-->
</UserControl>

View File

@@ -0,0 +1,267 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Media;
using DTS.Common.Base;
using DTS.Common.Events;
using DTS.Common.Utilities.Logging;
using Prism.Events;
using Prism.Ioc;
namespace DTS.Common.Controls
{
/// <inheritdoc cref="IBasePropertyChanged" />
/// <summary>
/// Interaction logic for GridViewColumnHeaderSearchable.xaml
/// </summary>
public partial class GridViewColumnHeaderSearchable : UserControl, IBasePropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public bool SetProperty<T>(ref T storage, T value, string propertyName = null)
{
if (Equals(storage, value)) return false;
storage = value;
OnPropertyChanged(propertyName);
return true;
}
public void OnPropertyChanged(string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public GridViewColumnHeaderSearchable()
{
InitializeComponent();
var eventAggregator = ContainerLocator.Container.Resolve<IEventAggregator>();
eventAggregator.GetEvent<ListViewStatusEvent>().Subscribe(OnListviewStatusEvent, ThreadOption.UIThread);
}
public override string ToString()
{
return HeaderTitle;
}
private void OnListviewStatusEvent(ListViewStatusArg arg)
{
if (arg.Status != ListViewStatusArg.ListViewStatus.Unloaded)
{
return;
}
if (arg.Id == ListviewId)
{
HeaderSearchTerm = string.Empty;
}
}
public string ListviewId
{
get => (string)GetValue(ListviewIdProperty);
set => SetValue(ListviewIdProperty, value);
}
public static readonly DependencyProperty ListviewIdProperty =
DependencyProperty.Register(
"ListviewId",
typeof(string),
typeof(GridViewColumnHeaderSearchable), new PropertyMetadata(""));
public string HeaderTitle
{
get => (string)GetValue(HeaderTitleProperty);
set => SetValue(HeaderTitleProperty, value);
}
// Using a DependencyProperty enables animation, styling, binding, etc.
public static readonly DependencyProperty HeaderTitleProperty =
DependencyProperty.Register(
"HeaderTitle", // The name of the DependencyProperty
typeof(string), // The type of the DependencyProperty
typeof(GridViewColumnHeaderSearchable), // The type of the owner of the DependencyProperty
new PropertyMetadata( // OnHeaderTitleChanged will be called when HeaderTitle changes
"Awesome", // The default value of the DependencyProperty
OnHeaderTitleChanged
)
);
private static void OnHeaderTitleChanged(
DependencyObject d,
DependencyPropertyChangedEventArgs e
)
{
if (d is GridViewColumnHeaderSearchable instance)
{
instance.HeaderTitle = (string)e.NewValue;
}
}
private bool _toggleButtonIsChecked = false;
/// <summary>
/// holds whether the toggle button is checked or not and controls whether the popup is open or not
/// </summary>
public bool ToggleButtonIsChecked
{
get => _toggleButtonIsChecked;
set
{
if (value == _toggleButtonIsChecked) return;
_toggleButtonIsChecked = value;
OnPropertyChanged("ToggleButtonIsChecked");
OnPropertyChanged("ToggleIconGeometry");
RaiseOpenChangedEvent(value);
}
}
public static readonly DependencyProperty HeaderIsCheckedProperty =
DependencyProperty.Register(
"ToggleButtonIsChecked",
typeof(bool),
typeof(GridViewColumnHeaderSearchable),
new PropertyMetadata(
false,
OnHeaderIsOpenChanged
)
);
private static void OnHeaderIsOpenChanged(
DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
if (!(d is GridViewColumnHeaderSearchable instance)) return;
instance.ToggleButtonIsChecked = (bool)e.NewValue;
var isOpen = instance.ToggleButtonIsChecked;
instance.RaiseOpenChangedEvent(isOpen);
}
// Create a custom routed event by first registering a RoutedEventID
// This event uses the bubbling routing strategy
public static readonly RoutedEvent OpenChangedEvent = EventManager.RegisterRoutedEvent(
"OpenChanged", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(GridViewColumnHeaderSearchable));
// Provide CLR accessors for the event
public event RoutedEventHandler OpenChanged
{
add => AddHandler(OpenChangedEvent, value);
remove => RemoveHandler(OpenChangedEvent, value);
}
// This method raises the Tap event
private void RaiseOpenChangedEvent(bool isOpen)
{
var newEventArgs = new RoutedEventArgs(OpenChangedEvent, isOpen);
RaiseEvent(newEventArgs);
}
public static readonly RoutedEvent ClickEvent = EventManager.RegisterRoutedEvent(
"ClickHandler", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(GridViewColumnHeaderSearchable));
public event RoutedEventHandler ClickHandler
{
add => AddHandler(ClickEvent, value);
remove => RemoveHandler(ClickEvent, value);
}
private void RaiseClickEvent(object tag)
{
var newEventArgs = new RoutedEventArgs(ClickEvent, tag);
RaiseEvent(newEventArgs);
}
private void PreviewLeftButtonUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
if (LogicalTreeHelper.GetParent((DependencyObject)e.OriginalSource) is Popup popupParent)
{
//RaiseClickEvent(Tag);
e.Handled = true;
}
}
private void HeaderSearchTerm_KeyDown(object sender, System.Windows.Input.KeyEventArgs e)
{
RaiseSearchEvent(((TextBox)sender).Text);
}
public Geometry ToggleIconGeometry
{
get
{
if (string.IsNullOrEmpty(HeaderSearchTerm))
{
return (Geometry)dtsGridViewColumnHeader.FindResource("DownArrowIconGeometry");
}
return (Geometry)dtsGridViewColumnHeader.FindResource("FilterIconGeometry");
}
}
public string HeaderSearchTerm
{
get => (string)GetValue(HeaderSearchTermProperty);
set => SetValue(HeaderSearchTermProperty, value);
}
// Using a DependencyProperty enables animation, styling, binding, etc.
public static readonly DependencyProperty HeaderSearchTermProperty =
DependencyProperty.Register(
"HeaderSearchTerm", // The name of the DependencyProperty
typeof(string), // The type of the DependencyProperty
typeof(GridViewColumnHeaderSearchable), // The type of the owner of the DependencyProperty
new PropertyMetadata( // OnHeaderTitleChanged will be called when HeaderTitle changes
"", // The default value of the DependencyProperty
OnHeaderSearchTermChanged
)
);
private static void OnHeaderSearchTermChanged(
DependencyObject d,
DependencyPropertyChangedEventArgs e
)
{
if (!(d is GridViewColumnHeaderSearchable instance)) return;
instance.HeaderSearchTerm = (string)e.NewValue;
instance.RaiseSearchEvent((string)e.NewValue);
}
// Create a custom routed event by first registering a RoutedEventID
// This event uses the bubbling routing strategy
public static readonly RoutedEvent SearchEvent = EventManager.RegisterRoutedEvent(
"Search", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(GridViewColumnHeaderSearchable));
// Provide CLR accessors for the event
public event RoutedEventHandler Search
{
add => AddHandler(SearchEvent, value);
remove => RemoveHandler(SearchEvent, value);
}
// This method raises the Tap event
private void RaiseSearchEvent(string searchTerm)
{
var newEventArgs = new RoutedEventArgs(SearchEvent, searchTerm);
RaiseEvent(newEventArgs);
OnPropertyChanged("ToggleIconGeometry");
}
}
public class BoolToInvertedBoolConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value is bool boolValue)
{
return !boolValue;
}
return false;
}
public object ConvertBack(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
throw new NotImplementedException("ConvertBack() of BoolToInvertedBoolConverter is not implemented");
}
}
}

View File

@@ -0,0 +1,53 @@
<UserControl x:Class="DTS.Common.Controls.GridViewColumnHeaderSearchableCheckBox"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:DTS.Common.Controls"
xmlns:strings="clr-namespace:DTS.Common.Strings"
mc:Ignorable="d"
x:Name="dtsGridViewColumnHeader"
d:DesignHeight="20" d:DesignWidth="200">
<UserControl.Resources>
<ResourceDictionary>
<Geometry x:Key="DownArrowIconGeometry">F0 M 0,0L 10,0L 5,7L 0,0 Z</Geometry>
<local:BoolToInvertedBoolConverter x:Key="BoolToInvertedBoolConverter"/>
<Style TargetType="Button" BasedOn="{StaticResource PageContentButtonDark}" />
</ResourceDictionary>
</UserControl.Resources>
<Grid DataContext="{Binding ElementName=dtsGridViewColumnHeader}" x:Name="mainGrid" Height="20" MaxHeight="20">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Border BorderThickness="0" Grid.Column="0" PreviewMouseLeftButtonDown="PreviewLeftButtonUp">
<TextBlock Text="{Binding Path=HeaderTitle, FallbackValue='I Should Not Be Here'}" HorizontalAlignment="Left" Margin="5,0,5,0" />
</Border>
<!--<Button Grid.Column="0" Content="{Binding Path=HeaderTitle, FallbackValue='I should not be here'}" HorizontalAlignment="Left" Margin="5,0,5,0" Click="ButtonClick" /> -->
<ToggleButton Grid.Column="1" x:Name="TogglePopupButton" Width="20" Height="20" MaxWidth="20" MaxHeight="20" IsEnabled="{Binding ElementName=ToggledPopup, Path=IsOpen, Converter={StaticResource BoolToInvertedBoolConverter}}"
IsChecked="{Binding ToggleButtonIsChecked, Mode=TwoWay, FallbackValue=True}">
<Path x:Name="BtnArrow" VerticalAlignment="Center" HorizontalAlignment="Center" Width="8" Fill="#FF527DB5" Stretch="Uniform"
Data="{Binding ToggleIconGeometry, FallbackValue={StaticResource DownArrowIconGeometry}}"/>
</ToggleButton>
<Popup Grid.Column="0" Grid.ColumnSpan="2" x:Name="ToggledPopup" HorizontalAlignment="Stretch" Placement="Bottom" VerticalAlignment="Stretch" StaysOpen="False" IsOpen="{Binding ToggleButtonIsChecked, Mode=TwoWay}" >
<Border Background="#FFEEEEEE" BorderBrush="#FF888888" BorderThickness="1">
<StackPanel Orientation="Vertical" VerticalAlignment="Stretch">
<Button Padding="10 5" Content="{x:Static strings:Strings.SelectAllParen}" Click="SelectAllButton_OnClick" />
<Button Padding="10, 5" Content="{x:Static strings:Strings.True}" Click="TrueButton_OnClick" />
<Button Padding="10, 5" Content="{x:Static strings:Strings.False}" Click="FalseButton_OnClick" />
</StackPanel>
<!--<<Grid HorizontalAlignment="Stretch" Margin="3">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
Button Grid.Column="0" Padding="10 5" Content="{x:Static strings:Strings.SelectAll}" Click="SelectAllButton_OnClick" />
<Button Grid.Column="1" Padding="10 5" Content="{x:Static strings:Strings.ClearAll}" Click="ClearAllButton_OnClick"/>
</Grid>-->
</Border>
</Popup>
</Grid>
<!--</GridViewColumnHeader>-->
</UserControl>

View File

@@ -0,0 +1,269 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Media;
using DTS.Common.Base;
using DTS.Common.Events;
using DTS.Common.Utilities.Logging;
using Prism.Events;
using Prism.Ioc;
namespace DTS.Common.Controls
{
/// <inheritdoc cref="IBasePropertyChanged" />
/// <summary>
/// Interaction logic for GridViewColumnHeaderSelectable.xaml
/// </summary>
public partial class GridViewColumnHeaderSearchableCheckBox : UserControl, IBasePropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public bool SetProperty<T>(ref T storage, T value, string propertyName = null)
{
if (Equals(storage, value)) return false;
storage = value;
OnPropertyChanged(propertyName);
return true;
}
public void OnPropertyChanged(string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public GridViewColumnHeaderSearchableCheckBox()
{
InitializeComponent();
var eventAggregator = ContainerLocator.Container.Resolve<IEventAggregator>();
eventAggregator.GetEvent<ListViewStatusEvent>().Subscribe(OnListviewStatusEvent, ThreadOption.UIThread);
}
public override string ToString()
{
return HeaderTitle;
}
private void OnListviewStatusEvent(ListViewStatusArg arg)
{
if (arg.Status != ListViewStatusArg.ListViewStatus.Unloaded)
{
return;
}
if (arg.Id == ListviewId)
{
}
}
public string ListviewId
{
get => (string)GetValue(ListviewIdProperty);
set => SetValue(ListviewIdProperty, value);
}
public static readonly DependencyProperty ListviewIdProperty =
DependencyProperty.Register(
"ListviewId",
typeof(string),
typeof(GridViewColumnHeaderSearchableCheckBox), new PropertyMetadata(""));
public string HeaderTitle
{
get => (string)GetValue(HeaderTitleProperty);
set => SetValue(HeaderTitleProperty, value);
}
// Using a DependencyProperty enables animation, styling, binding, etc.
public static readonly DependencyProperty HeaderTitleProperty =
DependencyProperty.Register(
"HeaderTitle", // The name of the DependencyProperty
typeof(string), // The type of the DependencyProperty
typeof(GridViewColumnHeaderSearchableCheckBox), // The type of the owner of the DependencyProperty
new PropertyMetadata( // OnHeaderTitleChanged will be called when HeaderTitle changes
"Awesome", // The default value of the DependencyProperty
OnHeaderTitleChanged
)
);
private static void OnHeaderTitleChanged(
DependencyObject d,
DependencyPropertyChangedEventArgs e
)
{
if (d is GridViewColumnHeaderSearchableCheckBox instance)
{
instance.HeaderTitle = (string)e.NewValue;
}
}
private bool _toggleButtonIsChecked = false;
/// <summary>
/// holds whether the toggle button is checked or not and controls whether the popup is open or not
/// </summary>
public bool ToggleButtonIsChecked
{
get => _toggleButtonIsChecked;
set
{
if (value == _toggleButtonIsChecked) return;
_toggleButtonIsChecked = value;
OnPropertyChanged("ToggleButtonIsChecked");
OnPropertyChanged("ToggleIconGeometry");
RaiseOpenChangedEvent(value);
}
}
public Geometry ToggleIconGeometry
{
get
{
if (string.IsNullOrEmpty(HeaderSearchTerm))
{
return (Geometry)dtsGridViewColumnHeader.FindResource("DownArrowIconGeometry");
}
return (Geometry)dtsGridViewColumnHeader.FindResource("FilterIconGeometry");
}
}
public string HeaderSearchTerm
{
get => (string)GetValue(HeaderSearchTermProperty);
set => SetValue(HeaderSearchTermProperty, value);
}
// Using a DependencyProperty enables animation, styling, binding, etc.
public static readonly DependencyProperty HeaderSearchTermProperty =
DependencyProperty.Register(
"HeaderSearchTerm", // The name of the DependencyProperty
typeof(string), // The type of the DependencyProperty
typeof(GridViewColumnHeaderSearchableCheckBox), // The type of the owner of the DependencyProperty
new PropertyMetadata( // OnHeaderTitleChanged will be called when HeaderTitle changes
"", // The default value of the DependencyProperty
OnHeaderSearchTermChanged
)
);
private static void OnHeaderSearchTermChanged(
DependencyObject d,
DependencyPropertyChangedEventArgs e
)
{
if (!(d is GridViewColumnHeaderSearchableCheckBox instance)) return;
instance.HeaderSearchTerm = (string)e.NewValue;
instance.RaiseSearchEvent((string)e.NewValue);
}
// Create a custom routed event by first registering a RoutedEventID
// This event uses the bubbling routing strategy
public static readonly RoutedEvent SearchEvent = EventManager.RegisterRoutedEvent(
"SearchCheckBox", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(GridViewColumnHeaderSearchableCheckBox));
// Provide CLR accessors for the event
public event RoutedEventHandler Search
{
add => AddHandler(SearchEvent, value);
remove => RemoveHandler(SearchEvent, value);
}
// This method raises the Tap event
private void RaiseSearchEvent(string searchTerm)
{
var newEventArgs = new RoutedEventArgs(SearchEvent, searchTerm);
RaiseEvent(newEventArgs);
OnPropertyChanged("ToggleIconGeometry");
}
public static readonly DependencyProperty HeaderIsCheckedProperty =
DependencyProperty.Register(
"ToggleButtonIsChecked",
typeof(bool),
typeof(GridViewColumnHeaderSearchableCheckBox),
new PropertyMetadata(
false,
OnHeaderIsOpenChanged
)
);
private static void OnHeaderIsOpenChanged(
DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
if (!(d is GridViewColumnHeaderSearchableCheckBox instance)) return;
instance.ToggleButtonIsChecked = (bool)e.NewValue;
var isOpen = instance.ToggleButtonIsChecked;
instance.RaiseOpenChangedEvent(isOpen);
}
// Create a custom routed event by first registering a RoutedEventID
// This event uses the bubbling routing strategy
public static readonly RoutedEvent OpenChangedEvent = EventManager.RegisterRoutedEvent(
"OpenChanged", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(GridViewColumnHeaderSearchableCheckBox));
// Provide CLR accessors for the event
public event RoutedEventHandler OpenChanged
{
add => AddHandler(OpenChangedEvent, value);
remove => RemoveHandler(OpenChangedEvent, value);
}
// This method raises the Tap event
private void RaiseOpenChangedEvent(bool isOpen)
{
var newEventArgs = new RoutedEventArgs(OpenChangedEvent, isOpen);
RaiseEvent(newEventArgs);
}
public static readonly RoutedEvent ClickEvent = EventManager.RegisterRoutedEvent(
"ClickHandler", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(GridViewColumnHeaderSearchableCheckBox));
public event RoutedEventHandler ClickHandler
{
add => AddHandler(ClickEvent, value);
remove => RemoveHandler(ClickEvent, value);
}
private void RaiseClickEvent(object tag)
{
var newEventArgs = new RoutedEventArgs(ClickEvent, tag);
RaiseEvent(newEventArgs);
}
private void PreviewLeftButtonUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
RaiseClickEvent(Tag);
}
private void SelectAllButton_OnClick(object sender, RoutedEventArgs e)
{
RaiseFilterEvent(@"All");
}
private void TrueButton_OnClick(object sender, RoutedEventArgs e)
{
RaiseFilterEvent("True");
}
private void FalseButton_OnClick(object sender, RoutedEventArgs e)
{
RaiseFilterEvent("False");
}
// Create a custom routed event by first registering a RoutedEventID
// This event uses the bubbling routing strategy
public static readonly RoutedEvent FilterEvent = EventManager.RegisterRoutedEvent(
"Filter", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(GridViewColumnHeaderSearchableCheckBox));
// Provide CLR accessors for the event
public event RoutedEventHandler Filter
{
add => AddHandler(FilterEvent, value);
remove => RemoveHandler(FilterEvent, value);
}
// This method raises the Tap event
private void RaiseFilterEvent(string filter)
{
var newEventArgs = new RoutedEventArgs(FilterEvent, filter);
RaiseEvent(newEventArgs);
OnPropertyChanged("ToggleIconGeometry");
}
}
}

View File

@@ -0,0 +1,47 @@
<UserControl x:Class="DTS.Common.Controls.GridViewColumnHeaderSelectable"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:DTS.Common.Controls"
xmlns:strings="clr-namespace:DTS.Common.Strings"
mc:Ignorable="d"
x:Name="dtsGridViewColumnHeader"
d:DesignHeight="20" d:DesignWidth="200">
<UserControl.Resources>
<ResourceDictionary>
<Geometry x:Key="DownArrowIconGeometry">F0 M 0,0L 10,0L 5,7L 0,0 Z</Geometry>
<local:BoolToInvertedBoolConverter x:Key="BoolToInvertedBoolConverter"/>
<Style TargetType="Button" BasedOn="{StaticResource PageContentButtonDark}" />
</ResourceDictionary>
</UserControl.Resources>
<Grid DataContext="{Binding ElementName=dtsGridViewColumnHeader}" x:Name="mainGrid" Height="20" MaxHeight="20">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Border BorderThickness="0" Grid.Column="0" PreviewMouseLeftButtonDown="PreviewLeftButtonUp">
<TextBlock Text="{Binding Path=HeaderTitle, FallbackValue='I Should Not Be Here'}" HorizontalAlignment="Left" Margin="5,0,5,0" />
</Border>
<!--<Button Grid.Column="0" Content="{Binding Path=HeaderTitle, FallbackValue='I should not be here'}" HorizontalAlignment="Left" Margin="5,0,5,0" Click="ButtonClick" /> -->
<ToggleButton Grid.Column="1" x:Name="TogglePopupButton" Width="20" Height="20" MaxWidth="20" MaxHeight="20" IsEnabled="{Binding ElementName=ToggledPopup, Path=IsOpen, Converter={StaticResource BoolToInvertedBoolConverter}}"
IsChecked="{Binding ToggleButtonIsChecked, Mode=TwoWay, FallbackValue=True}">
<Path x:Name="BtnArrow" VerticalAlignment="Center" HorizontalAlignment="Center" Width="8" Fill="#FF527DB5" Stretch="Uniform"
Data="{Binding ToggleIconGeometry, FallbackValue={StaticResource DownArrowIconGeometry}}"/>
</ToggleButton>
<Popup Grid.Column="0" Grid.ColumnSpan="2" x:Name="ToggledPopup" HorizontalAlignment="Stretch" Placement="Bottom" VerticalAlignment="Stretch" StaysOpen="False" IsOpen="{Binding ToggleButtonIsChecked, Mode=TwoWay}" >
<Border Background="#FFEEEEEE" BorderBrush="#FF888888" BorderThickness="1">
<Grid HorizontalAlignment="Stretch" Margin="3">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Button Grid.Column="0" Padding="10 5" Content="{x:Static strings:Strings.SelectAll}" Click="SelectAllButton_OnClick" />
<Button Grid.Column="1" Padding="10 5" Content="{x:Static strings:Strings.ClearAll}" Click="ClearAllButton_OnClick"/>
</Grid>
</Border>
</Popup>
</Grid>
<!--</GridViewColumnHeader>-->
</UserControl>

View File

@@ -0,0 +1,207 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Media;
using DTS.Common.Base;
using DTS.Common.Events;
using DTS.Common.Utilities.Logging;
using Prism.Events;
using Prism.Ioc;
namespace DTS.Common.Controls
{
/// <inheritdoc cref="IBasePropertyChanged" />
/// <summary>
/// Interaction logic for GridViewColumnHeaderSelectable.xaml
/// </summary>
public partial class GridViewColumnHeaderSelectable : UserControl, IBasePropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public bool SetProperty<T>(ref T storage, T value, string propertyName = null)
{
if (Equals(storage, value)) return false;
storage = value;
OnPropertyChanged(propertyName);
return true;
}
public void OnPropertyChanged(string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public GridViewColumnHeaderSelectable()
{
InitializeComponent();
var eventAggregator = ContainerLocator.Container.Resolve<IEventAggregator>();
eventAggregator.GetEvent<ListViewStatusEvent>().Subscribe(OnListviewStatusEvent, ThreadOption.UIThread);
}
public override string ToString()
{
return HeaderTitle;
}
private void OnListviewStatusEvent(ListViewStatusArg arg)
{
if (arg.Status != ListViewStatusArg.ListViewStatus.Unloaded)
{
return;
}
if (arg.Id == ListviewId)
{
}
}
public string ListviewId
{
get => (string)GetValue(ListviewIdProperty);
set => SetValue(ListviewIdProperty, value);
}
public static readonly DependencyProperty ListviewIdProperty =
DependencyProperty.Register(
"ListviewId",
typeof(string),
typeof(GridViewColumnHeaderSelectable), new PropertyMetadata(""));
public string HeaderTitle
{
get => (string)GetValue(HeaderTitleProperty);
set => SetValue(HeaderTitleProperty, value);
}
// Using a DependencyProperty enables animation, styling, binding, etc.
public static readonly DependencyProperty HeaderTitleProperty =
DependencyProperty.Register(
"HeaderTitle", // The name of the DependencyProperty
typeof(string), // The type of the DependencyProperty
typeof(GridViewColumnHeaderSelectable), // The type of the owner of the DependencyProperty
new PropertyMetadata( // OnHeaderTitleChanged will be called when HeaderTitle changes
"Awesome", // The default value of the DependencyProperty
OnHeaderTitleChanged
)
);
private static void OnHeaderTitleChanged(
DependencyObject d,
DependencyPropertyChangedEventArgs e
)
{
if (d is GridViewColumnHeaderSelectable instance)
{
instance.HeaderTitle = (string)e.NewValue;
}
}
private bool _toggleButtonIsChecked = false;
/// <summary>
/// holds whether the toggle button is checked or not and controls whether the popup is open or not
/// </summary>
public bool ToggleButtonIsChecked
{
get => _toggleButtonIsChecked;
set
{
if (value == _toggleButtonIsChecked) return;
_toggleButtonIsChecked = value;
OnPropertyChanged("ToggleButtonIsChecked");
OnPropertyChanged("ToggleIconGeometry");
RaiseOpenChangedEvent(value);
}
}
public static readonly DependencyProperty HeaderIsCheckedProperty =
DependencyProperty.Register(
"ToggleButtonIsChecked",
typeof(bool),
typeof(GridViewColumnHeaderSelectable),
new PropertyMetadata(
false,
OnHeaderIsOpenChanged
)
);
private static void OnHeaderIsOpenChanged(
DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
if (!(d is GridViewColumnHeaderSelectable instance)) return;
instance.ToggleButtonIsChecked = (bool)e.NewValue;
var isOpen = instance.ToggleButtonIsChecked;
instance.RaiseOpenChangedEvent(isOpen);
}
// Create a custom routed event by first registering a RoutedEventID
// This event uses the bubbling routing strategy
public static readonly RoutedEvent OpenChangedEvent = EventManager.RegisterRoutedEvent(
"OpenChanged", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(GridViewColumnHeaderSelectable));
// Provide CLR accessors for the event
public event RoutedEventHandler OpenChanged
{
add => AddHandler(OpenChangedEvent, value);
remove => RemoveHandler(OpenChangedEvent, value);
}
// This method raises the Tap event
private void RaiseOpenChangedEvent(bool isOpen)
{
var newEventArgs = new RoutedEventArgs(OpenChangedEvent, isOpen);
RaiseEvent(newEventArgs);
}
public static readonly RoutedEvent ClickEvent = EventManager.RegisterRoutedEvent(
"ClickHandler", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(GridViewColumnHeaderSelectable));
public event RoutedEventHandler ClickHandler
{
add => AddHandler(ClickEvent, value);
remove => RemoveHandler(ClickEvent, value);
}
private void RaiseClickEvent(object tag)
{
var newEventArgs = new RoutedEventArgs(ClickEvent, tag);
RaiseEvent(newEventArgs);
}
private void PreviewLeftButtonUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
RaiseClickEvent(Tag);
}
private void SelectAllButton_OnClick(object sender, RoutedEventArgs e)
{
RaiseSelectAllEvent(true);
}
private void ClearAllButton_OnClick(object sender, RoutedEventArgs e)
{
RaiseSelectAllEvent(false);
}
// Create a custom routed event by first registering a RoutedEventID
// This event uses the bubbling routing strategy
public static readonly RoutedEvent SelectAllEvent = EventManager.RegisterRoutedEvent(
"SelectAll", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(GridViewColumnHeaderSelectable));
// Provide CLR accessors for the event
public event RoutedEventHandler SelectAll
{
add => AddHandler(SelectAllEvent, value);
remove => RemoveHandler(SelectAllEvent, value);
}
// This method raises the Tap event
private void RaiseSelectAllEvent(bool selectAll)
{
var newEventArgs = new RoutedEventArgs(SelectAllEvent, selectAll);
RaiseEvent(newEventArgs);
OnPropertyChanged("ToggleIconGeometry");
}
}
}

View File

@@ -0,0 +1,68 @@
<UserControl x:Class="DTS.Common.Controls.IPTextBox"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:DTS.Common.Controls"
mc:Ignorable="d"
d:DesignHeight="50" d:DesignWidth="300"
FocusManager.IsFocusScope="True">
<UserControl.Resources>
<Style x:Key="{x:Type TextBox}" TargetType="{x:Type TextBox}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TextBoxBase">
<Border BorderThickness="{TemplateBinding Border.BorderThickness}"
BorderBrush="{TemplateBinding Border.BorderBrush}"
Background="{TemplateBinding Panel.Background}"
Name="border"
SnapsToDevicePixels="True">
<ScrollViewer HorizontalScrollBarVisibility="Hidden"
VerticalScrollBarVisibility="Hidden"
Name="PART_ContentHost"
Focusable="False" />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Opacity" TargetName="border" Value="0.56" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="10" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="10" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="10" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBox x:Name="FirstSegment" Grid.Column="0" TextAlignment="Center" MaxLength="3" BorderThickness="1,1,0,1" VerticalContentAlignment="Center"
TextChanged="TextBoxBase_OnTextChanged" PreviewKeyDown="UIElement_OnPreviewKeyDown" DataObject.Pasting="DataObject_OnPasting" />
<TextBox Grid.Column="1" Text="." TextAlignment="Center" IsReadOnly="True" Focusable="False" BorderThickness="0,1,0,1" VerticalContentAlignment="Center"
IsReadOnlyCaretVisible="False"/>
<TextBox x:Name="SecondSegment" Grid.Column="2" TextAlignment="Center" MaxLength="3" BorderThickness="0,1,0,1" VerticalContentAlignment="Center"
TextChanged="TextBoxBase_OnTextChanged" PreviewKeyDown="UIElement_OnPreviewKeyDown" DataObject.Pasting="DataObject_OnPasting" />
<TextBox Grid.Column="3" Text="." TextAlignment="Center" IsReadOnly="True" Focusable="False" BorderThickness="0,1,0,1" VerticalContentAlignment="Center"
IsReadOnlyCaretVisible="False"/>
<TextBox x:Name="ThirdSegment" Grid.Column="4" TextAlignment="Center" MaxLength="3" BorderThickness="0,1,0,1" VerticalContentAlignment="Center"
TextChanged="TextBoxBase_OnTextChanged" PreviewKeyDown="UIElement_OnPreviewKeyDown" DataObject.Pasting="DataObject_OnPasting" />
<TextBox Grid.Column="5" Text="." TextAlignment="Center" IsReadOnly="True" Focusable="False" BorderThickness="0,1,0,1" VerticalContentAlignment="Center"
IsReadOnlyCaretVisible="False" />
<TextBox x:Name="LastSegment" Grid.Column="6" TextAlignment="Center" MaxLength="3" BorderThickness="0,1,1,1" VerticalContentAlignment="Center"
TextChanged="TextBoxBase_OnTextChanged" PreviewKeyDown="UIElement_OnPreviewKeyDown" DataObject.Pasting="DataObject_OnPasting" />
</Grid>
</UserControl>

View File

@@ -0,0 +1,243 @@
//https://stackoverflow.com/questions/35324285/how-to-create-masked-textbox-like-windows-ip-address-fields
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
namespace DTS.Common.Controls
{
/// <summary>
/// Interaction logic for IPTextBox.xaml
/// </summary>
public partial class IPTextBox : UserControl
{
private static readonly List<Key> DigitKeys = new List<Key> { Key.D0, Key.D1, Key.D2, Key.D3, Key.D4, Key.D5, Key.D6, Key.D7, Key.D8, Key.D9,
//FB 43400 added NumPad 0-9 to the valid digit keys beside the main keyboard 0-9
Key.NumPad0, Key.NumPad1, Key.NumPad2, Key.NumPad3, Key.NumPad4, Key.NumPad5, Key.NumPad6, Key.NumPad7, Key.NumPad8, Key.NumPad9 };
private static readonly List<Key> MoveForwardKeys = new List<Key> { Key.Right };
private static readonly List<Key> MoveBackwardKeys = new List<Key> { Key.Left };
private static readonly List<Key> OtherAllowedKeys = new List<Key> { Key.Tab, Key.Delete };
private readonly List<TextBox> _segments = new List<TextBox>();
private bool _suppressAddressUpdate = false;
public IPTextBox()
{
InitializeComponent();
_segments.Add(FirstSegment);
_segments.Add(SecondSegment);
_segments.Add(ThirdSegment);
_segments.Add(LastSegment);
}
//FB 43400 added Clear method to reset the segments
public void Clear()
{
FirstSegment.Text = string.Empty;
SecondSegment.Text = string.Empty;
ThirdSegment.Text = string.Empty;
LastSegment.Text = string.Empty;
}
public static readonly DependencyProperty AddressProperty = DependencyProperty.Register(
"Address", typeof(string), typeof(IPTextBox), new FrameworkPropertyMetadata(default(string), AddressChanged)
{
BindsTwoWayByDefault = true
});
private static void AddressChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
{
var ipTextBox = dependencyObject as IPTextBox;
var text = e.NewValue as string;
if (text != null && ipTextBox != null)
{
ipTextBox._suppressAddressUpdate = true;
var i = 0;
foreach (var segment in text.Split('.'))
{
ipTextBox._segments[i].Text = segment;
i++;
}
ipTextBox._suppressAddressUpdate = false;
}
}
public string Address
{
get { return (string)GetValue(AddressProperty); }
set { SetValue(AddressProperty, value); }
}
private void UIElement_OnPreviewKeyDown(object sender, KeyEventArgs e)
{
if (DigitKeys.Contains(e.Key))
{
e.Handled = ShouldCancelDigitKeyPress();
HandleDigitPress();
}
else if (MoveBackwardKeys.Contains(e.Key))
{
e.Handled = ShouldCancelBackwardKeyPress();
HandleBackwardKeyPress();
}
else if (MoveForwardKeys.Contains(e.Key))
{
e.Handled = ShouldCancelForwardKeyPress();
HandleForwardKeyPress();
}
else if (e.Key == Key.Back)
{
HandleBackspaceKeyPress();
}
//FB 43400 added Decimal beside OemPeriod to valid keys
else if (e.Key == Key.OemPeriod || e.Key == Key.Decimal)
{
e.Handled = true;
HandlePeriodKeyPress();
}
else
{
e.Handled = !AreOtherAllowedKeysPressed(e);
}
}
private bool AreOtherAllowedKeysPressed(KeyEventArgs e)
{
return e.Key == Key.C && ((e.KeyboardDevice.Modifiers & ModifierKeys.Control) != 0) ||
e.Key == Key.V && ((e.KeyboardDevice.Modifiers & ModifierKeys.Control) != 0) ||
e.Key == Key.A && ((e.KeyboardDevice.Modifiers & ModifierKeys.Control) != 0) ||
e.Key == Key.X && ((e.KeyboardDevice.Modifiers & ModifierKeys.Control) != 0) ||
OtherAllowedKeys.Contains(e.Key);
}
private void HandleDigitPress()
{
var currentTextBox = FocusManager.GetFocusedElement(this) as TextBox;
if (currentTextBox != null && currentTextBox.Text.Length == 3 &&
currentTextBox.CaretIndex == 3 && currentTextBox.SelectedText.Length == 0)
{
MoveFocusToNextSegment(currentTextBox);
}
}
private bool ShouldCancelDigitKeyPress()
{
var currentTextBox = FocusManager.GetFocusedElement(this) as TextBox;
return currentTextBox != null &&
currentTextBox.Text.Length == 3 &&
currentTextBox.CaretIndex == 3 &&
currentTextBox.SelectedText.Length == 0;
}
private void TextBoxBase_OnTextChanged(object sender, TextChangedEventArgs e)
{
if (!_suppressAddressUpdate)
{
Address = string.Format("{0}.{1}.{2}.{3}", FirstSegment.Text, SecondSegment.Text, ThirdSegment.Text, LastSegment.Text);
}
var currentTextBox = FocusManager.GetFocusedElement(this) as TextBox;
if (currentTextBox != null && currentTextBox.Text.Length == 3 && currentTextBox.CaretIndex == 3)
{
MoveFocusToNextSegment(currentTextBox);
}
}
private bool ShouldCancelBackwardKeyPress()
{
var currentTextBox = FocusManager.GetFocusedElement(this) as TextBox;
return currentTextBox != null && currentTextBox.CaretIndex == 0;
}
private void HandleBackspaceKeyPress()
{
var currentTextBox = FocusManager.GetFocusedElement(this) as TextBox;
if (currentTextBox != null && currentTextBox.CaretIndex == 0 && currentTextBox.SelectedText.Length == 0)
{
MoveFocusToPreviousSegment(currentTextBox);
}
}
private void HandleBackwardKeyPress()
{
var currentTextBox = FocusManager.GetFocusedElement(this) as TextBox;
if (currentTextBox != null && currentTextBox.CaretIndex == 0)
{
MoveFocusToPreviousSegment(currentTextBox);
}
}
private bool ShouldCancelForwardKeyPress()
{
var currentTextBox = FocusManager.GetFocusedElement(this) as TextBox;
return currentTextBox != null && currentTextBox.CaretIndex == 3;
}
private void HandleForwardKeyPress()
{
var currentTextBox = FocusManager.GetFocusedElement(this) as TextBox;
if (currentTextBox != null && currentTextBox.CaretIndex == currentTextBox.Text.Length)
{
MoveFocusToNextSegment(currentTextBox);
}
}
private void HandlePeriodKeyPress()
{
var currentTextBox = FocusManager.GetFocusedElement(this) as TextBox;
if (currentTextBox != null && currentTextBox.Text.Length > 0 && currentTextBox.CaretIndex == currentTextBox.Text.Length)
{
MoveFocusToNextSegment(currentTextBox);
}
}
private void MoveFocusToPreviousSegment(TextBox currentTextBox)
{
if (!ReferenceEquals(currentTextBox, FirstSegment))
{
var previousSegmentIndex = _segments.FindIndex(box => ReferenceEquals(box, currentTextBox)) - 1;
currentTextBox.SelectionLength = 0;
currentTextBox.MoveFocus(new TraversalRequest(FocusNavigationDirection.Previous));
_segments[previousSegmentIndex].CaretIndex = _segments[previousSegmentIndex].Text.Length;
}
}
private void MoveFocusToNextSegment(TextBox currentTextBox)
{
if (!ReferenceEquals(currentTextBox, LastSegment))
{
currentTextBox.SelectionLength = 0;
currentTextBox.MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
}
}
private void DataObject_OnPasting(object sender, DataObjectPastingEventArgs e)
{
var isText = e.SourceDataObject.GetDataPresent(DataFormats.UnicodeText, true);
if (!isText)
{
e.CancelCommand();
return;
}
var text = e.SourceDataObject.GetData(DataFormats.UnicodeText) as string;
int num;
if (!int.TryParse(text, out num))
{
e.CancelCommand();
}
}
}
}

View File

@@ -0,0 +1,149 @@
<Popup x:Class="DTS.Common.Controls.ISOPopup"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:DTS.Common.Controls"
xmlns:converters="clr-namespace:DTS.Common.Converters"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Popup.Resources>
<converters:BooleanOrMultiConverter x:Key="BooleanOrMultiConverter" />
</Popup.Resources>
<Popup.Style>
<Style TargetType="Popup">
<Setter Property="IsOpen" Value="False"/>
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding CodeType}" Value="ISO" />
<Condition Binding="{Binding ShowISOStringBuilder}" Value="True" />
<Condition Value="True">
<Condition.Binding>
<MultiBinding Converter="{StaticResource BooleanOrMultiConverter}">
<Binding ElementName="MainEditBox" Path="IsKeyboardFocused" />
<Binding ElementName="ISOPopup" Path="IsMouseOver" />
<Binding ElementName="ISOPopup" Path="IsKeyboardFocusWithin" />
<Binding ElementName="possibleChannels" Path="IsKeyboardFocusWithin"/>
</MultiBinding>
</Condition.Binding>
</Condition>
</MultiDataTrigger.Conditions>
<Setter Property="IsOpen" Value="True" />
</MultiDataTrigger>
</Style.Triggers>
</Style>
</Popup.Style>
<Border Background="#FFEEEEEE" BorderBrush="#FF888888" BorderThickness="1" >
<StackPanel Orientation="Horizontal">
<StackPanel>
<TextBlock Text="Test Object" Margin="5"/>
<TextBox HorizontalAlignment="Center" Height="23" TextWrapping="Wrap" VerticalContentAlignment="Center" Width="20" Text="{Binding TestObject, Mode=TwoWay}" Name="txtTO" MaxLength="1" GotKeyboardFocus="ISOPart_OnGotKeyboardFocus" GotMouseCapture="ISOPart_OnGotMouseCapture" PreviewKeyUp="ISOPart_OnPreviewKeyUp"
IsReadOnly="True" Background="Transparent" BorderBrush="Transparent"
AutomationProperties.AutomationId="CCB_ISOPopupTestObjectTextBox">
<TextBox.InputBindings>
<KeyBinding Key="Enter"
Command="{local:UpdatePropertySourceWhenEnterPressed}"
CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor,AncestorType={x:Type TextBox}}}" />
</TextBox.InputBindings>
</TextBox>
</StackPanel>
<StackPanel>
<TextBlock Text="Position" Margin="5"/>
<TextBox HorizontalAlignment="Center" Height="23" TextWrapping="Wrap" VerticalContentAlignment="Center" Width="20" Text="{Binding Position}" Name="txtPosition" MaxLength="1" GotKeyboardFocus="ISOPart_OnGotKeyboardFocus" GotMouseCapture="ISOPart_OnGotMouseCapture" PreviewKeyUp="ISOPart_OnPreviewKeyUp"
IsReadOnly="True" Background="Transparent" BorderBrush="Transparent"
AutomationProperties.AutomationId="CCB_ISOPopupPositionTextBox">
<TextBox.InputBindings>
<KeyBinding Key="Enter"
Command="{local:UpdatePropertySourceWhenEnterPressed}"
CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor,AncestorType={x:Type TextBox}}}" />
</TextBox.InputBindings>
</TextBox>
</StackPanel>
<StackPanel>
<TextBlock Text="Main Location" Margin="5"/>
<TextBox HorizontalAlignment="Center" Height="23" TextWrapping="Wrap" VerticalContentAlignment="Center" Width="40" Text="{Binding MainLocation}" Name="txtMainLoc" MaxLength="4" GotKeyboardFocus="ISOPart_OnGotKeyboardFocus" GotMouseCapture="ISOPart_OnGotMouseCapture" PreviewKeyUp="ISOPart_OnPreviewKeyUp"
IsReadOnly="True" Background="Transparent" BorderBrush="Transparent"
AutomationProperties.AutomationId="CCB_ISOPopupMainLocationTextBox">
<TextBox.InputBindings>
<KeyBinding Key="Enter"
Command="{local:UpdatePropertySourceWhenEnterPressed}"
CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor,AncestorType={x:Type TextBox}}}" />
</TextBox.InputBindings>
</TextBox>
</StackPanel>
<StackPanel>
<TextBlock Text="Fine Loc1" Margin="5"/>
<TextBox HorizontalAlignment="Center" Height="23" TextWrapping="Wrap" VerticalContentAlignment="Center" Width="30" Text="{Binding FineLocation1}" Name="txtFL1" MaxLength="2" GotKeyboardFocus="ISOPart_OnGotKeyboardFocus" GotMouseCapture="ISOPart_OnGotMouseCapture" PreviewKeyUp="ISOPart_OnPreviewKeyUp"
IsReadOnly="True" Background="Transparent" BorderBrush="Transparent"
AutomationProperties.AutomationId="CCB_ISOPopupFineLocation1TextBox">
<TextBox.InputBindings>
<KeyBinding Key="Enter"
Command="{local:UpdatePropertySourceWhenEnterPressed}"
CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor,AncestorType={x:Type TextBox}}}" />
</TextBox.InputBindings>
</TextBox>
</StackPanel>
<StackPanel>
<TextBlock Text="Fine Loc2" Margin="5"/>
<TextBox HorizontalAlignment="Center" Height="23" TextWrapping="Wrap" VerticalContentAlignment="Center" Width="30" Text="{Binding FineLocation2}" Name="txtFL2" MaxLength="2" GotKeyboardFocus="ISOPart_OnGotKeyboardFocus" GotMouseCapture="ISOPart_OnGotMouseCapture" PreviewKeyUp="ISOPart_OnPreviewKeyUp"
IsReadOnly="True" Background="Transparent" BorderBrush="Transparent"
AutomationProperties.AutomationId="CCB_ISOPopupFineLocation2TextBox">
<TextBox.InputBindings>
<KeyBinding Key="Enter"
Command="{local:UpdatePropertySourceWhenEnterPressed}"
CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor,AncestorType={x:Type TextBox}}}" />
</TextBox.InputBindings>
</TextBox>
</StackPanel>
<StackPanel>
<TextBlock Text="Fine Loc3" Margin="5"/>
<TextBox HorizontalAlignment="Center" Height="23" TextWrapping="Wrap" VerticalContentAlignment="Center" Width="30" Text="{Binding FineLocation3}" Name="txtFL3" MaxLength="2" GotKeyboardFocus="ISOPart_OnGotKeyboardFocus" GotMouseCapture="ISOPart_OnGotMouseCapture" PreviewKeyUp="ISOPart_OnPreviewKeyUp"
IsReadOnly="True" Background="Transparent" BorderBrush="Transparent"
AutomationProperties.AutomationId="CCB_ISOPopupFineLocation3TextBox">
<TextBox.InputBindings>
<KeyBinding Key="Enter"
Command="{local:UpdatePropertySourceWhenEnterPressed}"
CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor,AncestorType={x:Type TextBox}}}" />
</TextBox.InputBindings>
</TextBox>
</StackPanel>
<StackPanel>
<TextBlock Text="Dimension" Margin="5"/>
<TextBox HorizontalAlignment="Center" Height="23" TextWrapping="Wrap" VerticalContentAlignment="Center" Width="30" Text="{Binding Dimension}" Name="txtDim" MaxLength="2" GotKeyboardFocus="ISOPart_OnGotKeyboardFocus" GotMouseCapture="ISOPart_OnGotMouseCapture" PreviewKeyUp="ISOPart_OnPreviewKeyUp"
IsReadOnly="True" Background="Transparent" BorderBrush="Transparent"
AutomationProperties.AutomationId="CCB_ISOPopupDimensionTextBox">
<TextBox.InputBindings>
<KeyBinding Key="Enter"
Command="{local:UpdatePropertySourceWhenEnterPressed}"
CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor,AncestorType={x:Type TextBox}}}" />
</TextBox.InputBindings>
</TextBox>
</StackPanel>
<StackPanel>
<TextBlock Text="Direction" Margin="5"/>
<TextBox HorizontalAlignment="Center" Height="23" TextWrapping="Wrap" VerticalContentAlignment="Center" Width="20" Text="{Binding Direction}" Name="txtDir" MaxLength="1" GotKeyboardFocus="ISOPart_OnGotKeyboardFocus" GotMouseCapture="ISOPart_OnGotMouseCapture" PreviewKeyUp="ISOPart_OnPreviewKeyUp"
IsReadOnly="True" Background="Transparent" BorderBrush="Transparent"
AutomationProperties.AutomationId="CCB_ISOPopupDirectionTextBox">
<TextBox.InputBindings>
<KeyBinding Key="Enter"
Command="{local:UpdatePropertySourceWhenEnterPressed}"
CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor,AncestorType={x:Type TextBox}}}" />
</TextBox.InputBindings>
</TextBox>
</StackPanel>
<StackPanel>
<TextBlock Text="Filter Class" Margin="5"/>
<TextBox HorizontalAlignment="Center" Height="23" TextWrapping="Wrap" VerticalContentAlignment="Center" Width="20" Text="{Binding FilterClass}" Name="txtFilterClass" MaxLength="1" GotKeyboardFocus="ISOPart_OnGotKeyboardFocus" GotMouseCapture="ISOPart_OnGotMouseCapture" PreviewKeyUp="ISOPart_OnPreviewKeyUp"
IsReadOnly="True" Background="Transparent" BorderBrush="Transparent"
AutomationProperties.AutomationId="CCB_ISOPopupFilterClassTextBox">
<TextBox.InputBindings>
<KeyBinding Key="Enter"
Command="{local:UpdatePropertySourceWhenEnterPressed}"
CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor,AncestorType={x:Type TextBox}}}" />
</TextBox.InputBindings>
</TextBox>
</StackPanel>
</StackPanel>
</Border>
</Popup>

View File

@@ -0,0 +1,55 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace DTS.Common.Controls
{
/// <summary>
/// Interaction logic for ISOPopup.xaml
/// </summary>
public partial class ISOPopup : Popup
{
public ISOPopup()
{
InitializeComponent();
}
private void ISOPart_OnPreviewKeyUp(object sender, KeyEventArgs e)
{
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;
e.Handled = !(e.Key == Key.OemQuestion || isAlpha || isNumPadNumeric || isNumeric || isControl);
}
private void ISOPart_OnGotMouseCapture(object sender, MouseEventArgs e)
{
if (sender is TextBox tb)
{
tb.SelectAll();
}
}
private void ISOPart_OnGotKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
{
if (sender is TextBox tb)
{
tb.SelectAll();
}
}
}
}

View File

@@ -0,0 +1,76 @@
<Popup x:Class="DTS.Common.Controls.LookupPopup"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:DTS.Common.Controls"
xmlns:strings="clr-namespace:DTS.Common.Strings"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
Opened="LookupPopup_OnOpenedClosed"
Closed="LookupPopup_OnOpenedClosed"
x:Name="lookupPopup">
<Popup.Resources>
<!--<local:BooleanOrMultiConverter x:Key="BooleanOrMultiConverter" />-->
</Popup.Resources>
<!--<Popup.Style>
<Style TargetType="Popup">
<Setter Property="IsOpen" Value="False"/>
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding ShowChannelCodeLookupHelper}" Value="True" />
<Condition Value="True">
<Condition.Binding>
<MultiBinding Converter="{StaticResource BooleanOrMultiConverter}">
<Binding ElementName="MainEditBox" Path="IsKeyboardFocused" />
<Binding ElementName="ISOPopup" Path="IsMouseOver" />
<Binding ElementName="ISOPopup" Path="IsKeyboardFocusWithin" />
<Binding ElementName="LookupPopup" Path="IsKeyboardFocusWithin"/>
</MultiBinding>
</Condition.Binding>
</Condition>
</MultiDataTrigger.Conditions>
<Setter Property="IsOpen" Value="True" />
</MultiDataTrigger>
</Style.Triggers>
</Style>
</Popup.Style>-->
<Border Background="#FFEEEEEE" BorderBrush="#FF888888" BorderThickness="1" DataContext="{Binding ElementName=lookupPopup}">
<StackPanel>
<DataGrid x:Name="possibleChannels"
ItemsSource="{Binding PossibleChannels}"
VirtualizingPanel.IsVirtualizing="True" EnableRowVirtualization="True"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
SelectionMode="Single"
MouseDoubleClick="PossibleChannels_OnMouseDoubleClick"
MaxHeight="350"
AutomationProperties.AutomationId="CCB_LookupPopupPossibleChannelsDataGrid">
<DataGrid.Style>
<Style TargetType="{x:Type DataGrid}" BasedOn="{StaticResource {x:Type DataGrid}}">
<Setter Property="Visibility" Value="Visible" />
<Style.Triggers>
<DataTrigger Binding="{Binding PossibleChannels.Count}" Value="0">
<Setter Property="Visibility" Value="Collapsed" />
</DataTrigger>
</Style.Triggers>
</Style>
</DataGrid.Style>
</DataGrid>
<TextBlock Text="{x:Static strings:Strings.NoChannelCodes}" Padding="5"
AutomationProperties.AutomationId="CCB_LookupPopupNoChannelsLabel">
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}" BasedOn="{StaticResource {x:Type TextBlock}}">
<Setter Property="Visibility" Value="Collapsed" />
<Style.Triggers>
<DataTrigger Binding="{Binding PossibleChannels.Count}" Value="0">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</StackPanel>
</Border>
</Popup>

View File

@@ -0,0 +1,57 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using DTS.Common.Interface.Channels.ChannelCodes;
namespace DTS.Common.Controls
{
/// <summary>
/// Interaction logic for LookupPopup.xaml
/// </summary>
public partial class LookupPopup : Popup
{
public IEnumerable<IChannelCode> AllChannelCodes { get; private set; }
public LookupPopup()
{
InitializeComponent();
}
public delegate void ChannelCodeSelectedEventHandler(object sender, string code, string name);
public event ChannelCodeSelectedEventHandler ChannelCodeSelected;
private void LookupPopup_OnOpenedClosed(object sender, EventArgs e)
{
//OnPropertyChanged(new PropertyChangedEventArgs("PossibleChannels"));
}
public static readonly DependencyProperty PossibleChannelsProperty =
DependencyProperty.Register(
"PossibleChannels",
typeof(IList),
typeof(LookupPopup),
new PropertyMetadata(Enumerable.Repeat(new { Code = "", Name = "" }, 0).ToList())
);
public IList PossibleChannels
{
get => (IList)GetValue(PossibleChannelsProperty);
set => SetValue(PossibleChannelsProperty, value);
}
private void PossibleChannels_OnMouseDoubleClick(object sender, MouseButtonEventArgs e)
{
if (sender == null) return;
if (((DataGrid)sender).SelectedIndex < 0) return;
var item = PossibleChannels[((DataGrid)sender).SelectedIndex];
e.Handled = false;
ChannelCodeSelected?.Invoke(this, item.GetType().GetProperty("Code")?.GetValue(item, null).ToString(), item.GetType().GetProperty("Name")?.GetValue(item, null).ToString());
}
}
}

View File

@@ -0,0 +1,27 @@
<UserControl x:Class="DTS.Common.Controls.RoundedBox"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid>
<Grid Margin="-6,-5,-12,-13">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="20"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="27"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="20"/>
<RowDefinition Height="*"/>
<RowDefinition Height="27"/>
</Grid.RowDefinitions>
<Image Height="20" Width="20" Source="../Images/shadow_tl.png" Stretch="Fill"/>
<Image Height="20" Grid.Row="0" Grid.Column="1" Source="../Images/shadow_t.png" Stretch="Fill"/>
<Image Height="20" Width="27" Grid.Row="0" Grid.Column="2" Source="../Images/shadow_tr.png" Stretch="Fill"/>
<Image Width="27" Grid.Column="2" Grid.Row="1" Source="../Images/shadow_r.png" Stretch="Fill"/>
<Image Width="20" Grid.Row="1" Grid.Column="0" Source="../Images/shadow_l.png" Stretch="Fill"/>
<Image Height="27" Grid.Column="1" Grid.Row="2" Source="../Images/shadow_b.png" Stretch="Fill" />
<Image Height="27" Width="20" Grid.Row="2" Grid.Column="0" Source="../Images/shadow_bl.png" Stretch="Fill"/>
<Image Height="27" Width="27" Grid.Column="2" Grid.Row="2" Source="../Images/shadow_br.png" Stretch="Fill"/>
</Grid>
<Border Background="#99FFFFFF" Opacity="0.8" CornerRadius="12,12,12,12" VerticalAlignment="Stretch" />
</Grid>
</UserControl>

View File

@@ -0,0 +1,20 @@
using System.Windows.Controls;
namespace DTS.Common.Controls
{
/// <summary>
/// Interaction logic for RoundedBox.xaml
/// </summary>
public partial class RoundedBox : UserControl
{
public RoundedBox()
{
InitializeComponent();
}
public void Connect(int connectionId, object target)
{
//throw new System.NotImplementedException();
}
}
}

View File

@@ -0,0 +1,23 @@
<UserControl x:Class="DTS.Common.Controls.TestIdControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d" d:DesignWidth="800" x:Name="TestIdInfo" d:DesignHeight="703">
<UserControl.Resources>
<Style TargetType="TextBox" BasedOn="{StaticResource PageContentTextBoxStyle}" />
<Style TargetType="TextBlock" BasedOn="{StaticResource PageContentTextStyle}" />
<Style TargetType="CheckBox" BasedOn="{StaticResource PageContentCheckBoxStyle}" />
<Style TargetType="PasswordBox" BasedOn="{StaticResource PageContentPasswordBoxStyle}" />
<Style TargetType="Button" BasedOn="{StaticResource PageContentButton}" />
</UserControl.Resources>
<StackPanel Orientation="Horizontal" AutomationProperties.AutomationId="StackPanelTestIdBuilder" DataContext="{Binding ElementName=TestIdInfo}">
<ComboBox ItemsSource="{Binding AllTestIdPrefixSuffixValues}" Width="200" SelectedItem="{Binding SelectedTestIdPrefixValueItem}"
AutomationProperties.AutomationId="TestIdPrefixComboBox"/>
<TextBlock Text="{Binding TestSetupLabel, FallbackValue='Awesome Sauce'}" Visibility="{Binding TestSetupLabelVisibility, FallbackValue=Collapsed}" AutomationProperties.AutomationId="TestIdTextBox"/>
<TextBox Text="{Binding TestIdEditableText}" Width="150" Style="{StaticResource PageContentTextBoxStyleId}"
AutomationProperties.AutomationId="TestIdTextBox"/>
<ComboBox ItemsSource="{Binding AllTestIdPrefixSuffixValues}" Width="200" SelectedItem="{Binding SelectedTestIdSuffixValueItem}"
AutomationProperties.AutomationId="TestIdSuffixComboBox"/>
</StackPanel>
</UserControl>

View File

@@ -0,0 +1,154 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
namespace DTS.Common.Controls
{
//Remove this control after deleting the TTS module and migrating to new test setup wizard
/// <inheritdoc cref="UserControl" />
/// <summary>
/// Interaction logic for TestIdControl.xaml
/// </summary>
public partial class TestIdControl : UserControl, INotifyPropertyChanged
{
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
protected bool SetProperty<T>(ref T storage, T value, string propertyName = null)
{
if (Equals(storage, value)) return false;
storage = value;
OnPropertyChanged(propertyName);
return true;
}
protected void OnPropertyChanged(string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
private static readonly TestIdPreFixSuffixHelper TEST_ID_SUFFIX_VALUE_NONE_ITEM =
new TestIdPreFixSuffixHelper(TestIdFixedPrefixSuffixValues.None);
private static readonly TestIdPreFixSuffixHelper TEST_ID_SUFFIX_VALUE_TIME_STAMP_ITEM =
new TestIdPreFixSuffixHelper(TestIdFixedPrefixSuffixValues.TimeStamp);
private static readonly TestIdPreFixSuffixHelper TEST_ID_SUFFIX_VALUE_TEST_SETUP_ITEM =
new TestIdPreFixSuffixHelper(TestIdFixedPrefixSuffixValues.TestSetupName);
public TestIdControl()
{
InitializeComponent();
}
private string _testSetupLabel = string.Empty;
public string TestSetupLabel
{
get => _testSetupLabel;
set
{
_testSetupLabel = value;
OnPropertyChanged("TestSetupLabel");
OnPropertyChanged("TestSetupLabelVisibility");
}
}
public Visibility TestSetupLabelVisibility => string.IsNullOrEmpty(TestSetupLabel)
? Visibility.Collapsed
: Visibility.Visible;
public string TestIdEditableText { get; set; } = string.Empty;
public void PopulateAllTestIdPrefixSuffixValues(string[] serializedValues)
{
_allTestIdPrefixSuffixValues = new List<TestIdPreFixSuffixHelper>
{
TEST_ID_SUFFIX_VALUE_NONE_ITEM,
TEST_ID_SUFFIX_VALUE_TIME_STAMP_ITEM,
TEST_ID_SUFFIX_VALUE_TEST_SETUP_ITEM
};
// Second, populate all values from the db
var dbList = new List<string>();
dbList.AddRange(serializedValues);
foreach (var s in dbList)
{
_allTestIdPrefixSuffixValues.Add(new TestIdPreFixSuffixHelper(s));
}
OnPropertyChanged("AllTestIDPrefixSuffixValues");
}
private List<TestIdPreFixSuffixHelper> _allTestIdPrefixSuffixValues;
public TestIdPreFixSuffixHelper[] AllTestIdPrefixSuffixValues => _allTestIdPrefixSuffixValues?.ToArray() ?? new TestIdPreFixSuffixHelper[0];
public TestIdPreFixSuffixHelper SelectedTestIdPrefixValueItem { get; set; } = TEST_ID_SUFFIX_VALUE_NONE_ITEM;
public TestIdPreFixSuffixHelper SelectedTestIdSuffixValueItem { get; set; } = TEST_ID_SUFFIX_VALUE_TIME_STAMP_ITEM;
public string TestName
{
get; set;
}
public string GetTestId()
{
var testIdList = new List<string>();
var prefix = GetRunTimeTestIdPrefixOrSuffix(SelectedTestIdPrefixValueItem);
if (prefix != string.Empty)
{
testIdList.Add(prefix);
}
if (!string.IsNullOrEmpty(TestSetupLabel))
{
testIdList.Add(TestSetupLabel);
}
if (TestIdEditableText != string.Empty)
{
testIdList.Add(TestIdEditableText);
}
var suffix = GetRunTimeTestIdPrefixOrSuffix(SelectedTestIdSuffixValueItem);
if (suffix != string.Empty)
{
testIdList.Add(suffix);
}
return string.Join("_", testIdList);
/*
CurrentTest.TestId = TestId;
CurrentTest.TestIdNode = CurrentTest.TestId;
_page.HeaderRibbon.tbCurrentTestIdString = TestId;*/
}
private string GetRunTimeTestIdPrefixOrSuffix(TestIdPreFixSuffixHelper prefixOrSuffix)
{
var rv = string.Empty;
if (prefixOrSuffix == null) { return rv; }
switch (prefixOrSuffix.TestIdPreFixSuffix.FixedValue)
{
case TestIdFixedPrefixSuffixValues.TestSetupName:
rv = TestName;
break;
case TestIdFixedPrefixSuffixValues.TimeStamp:
rv = GetTestIdTimestamp();
break;
case TestIdFixedPrefixSuffixValues.NotFixed:
rv = prefixOrSuffix.TestIdPreFixSuffix.ToString();
break;
case TestIdFixedPrefixSuffixValues.None:
default:
// do nothing
break;
}
return rv;
}
public string GetTestIdTimestamp()
{
return $"{DateTime.Now.Year:0000}_{DateTime.Now.Month:00}_{DateTime.Now.Day:00} {DateTime.Now.Hour:00}_{DateTime.Now.Minute:00}";
}
}
}

View File

@@ -0,0 +1,16 @@
<UserControl x:Class="DTS.Common.Controls.TestIDTextBox"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:DTS.Common.Controls"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
mc:Ignorable="d"
d:DesignHeight="50" d:DesignWidth="300"
FocusManager.IsFocusScope="True" x:Name="TestIdTextBoxControl">
<UserControl.Resources>
<sys:Int32 x:Key="TESTID_LENGTH_MAX">50</sys:Int32>
</UserControl.Resources>
<TextBox x:Name="tbTestId" Text="{Binding ElementName=TestIdTextBoxControl, Path=Text, UpdateSourceTrigger=PropertyChanged}" BorderThickness="1,1,1,1" VerticalContentAlignment="Center" MaxLength="{StaticResource TESTID_LENGTH_MAX}"
PreviewTextInput="tbTestId_PreviewTextInput"/>
</UserControl>

View File

@@ -0,0 +1,89 @@
//https://stackoverflow.com/questions/35324285/how-to-create-masked-textbox-like-windows-ip-address-fields
using System;
using System.Collections.Generic;
using System.IO;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
namespace DTS.Common.Controls
{
/// <summary>
/// Interaction logic for IPTextBox.xaml
/// </summary>
public partial class TestIDTextBox : UserControl
{
private static readonly List<Key> OtherAllowedKeys = new List<Key> { Key.Delete };
private bool _suppressAddressUpdate = false;
public TestIDTextBox()
{
InitializeComponent();
}
//FB 43400 added Clear method to reset the segments
public void Clear()
{
tbTestId.Text = string.Empty;
}
public static readonly DependencyProperty TextProperty = DependencyProperty.Register(
"Text", typeof(string), typeof(TestIDTextBox), new FrameworkPropertyMetadata(default(string), TextChanged)
{
BindsTwoWayByDefault = true
});
private static void TextChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
{
var tb = dependencyObject as TestIDTextBox;
var text = e.NewValue as string;
if (text != null && tb != null)
{
tb._suppressAddressUpdate = true;
if (IsValidText(text))
{
tb.tbTestId.Text = text;
}
else
{
tb.tbTestId.Text = (string)e.OldValue;
}
tb._suppressAddressUpdate = false;
}
}
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
private static bool IsValidText(string text)
{
var invalidChars = new List<char>();
invalidChars.AddRange(Path.GetInvalidFileNameChars());
invalidChars.AddRange(Path.GetInvalidPathChars());
invalidChars.Add('.');
var charArray = text.ToCharArray();
return !Array.Exists(charArray, x => invalidChars.Contains(x));
}
private void tbTestId_PreviewTextInput(object sender, TextCompositionEventArgs e)
{
e.Handled = !IsValidText(e.Text);
}
private void TextBoxBase_OnTextChanged(object sender, TextChangedEventArgs e)
{
if ( !(sender is TextBox tb)) { return; }
e.Handled = !IsValidText(tb.Text);
}
}
}

View File

@@ -0,0 +1,29 @@
<UserControl x:Class="DTS.Common.Controls.TestIDView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="703">
<UserControl.Resources>
<Style TargetType="TextBox" BasedOn="{StaticResource PageContentTextBoxStyle}" />
<Style TargetType="TextBlock" BasedOn="{StaticResource PageContentTextStyle}" />
<Style TargetType="CheckBox" BasedOn="{StaticResource PageContentCheckBoxStyle}" />
<Style TargetType="PasswordBox" BasedOn="{StaticResource PageContentPasswordBoxStyle}" />
<Style TargetType="Button" BasedOn="{StaticResource PageContentButton}" />
</UserControl.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<ComboBox Grid.Column="0" ItemsSource="{Binding AllTestIdPrefixSuffixValues}" Width="200" SelectedItem="{Binding SelectedTestIdPrefixValueItem}"
AutomationProperties.AutomationId="TestIdPrefixComboBox"/>
<TextBlock Grid.Column="1" Text="{Binding TestSetupLabel, FallbackValue='Awesome Sauce'}" MinWidth="100" MaxWidth="150" Padding="0,0,5,0" TextTrimming="CharacterEllipsis" Visibility="Collapsed" AutomationProperties.AutomationId="TestIdTextBox"/>
<TextBox Grid.Column="2" Text="{Binding TestIdEditableText}" Width="150" Style="{StaticResource PageContentTextBoxStyleId}"
AutomationProperties.AutomationId="TestIdTextBox"/>
<ComboBox Grid.Column="3" ItemsSource="{Binding AllTestIdPrefixSuffixValues}" Width="200" SelectedItem="{Binding SelectedTestIdSuffixValueItem}"
AutomationProperties.AutomationId="TestIdSuffixComboBox"/>
</Grid>
</UserControl>

View File

@@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace DTS.Common.Controls
{
/// <summary>
/// Interaction logic for TestIDView.xaml
/// </summary>
public partial class TestIDView : UserControl
{
public TestIDView()
{
InitializeComponent();
}
}
}

View File

@@ -0,0 +1,147 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
namespace DTS.Common.Controls
{
public class TestIDViewModel : INotifyPropertyChanged
{
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
protected bool SetProperty<T>(ref T storage, T value, string propertyName = null)
{
if (Equals(storage, value)) return false;
storage = value;
OnPropertyChanged(propertyName);
return true;
}
protected void OnPropertyChanged(string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
private static readonly TestIdPreFixSuffixHelper TEST_ID_SUFFIX_VALUE_NONE_ITEM =
new TestIdPreFixSuffixHelper(TestIdFixedPrefixSuffixValues.None);
private static readonly TestIdPreFixSuffixHelper TEST_ID_SUFFIX_VALUE_TIME_STAMP_ITEM =
new TestIdPreFixSuffixHelper(TestIdFixedPrefixSuffixValues.TimeStamp);
private static readonly TestIdPreFixSuffixHelper TEST_ID_SUFFIX_VALUE_TEST_SETUP_ITEM =
new TestIdPreFixSuffixHelper(TestIdFixedPrefixSuffixValues.TestSetupName);
private string _testSetupLabel = string.Empty;
public string TestSetupLabel
{
get => _testSetupLabel;
set
{
_testSetupLabel = value;
OnPropertyChanged("TestSetupLabel");
OnPropertyChanged("TestSetupLabelVisibility");
}
}
public Visibility TestSetupLabelVisibility => string.IsNullOrEmpty(TestSetupLabel)
? Visibility.Collapsed
: Visibility.Visible;
private string _testIdEditableText = string.Empty;
public string TestIdEditableText
{
get => _testIdEditableText;
set => SetProperty(ref _testIdEditableText, value, "TestIdEditableText");
}
public void PopulateAllTestIdPrefixSuffixValues(string[] serializedValues)
{
_allTestIdPrefixSuffixValues = new List<TestIdPreFixSuffixHelper>
{
TEST_ID_SUFFIX_VALUE_NONE_ITEM,
TEST_ID_SUFFIX_VALUE_TIME_STAMP_ITEM,
TEST_ID_SUFFIX_VALUE_TEST_SETUP_ITEM
};
// Second, populate all values from the db
var dbList = new List<string>();
dbList.AddRange(serializedValues);
foreach (var s in dbList)
{
_allTestIdPrefixSuffixValues.Add(new TestIdPreFixSuffixHelper(s));
}
OnPropertyChanged("AllTestIDPrefixSuffixValues");
}
private List<TestIdPreFixSuffixHelper> _allTestIdPrefixSuffixValues;
public TestIdPreFixSuffixHelper[] AllTestIdPrefixSuffixValues => _allTestIdPrefixSuffixValues?.ToArray() ?? new TestIdPreFixSuffixHelper[0];
public TestIdPreFixSuffixHelper SelectedTestIdPrefixValueItem { get; set; } = TEST_ID_SUFFIX_VALUE_NONE_ITEM;
public TestIdPreFixSuffixHelper SelectedTestIdSuffixValueItem { get; set; } = TEST_ID_SUFFIX_VALUE_TIME_STAMP_ITEM;
public string TestName
{
get; set;
}
public string GetTestId()
{
var testIdList = new List<string>();
var prefix = GetRunTimeTestIdPrefixOrSuffix(SelectedTestIdPrefixValueItem);
if (prefix != string.Empty)
{
testIdList.Add(prefix);
}
if (!string.IsNullOrEmpty(TestSetupLabel))
{
testIdList.Add(TestSetupLabel);
}
if (TestIdEditableText != string.Empty)
{
testIdList.Add(TestIdEditableText);
}
var suffix = GetRunTimeTestIdPrefixOrSuffix(SelectedTestIdSuffixValueItem);
if (suffix != string.Empty)
{
testIdList.Add(suffix);
}
return string.Join("_", testIdList);
}
private string GetRunTimeTestIdPrefixOrSuffix(TestIdPreFixSuffixHelper prefixOrSuffix)
{
var rv = string.Empty;
if (prefixOrSuffix == null) { return rv; }
switch (prefixOrSuffix.TestIdPreFixSuffix.FixedValue)
{
case TestIdFixedPrefixSuffixValues.TestSetupName:
rv = TestName;
break;
case TestIdFixedPrefixSuffixValues.TimeStamp:
rv = GetTestIdTimestamp();
break;
case TestIdFixedPrefixSuffixValues.NotFixed:
rv = prefixOrSuffix.TestIdPreFixSuffix.ToString();
break;
case TestIdFixedPrefixSuffixValues.None:
default:
// do nothing
break;
}
return rv;
}
public string GetTestIdTimestamp()
{
return $"{DateTime.Now.Year:0000}_{DateTime.Now.Month:00}_{DateTime.Now.Day:00} {DateTime.Now.Hour:00}_{DateTime.Now.Minute:00}";
}
}
}

View File

@@ -0,0 +1,55 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DTS.Common.Controls
{
public enum TestIdFixedPrefixSuffixValues
{
NotFixed = -1,
None = 0,
TimeStamp = 1,
TestSetupName = 2
}
public class TestIdPreFixSuffix
{
private readonly string _tempString;
public TestIdFixedPrefixSuffixValues FixedValue { get; } = TestIdFixedPrefixSuffixValues.NotFixed;
public TestIdPreFixSuffix(TestIdFixedPrefixSuffixValues fixedPrefixSuffix)
{
_tempString = "TESTID_PREFIX_SUFFIX_" + fixedPrefixSuffix;
FixedValue = fixedPrefixSuffix;
}
public TestIdPreFixSuffix(string dbPrefixSuffix) { _tempString = dbPrefixSuffix; }
public override string ToString()
{
return _tempString;
}
}
public class TestIdPreFixSuffixHelper : Base.BasePropertyChanged
{
public TestIdPreFixSuffix TestIdPreFixSuffix { get; }
public TestIdPreFixSuffixHelper(string testIdPreFixSuffix)
{
TestIdPreFixSuffix = new TestIdPreFixSuffix(testIdPreFixSuffix);
}
public TestIdPreFixSuffixHelper(TestIdFixedPrefixSuffixValues testIdPreFixSuffix)
{
TestIdPreFixSuffix = new TestIdPreFixSuffix(testIdPreFixSuffix);
}
public override string ToString()
{
return Strings.Strings.ResourceManager.GetString(TestIdPreFixSuffix.ToString()) ??
TestIdPreFixSuffix.ToString();
}
public override bool Equals(object obj)
{
if (!(obj is TestIdPreFixSuffixHelper testid) || testid.TestIdPreFixSuffix.FixedValue != TestIdPreFixSuffix.FixedValue)
return base.Equals(obj);
return testid.TestIdPreFixSuffix.FixedValue != TestIdFixedPrefixSuffixValues.NotFixed || string.Equals(testid.TestIdPreFixSuffix.ToString(), TestIdPreFixSuffix.ToString());
}
}
}

View File

@@ -0,0 +1,101 @@
<ResourceDictionary
x:Class="DTS.Common.Controls.checkbox"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" >
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/DTS.Common;component/Themes/CommonStyles.xaml" />
</ResourceDictionary.MergedDictionaries>
<!-- SimpleStyles: CheckBox -->
<Style x:Key="FlatCheckBoxStyle" TargetType="CheckBox">
<Setter Property="SnapsToDevicePixels" Value="true"/>
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="Foreground" Value="White" />
<Setter Property="FontSize" Value="14" />
<Setter Property="FontFamily" Value="Segoe UI" />
<Setter Property="Margin" Value="0,0,5,0" />
<Setter Property="FocusVisualStyle" Value="{x:Null}"/>
<Setter Property="Height" Value="20" />
<Setter Property="Width" Value="20" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="CheckBox">
<BulletDecorator Background="Transparent">
<BulletDecorator.Bullet>
<Border x:Name="Border"
Width="{TemplateBinding Width}"
Height="{TemplateBinding Height}"
HorizontalAlignment="{TemplateBinding HorizontalAlignment}"
CornerRadius="0"
Background="{StaticResource Brush_FlatControlWindowBackground}"
BorderThickness="{TemplateBinding BorderThickness}"
BorderBrush="{TemplateBinding BorderBrush}">
<!--<Rectangle x:Name="CheckMark" Height="14" Width="14" Fill="{StaticResource GlyphBrush}" />-->
<!--<Path
Width="15" Height="15"
x:Name="CheckMark"
SnapsToDevicePixels="False"
Stroke="{StaticResource GlyphBrush}"
StrokeThickness="2"
Data="M 0 0 L 15 15 M 0 15 L 15 0" />-->
<Path
Width="15" Height="15"
x:Name="CheckMark"
SnapsToDevicePixels="False"
Stroke="{StaticResource GlyphBrush}"
StrokeThickness="2"
Data="M 3 8 L 8 15 M 7 15 L 14 1" />
</Border>
</BulletDecorator.Bullet>
<ContentPresenter Margin="5,0,0,0"
VerticalAlignment="Center"
HorizontalAlignment="Left"
RecognizesAccessKey="True"/>
</BulletDecorator>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="false">
<Setter TargetName="CheckMark" Property="Visibility" Value="Collapsed"/>
</Trigger>
<Trigger Property="IsChecked" Value="{x:Null}">
<Setter TargetName="CheckMark" Property="Opacity" Value="0"/>
<Setter TargetName="CheckMark" Property="Visibility" Value="Hidden" />
</Trigger>
<Trigger Property="IsMouseOver" Value="true">
<Setter TargetName="Border" Property="Background" Value="{StaticResource Brush_FlatControlMouseOverBackground}" />
</Trigger>
<Trigger Property="IsPressed" Value="true">
<Setter TargetName="Border" Property="Background" Value="{StaticResource Brush_FlatControlWindowBackground}" />
<Setter TargetName="Border" Property="BorderBrush" Value="{StaticResource PressedBorderBrush}" />
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter TargetName="CheckMark" Property="Opacity" Value="0.3"/>
<Setter TargetName="Border" Property="Background" Value="{StaticResource Brush_FlatControlDisabledBackground}" />
<Setter TargetName="Border" Property="BorderBrush" Value="{StaticResource Brush_FlatControlBorder}" />
<Setter Property="Foreground" Value="{StaticResource Brush_FlatControlDisabledForeground}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="ToolTipService.ToolTip" Value="" />
<EventSetter Event="ToolTipOpening" Handler="ToolTipEventHandler" />
</Style>
<Style x:Key="{x:Type CheckBox}" TargetType="CheckBox" BasedOn="{StaticResource FlatCheckBoxStyle}" />
<Style x:Key="PageContentCheckBoxStyle" TargetType="CheckBox" BasedOn="{StaticResource FlatCheckBoxStyle}">
<Setter Property="HorizontalAlignment" Value="Left" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="Margin" Value="5,2,5,2" />
<Setter Property="BorderThickness" Value="0" />
<Setter Property="ToolTipService.ToolTip" Value="" />
<EventSetter Event="ToolTipOpening" Handler="ToolTipEventHandler" />
</Style>
<Style x:Key="PageContentCheckBoxErrorStyle" TargetType="CheckBox" BasedOn="{StaticResource PageContentCheckBoxStyle}">
<Setter Property="BorderThickness" Value="2" />
<Setter Property="BorderBrush" Value="Red" />
</Style>
</ResourceDictionary>

View File

@@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Controls;
using DTS.Common.Events;
using Prism.Ioc;
using Prism.Events;
namespace DTS.Common.Controls
{
public partial class checkbox
{
public void ToolTipEventHandler(object sender, System.Windows.Controls.ToolTipEventArgs e)
{
e.Handled = true;
var eventAggregator = ContainerLocator.Container.Resolve<IEventAggregator>();
eventAggregator.GetEvent<HelpTextEvent>().Publish(new HelpTextEventArg()
{ Sender = sender, E = e });
}
}
}

View File

@@ -0,0 +1,296 @@
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/DTS.Common;component/Themes/CommonStyles.xaml" />
</ResourceDictionary.MergedDictionaries>
<sys:Double x:Key="MaxDropDownWidth">500</sys:Double>
<!-- SimpleStyles: ComboBox -->
<ControlTemplate x:Key="ComboBoxToggleButton" TargetType="ToggleButton">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="20" />
</Grid.ColumnDefinitions>
<Border
x:Name="Border"
Grid.ColumnSpan="1"
Grid.Column="1"
Background="{StaticResource Brush_FlatControlWindowBackground}"
BorderBrush="{StaticResource NormalBorderBrush}"
BorderThickness="0" />
<Border
x:Name="Content_Text"
Grid.Column="0"
Margin="0,0,1,0"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch"
Background="{DynamicResource Brush_FlatControlWindowBackground}"
BorderBrush="{StaticResource DefaultedBorderBrush}"
BorderThickness="0" />
<Path
x:Name="Arrow"
Grid.Column="1"
Fill="{StaticResource GlyphBrush}"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Data="M 0 0 L 4 4 L 8 0 Z"/>
<Path
x:Name="Lock"
Grid.Column="0"
Data="M5.5,3.5730734 C5.152669,3.5730734 4.8814287,3.5981388 4.6862793,3.6482687 C4.4911294,3.6983995 4.3452148,3.780756 4.2485352,3.895339 C4.1518555,4.0099225 4.0918779,4.1612082 4.0686035,4.3491964 C4.0453286,4.5371847 4.0336914,4.7672467 4.0336914,5.039382 L4.0336914,5.8826437 L6.9663086,5.8826437 L6.9663086,5.039382 C6.9663086,4.7672467 6.9555664,4.5371847 6.934082,4.3491964 C6.9125977,4.1612082 6.8544106,4.0099225 6.7595215,3.895339 C6.6646318,3.780756 6.5187173,3.6983995 6.3217773,3.6482687 C6.1248369,3.5981388 5.8509111,3.5730734 5.5,3.5730734 z M5.5,2.461257 C6.0514321,2.461257 6.4945474,2.5266056 6.8293457,2.6573019 C7.1641436,2.7879992 7.4219561,2.9688253 7.6027832,3.1997824 C7.7836099,3.4307399 7.9035645,3.7037706 7.9626465,4.0188742 C8.0217285,4.3339787 8.0512695,4.6741476 8.0512695,5.039382 L8.0512695,5.9094992 C8.1515293,5.9094992 8.248209,5.9291935 8.3413086,5.9685812 C8.4344072,6.0079694 8.5158691,6.0607853 8.5856934,6.1270285 C8.6555176,6.1932721 8.7110186,6.2702579 8.7521973,6.3579855 C8.793375,6.4457135 8.8139648,6.5379171 8.8139648,6.6345968 L8.8139648,10.67903 C8.8139648,10.782871 8.795166,10.879551 8.7575684,10.969069 C8.7199707,11.058588 8.6671543,11.138259 8.5991211,11.208083 C8.5310869,11.277907 8.4523106,11.332514 8.362793,11.371902 C8.2732744,11.411289 8.1783848,11.430984 8.078125,11.430984 L2.9379883,11.430984 C2.834147,11.430984 2.7365723,11.411289 2.6452637,11.371902 C2.5539551,11.332514 2.4742837,11.277907 2.40625,11.208083 C2.3382161,11.138259 2.2845051,11.058588 2.2451172,10.969069 C2.205729,10.879551 2.1860352,10.782871 2.1860352,10.67903 L2.1860352,6.6345968 C2.1860352,6.5379171 2.2066243,6.4457135 2.2478027,6.3579855 C2.288981,6.2702579 2.3444824,6.1932721 2.4143066,6.1270285 C2.4841309,6.0607853 2.563802,6.0079694 2.6533203,5.9685812 C2.7428384,5.9291935 2.8377278,5.9094992 2.9379883,5.9094992 L2.9379883,5.039382 C2.9379883,4.6741476 2.9684243,4.3339787 3.0292969,4.0188742 C3.0901692,3.7037706 3.2128091,3.4307399 3.3972168,3.1997824 C3.581624,2.9688253 3.8412271,2.7879992 4.1760254,2.6573019 C4.5108232,2.5266056 4.9521484,2.461257 5.5,2.461257 z"
HorizontalAlignment="Right"
Height="8.97" Margin="5" Stretch="Fill"
VerticalAlignment="Center"
Width="6.628"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="ToggleButton.IsMouseOver" Value="true">
<Setter TargetName="Border" Property="Background" Value="{StaticResource Brush_FlatControlMouseOverBackground}" />
<Setter TargetName="Content_Text" Property="Background" Value="{StaticResource Brush_FlatControlMouseOverBackground}" />
</Trigger>
<Trigger Property="ToggleButton.IsChecked" Value="true">
<Setter TargetName="Border" Property="Background" Value="{StaticResource Brush_FlatControlMouseOverBackground}" />
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter TargetName="Lock" Property="Fill" Value="{StaticResource Brush_FlatControlDisabledForeground}"/>
<Setter TargetName="Border" Property="Background" Value="{StaticResource Brush_FlatControlDisabledBackground}" />
<Setter TargetName="Border" Property="BorderBrush" Value="{StaticResource Brush_FlatControlBorder}" />
<Setter Property="Foreground" Value="{StaticResource Brush_FlatControlDisabledForeground}"/>
<Setter TargetName="Content_Text" Property="Background" Value="{StaticResource Brush_FlatControlDisabledBackground}"/>
<Setter TargetName="Arrow" Property="Fill" Value="{StaticResource Brush_FlatControlDisabledForeground}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
<ControlTemplate x:Key="ComboBoxTextBox" TargetType="TextBox" >
<Border x:Name="PART_ContentHost" Focusable="False" Background="{TemplateBinding Background}" />
</ControlTemplate>
<Style x:Key="FlatComboBoxStyle" TargetType="ComboBox">
<Setter Property="SnapsToDevicePixels" Value="true"/>
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Auto"/>
<Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto"/>
<Setter Property="ScrollViewer.CanContentScroll" Value="true"/>
<Setter Property="FontSize" Value="16" />
<!--7873 Font size is smaller in Name field than in combo-box fields-->
<Setter Property="FontFamily" Value="Segoe UI" />
<Setter Property="MinWidth" Value="40"/>
<Setter Property="Height" Value="22" />
<Setter Property="VerticalAlignment" Value="Stretch" />
<Setter Property="Margin" Value="5,2,5,2"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ComboBox">
<Border BorderThickness="{TemplateBinding BorderThickness}" BorderBrush="{TemplateBinding BorderBrush}">
<Grid>
<ToggleButton Name="ToggleButton"
Template="{StaticResource ComboBoxToggleButton}"
Grid.Column="2"
Focusable="false"
IsChecked="{Binding Path=IsDropDownOpen,Mode=TwoWay,RelativeSource={RelativeSource TemplatedParent}}"
ClickMode="Press">
</ToggleButton>
<ContentPresenter Name="ContentSite"
IsHitTestVisible="False"
Content="{TemplateBinding SelectionBoxItem}"
ContentTemplate="{TemplateBinding SelectionBoxItemTemplate}"
ContentTemplateSelector="{TemplateBinding ItemTemplateSelector}"
Margin="3,0,23,0"
VerticalAlignment="Center"
TextBlock.Foreground="{TemplateBinding Foreground}"
TextBlock.FontSize="{TemplateBinding FontSize}"
HorizontalAlignment="Left" />
<TextBox x:Name="PART_EditableTextBox"
Style="{x:Null}"
Template="{StaticResource ComboBoxTextBox}"
FontSize="{TemplateBinding FontSize}"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Margin="3,0,23,0"
Focusable="True"
Background="Transparent"
Visibility="Hidden"
IsReadOnly="{TemplateBinding IsReadOnly}"/>
<Popup Name="Popup"
Placement="Bottom"
IsOpen="{TemplateBinding IsDropDownOpen}"
AllowsTransparency="True"
Focusable="False"
PopupAnimation="Slide">
<Grid Name="DropDown"
SnapsToDevicePixels="True"
TextBlock.FontSize="{TemplateBinding FontSize}"
MaxWidth="{TemplateBinding ActualWidth}"
MinWidth="{TemplateBinding ActualWidth}"
MaxHeight="{TemplateBinding MaxDropDownHeight}">
<Border x:Name="DropDownBorder"
Background="{StaticResource Brush_FlatControlWindowBackground}"
BorderThickness="1"
BorderBrush="{StaticResource SolidBorderBrush}"/>
<ScrollViewer Margin="4,6,4,6" SnapsToDevicePixels="True" Visibility="Visible">
<StackPanel IsItemsHost="True" KeyboardNavigation.DirectionalNavigation="Contained" />
</ScrollViewer>
</Grid>
</Popup>
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="HasItems" Value="false">
<Setter TargetName="DropDownBorder" Property="MinHeight" Value="95"/>
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Foreground" Value="{StaticResource Brush_FlatControlDisabledForeground}"/>
</Trigger>
<Trigger Property="IsGrouping" Value="true">
<Setter Property="ScrollViewer.CanContentScroll" Value="True"/>
</Trigger>
<Trigger SourceName="Popup" Property="Popup.AllowsTransparency" Value="true">
<Setter TargetName="DropDownBorder" Property="Margin" Value="0,0,0,0"/>
</Trigger>
<Trigger Property="IsEditable" Value="true">
<Setter Property="IsTabStop" Value="false"/>
<Setter TargetName="PART_EditableTextBox" Property="Visibility" Value="Visible"/>
<Setter TargetName="ContentSite" Property="Visibility" Value="Hidden"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="FlatComboBoxStyleWideDrop" TargetType="ComboBox">
<Setter Property="SnapsToDevicePixels" Value="true"/>
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Auto"/>
<Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto"/>
<Setter Property="ScrollViewer.CanContentScroll" Value="true"/>
<Setter Property="FontSize" Value="16" />
<!--7873 Font size is smaller in Name field than in combo-box fields-->
<Setter Property="FontFamily" Value="Segoe UI" />
<Setter Property="MinWidth" Value="40"/>
<Setter Property="Height" Value="22" />
<Setter Property="VerticalAlignment" Value="Stretch" />
<Setter Property="Margin" Value="5,2,5,2"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ComboBox">
<Border BorderThickness="{TemplateBinding BorderThickness}" BorderBrush="{TemplateBinding BorderBrush}">
<Grid>
<ToggleButton Name="ToggleButton"
Template="{StaticResource ComboBoxToggleButton}"
Grid.Column="2"
Focusable="false"
IsChecked="{Binding Path=IsDropDownOpen,Mode=TwoWay,RelativeSource={RelativeSource TemplatedParent}}"
ClickMode="Press">
</ToggleButton>
<ContentPresenter Name="ContentSite"
IsHitTestVisible="False"
Content="{TemplateBinding SelectionBoxItem}"
ContentTemplate="{TemplateBinding SelectionBoxItemTemplate}"
ContentTemplateSelector="{TemplateBinding ItemTemplateSelector}"
Margin="3,0,23,0"
VerticalAlignment="Center"
TextBlock.Foreground="{TemplateBinding Foreground}"
TextBlock.FontSize="{TemplateBinding FontSize}"
HorizontalAlignment="Left" />
<TextBox x:Name="PART_EditableTextBox"
Style="{x:Null}"
Template="{StaticResource ComboBoxTextBox}"
FontSize="{TemplateBinding FontSize}"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Margin="3,0,23,0"
Focusable="True"
Background="Transparent"
Visibility="Hidden"
IsReadOnly="{TemplateBinding IsReadOnly}"/>
<Popup Name="Popup"
Placement="Bottom"
IsOpen="{TemplateBinding IsDropDownOpen}"
AllowsTransparency="True"
Focusable="False"
PopupAnimation="Slide">
<Grid Name="DropDown"
SnapsToDevicePixels="True"
TextBlock.FontSize="{TemplateBinding FontSize}"
MaxWidth="{StaticResource MaxDropDownWidth}"
MinWidth="{TemplateBinding ActualWidth}"
MaxHeight="{TemplateBinding MaxDropDownHeight}">
<Border x:Name="DropDownBorder"
Background="{StaticResource Brush_FlatControlWindowBackground}"
BorderThickness="1"
BorderBrush="{StaticResource SolidBorderBrush}"/>
<ScrollViewer Margin="4,6,4,6" SnapsToDevicePixels="True" Visibility="Visible">
<StackPanel IsItemsHost="True" KeyboardNavigation.DirectionalNavigation="Contained" />
</ScrollViewer>
</Grid>
</Popup>
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="HasItems" Value="false">
<Setter TargetName="DropDownBorder" Property="MinHeight" Value="95"/>
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Foreground" Value="{StaticResource Brush_FlatControlDisabledForeground}"/>
</Trigger>
<Trigger Property="IsGrouping" Value="true">
<Setter Property="ScrollViewer.CanContentScroll" Value="True"/>
</Trigger>
<Trigger SourceName="Popup" Property="Popup.AllowsTransparency" Value="true">
<Setter TargetName="DropDownBorder" Property="Margin" Value="0,0,0,0"/>
</Trigger>
<Trigger Property="IsEditable" Value="true">
<Setter Property="IsTabStop" Value="false"/>
<Setter TargetName="PART_EditableTextBox" Property="Visibility" Value="Visible"/>
<Setter TargetName="ContentSite" Property="Visibility" Value="Hidden"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- SimpleStyles: ComboBoxItem -->
<Style x:Key="{x:Type ComboBoxItem}" TargetType="ComboBoxItem">
<Setter Property="SnapsToDevicePixels" Value="true"/>
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ComboBoxItem">
<Border
Name="Border"
Padding="0"
SnapsToDevicePixels="true">
<ContentPresenter />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsHighlighted" Value="true">
<Setter TargetName="Border" Property="Background" Value="{StaticResource Brush_FlatControlSelectedBackground}"/>
<Setter Property="Foreground" Value="{StaticResource Brush_FlatControlSelectedForeground}" />
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Foreground" Value="{StaticResource Brush_FlatControlForeground}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="{x:Type ComboBox}" TargetType="ComboBox" BasedOn="{StaticResource FlatComboBoxStyle}" />
<Style x:Key="PageContentComboBoxStyle" TargetType="ComboBox" BasedOn="{StaticResource FlatComboBoxStyle}" >
<Setter Property="Margin" Value="5,2,5,2" />
<Setter Property="Height" Value="22" />
</Style>
<Style x:Key="PageContentComboBoxErrorStyle" TargetType="ComboBox" BasedOn="{StaticResource PageContentComboBoxStyle}">
<Setter Property="BorderThickness" Value="2" />
<Setter Property="BorderBrush" Value="Red" />
</Style>
</ResourceDictionary>