/* * Copyright (c) 2022 LG Electronics Inc. * SPDX-License-Identifier: LicenseRef-LGE-Proprietary */ using Aurora.View; using AuroraUIComponent.UserControls.KeyValueBase; using System; using System.Windows; using System.Windows.Input; namespace AuroraUIComponent.UserControls { // 윈폼 정보 참조 // https://learn.microsoft.com/ko-kr/dotnet/desktop/winforms/controls/numericupdown-control-windows-forms?view=netframeworkdesktop-4.8 // 사용자가 클릭하여 값을 조정할 수 있는 텍스트 상자와 화살표 쌍의 조합처럼 보임 // 참고자료 // https://www.codeproject.com/Articles/509824/Creating-a-NumericUpDown-control-from-scratch /* There are three ways to detect if the user has finished editing the TextBox: 1. NumericUpDown loses focus 2. User uses keyboard arrows or the buttons on the side to increase/decrease Value 3. User presses the enter (return) key */ // 이 컨트롤은 크게 3가지로 구성됨 // 1. 컨트롤 레이블 // 2/3. TextBox + 스핀 컨트롤 (Repeat 버튼) // 기타. 유닛 레이블 /// /// Interaction logic for UCNumericUpDown.xaml /// public partial class UCNumericUpDown : UCKeyValueNumber { public static readonly DependencyProperty MaximumProperty = DependencyProperty.Register( nameof(Maximum), typeof(decimal), typeof(UCNumericUpDown), new PropertyMetadata( 100m, OnMaximumChanged, CoerceMaximumCallback) ); public decimal Maximum { get { return (decimal)GetValue(MaximumProperty); } set { SetValue(MaximumProperty, value); } } private static void OnMaximumChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var control = (UCNumericUpDown)d; var maxValue = (decimal)e.NewValue; // If maxValue steps over Minimum, shift it if (maxValue < control.Minimum) { control.Minimum = maxValue; } // desired 값이 경계 내에 존재하도록 강제하는 로직.. if (maxValue <= control.Value) { control.Value = maxValue; } } private static object CoerceMaximumCallback(DependencyObject d, object baseValue) { var maxValue = (decimal)baseValue; return maxValue; } public static readonly DependencyProperty MinimumProperty = DependencyProperty.Register( nameof(Minimum), typeof(decimal), typeof(UCNumericUpDown), new PropertyMetadata( 0m, OnMinimumChanged, CoerceMinimumCallback) ); public decimal Minimum { get { return (decimal)GetValue(MinimumProperty); } set { SetValue(MinimumProperty, value); } } private static void OnMinimumChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var control = (UCNumericUpDown)d; var minValue = (decimal)e.NewValue; // If minValue steps over Maximum, shift it if (minValue > control.Maximum) { control.Maximum = minValue; } if (minValue >= control.Value) { control.Value = minValue; } } private static object CoerceMinimumCallback(DependencyObject d, object baseValue) { var minValue = (decimal)baseValue; return minValue; } public static readonly DependencyProperty IsAutoSelectionActiveProperty = DependencyProperty.Register( nameof(IsAutoSelectionActive), typeof(Boolean), typeof(UCNumericUpDown), new PropertyMetadata(false)); public Boolean IsAutoSelectionActive { get { return (Boolean)GetValue(IsAutoSelectionActiveProperty); } set { SetValue(IsAutoSelectionActiveProperty, value); } } // step size public static readonly DependencyProperty IncrementProperty = DependencyProperty.Register( nameof(Increment), typeof(Decimal), typeof(UCNumericUpDown), new PropertyMetadata(1m), new ValidateValueCallback(IsValidIncrementReading)); private static bool IsValidIncrementReading(object value) { // 아래처럼 하면 예외가 발생함. //double num = (double)value; Decimal num = Convert.ToDecimal(value); return (num > 0); } // 문자열 타입으로 하면, 여러 숫자 타입에 대응 가능하지 않을까??? public Decimal Increment { get { return (Decimal)GetValue(IncrementProperty); } set { SetValue(IncrementProperty, value); } } public ICommand CMD_UpButtonClick { get; } public ICommand CMD_DownButtonClick { get; } #region 생성자 public UCNumericUpDown() : base() { InitializeComponent(); CMD_UpButtonClick = new DelegateCommand(UpButtonClick); CMD_DownButtonClick = new DelegateCommand(DownButtonClick); // 가장 마지막 컨트롤에만 적용이 되는데...??? 이상하네..... // Test : 키보드 화살표 키 매핑 // PreviewKeyDown 으로 변경 /* CommandManager.RegisterClassInputBinding(typeof(TextBox), new KeyBinding(CMD_UpButtonClick, new KeyGesture(Key.Up))); CommandManager.RegisterClassInputBinding(typeof(TextBox), new KeyBinding(CMD_DownButtonClick, new KeyGesture(Key.Down))); */ //============================ TextValue.LostFocus += TextBoxOnLostFocus; TextValue.PreviewMouseLeftButtonUp += TextBoxOnPreviewMouseLeftButtonUp; // --- 별 의미 없는 것 같은데... IncreaseButton.PreviewMouseLeftButtonDown += (sender, args) => RemoveFocus(); DecreaseButton.PreviewMouseLeftButtonDown += (sender, args) => RemoveFocus(); } #endregion protected override void ChangeThousandSeparator(decimal value) { // Change formatting based on this flag if (ThousandSeparator) { if (TextValue.Text != null) { TextValue.Text = value.ToString("N", Culture); } } else { if (TextValue.Text != null) { TextValue.Text = value.ToString("F", Culture); } } } private void TextBoxOnPreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e) { if (IsAutoSelectionActive) { TextValue.SelectAll(); } } private void RemoveFocus() { // Passes focus here and then just deletes it Focusable = true; Focus(); Focusable = false; } private void TextBoxOnLostFocus(object sender, RoutedEventArgs e) { Value = ParseString2Decimal(TextValue.Text); } private void UpButtonClick(object obj) { // Get the value that's currently in the TextBox.Text var _value = ParseString2Decimal(TextValue.Text); // Coerce the value to min/max CoerceValue2Bounds(ref _value); // Only change the value if it has any meaning // 참고한 자료는 무의미하지 않나?? 아래처럼 수정하는게 맞지 않나??? 흠... 일단은 참고자료대로.. if (_value < Maximum) { _value += Increment; } Value = _value; } private void DownButtonClick(object obj) { // Get the value that's currently in the TextBox.Text var _value = ParseString2Decimal(TextValue.Text); // Coerce the value to min/max CoerceValue2Bounds(ref _value); // Only change the value if it has any meaning if (_value > Minimum) { _value -= Increment; } Value = _value; } private void CoerceValue2Bounds(ref decimal value) { if (value < Minimum) { value = Minimum; } else if (value > Maximum) { value = Maximum; } } private void TextBox_KeyDown(object sender, KeyEventArgs e) { if (e.Key == Key.Enter) { // 엔터키 이벤트... Value = ParseString2Decimal(TextValue.Text); } } private void TextBox_PreviewKeyDown(object sender, KeyEventArgs e) { switch (e.Key) { // 키 이벤트... case Key.Up: { UpButtonClick(null); // 굳이 true 로 설정할 필요는 없을듯 ..? //e.Handled = true; break; } case Key.Down: { DownButtonClick(null); break; } } } protected override void KeyTextChanged(string newValue) { if (string.IsNullOrEmpty(newValue)) { // KeyText 가 없으므로, 해당 grid 의 width 를 0 으로 만든다 GridParent.ColumnDefinitions[0].Width = new GridLength(0); } } } }