2026-01-26 14:51:28 +09:00

286 lines
9.5 KiB
C#

/*
* 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 버튼)
// 기타. 유닛 레이블
/// <summary>
/// Interaction logic for UCNumericUpDown.xaml
/// </summary>
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);
}
}
}
}