286 lines
9.5 KiB
C#
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);
|
|
}
|
|
}
|
|
}
|
|
}
|