programing

사용자를 화나게 하지 않고 TextBox에 바인딩된 10진수 형식을 지정할 수 있는 방법은 무엇입니까?

showcode 2023. 4. 25. 23:46
반응형

사용자를 화나게 하지 않고 TextBox에 바인딩된 10진수 형식을 지정할 수 있는 방법은 무엇입니까?

WPF의 데이터 바인딩을 사용하여 텍스트 상자에 형식이 지정된 소수점을 표시하려고 합니다.

목표들

목표 1: 코드에서 소수점 속성을 설정할 때 TextBox에 소수점 2자리를 표시합니다.

목표 2: 사용자가 TextBox와 상호 작용할 때 화나게 하지 마십시오.

목표 3: 바인딩은 PropertyChanged의 소스를 업데이트해야 합니다.

시도합니다.

시도 1: 포맷하지 않습니다.

여기서 우리는 거의 처음부터 시작하고 있습니다.

<TextBox Text="{Binding Path=SomeDecimal, UpdateSourceTrigger=PropertyChanged}" />

목표 1을 위반합니다.SomeDecimal = 4.5텍스트 상자에 "4.500"이 표시됩니다.

시도 2: 바인딩에서 StringFormat을 사용합니다.

<TextBox Text="{Binding Path=SomeDecimal, UpdateSourceTrigger=PropertyChanged, StringFormat=F2}" />

목표 2를 위반합니다.SomeDecimal이 2.5라고 가정하고 TextBox에 "2.50"이 표시됩니다.모두 선택하고 "13.5"를 입력하면 형식 지정기가 소수점과 0을 "도움"으로 삽입하기 때문에 텍스트 상자에 "13.5.00"으로 표시됩니다.

시도 3: Extended WPF Toolkit의 MaskedTextBox를 사용합니다.

http://wpftoolkit.codeplex.com/wikipage?title=MaskedTextBox

설명서를 올바르게 읽고 있다고 가정할 때 마스크 ##0.00은 "선택적인 두 자리 숫자 뒤에 필수 자리, 소수점 및 필수 자리 두 자리 뒤에 오는 두 자리"를 의미합니다.이 경우 "이 TextBox에 들어갈 수 있는 최대 숫자는 999.99"라고 말해야 합니다. 하지만 문제가 없다고 가정해 보겠습니다.

<xctk:MaskedTextBox Value="{Binding Path=SomeDecimal, UpdateSourceTrigger=PropertyChanged}" Mask="##0.00" />

목표 2를 위반합니다.TextBox는 다음과 같이 시작합니다.___.__그리고 그것을 선택하고 5.75의 산출량을 입력합니다.575.__5.75를 얻는 유일한 방법은 TextBox를 선택하고 입력하는 것입니다.<space><space>5.75.

시도 4: Extended WPF Toolkit의 DecimalUpDown 스피너를 사용합니다.

http://wpftoolkit.codeplex.com/wikipage?title=DecimalUpDown

<xctk:DecimalUpDown Value="{Binding Path=SomeDecimal, UpdateSourceTrigger=PropertyChanged}" FormatString="F2" />

목표 3을 위반합니다.DecimalUpDown은 UpdateSourceTrigger=DiginalChanged를 무시합니다.Extended WPF Toolkit Codeplex 페이지의 코디네이터 중 한 명이 http://wpftoolkit.codeplex.com/discussions/352551/에서 수정된 ControlTemplate를 제안합니다.이는 목표 3을 만족시키지만 목표 2를 위반하여 시도 2와 동일한 동작을 보입니다.

시도 5: 스타일 데이터트리거를 사용하여 사용자가 편집하지 않을 때만 형식을 사용하십시오.

TextBox에서 StringFormat을 사용하여 이중으로 바인딩합니다.

이 텍스트 상자가 세 가지 목표를 모두 충족하더라도 사용하지 않을 것입니다. (A) 텍스트 상자는 각각 1 행이 아닌 12 행이 되며, 내 응용 프로그램에는 여러 텍스트 상자가 포함되어 있습니다. (B) 모든 텍스트 상자는 이미 여백, 높이 등을 설정하는 전역 정적 리소스를 가리키는 스타일 특성을 가지고 있습니다.(C) 아래 코드가 바인딩 경로를 두 번 설정하여 DRY 원칙을 위반하는 것을 눈치챘을 수 있습니다.

<TextBox>
    <TextBox.Style>
        <Style TargetType="{x:Type TextBox}">
            <Setter Property="Text" Value="{Binding Path=SomeDecimal, StringFormat=F2}" />
            <Style.Triggers>
                <Trigger Property="IsFocused" Value="True">
                    <Setter Property="Text" Value="{Binding Path=SomeDecimal, UpdateSourceTrigger=PropertyChanged}" />
                </Trigger>
            </Style.Triggers>
        </Style>
    </TextBox.Style>
</TextBox>

이 모든 불편한 일들은 제쳐두고 말이죠...

목표 2를 위반합니다.첫째, "13.50"으로 표시된 텍스트 상자를 클릭하면 갑자기 "13.5000"으로 표시되는데, 이는 예상치 못한 것입니다.둘째, 빈 TextBox로 시작하고 "13.50"을 입력하면 TextBox에 "1350"이 포함됩니다.이유는 설명할 수 없지만 TextBox에서 문자열의 오른쪽 끝에 커서가 있을 경우 마침표 키를 눌러도 소수점이 삽입되지 않습니다.TextBox에 길이 > 0인 문자열이 포함되어 있고 문자열의 오른쪽 끝을 제외한 다른 위치로 커서를 재배치하면 소수점을 삽입할 수 있습니다.

시도 6: 직접 하십시오.

저는 이제 TextBox를 세분화하거나 첨부파일을 만들고 포맷 코드를 직접 작성함으로써 고통과 고통의 여행을 떠나려고 합니다.끈 조작으로 가득 차서 탈모가 심해질 거예요.


위의 모든 목표를 충족하는 텍스트 상자에 바인딩된 소수점 형식을 제안할 수 있는 사람이 있습니까?

ViewModel 수준에서 문제를 해결합니다.바로 그거에요:

public class FormattedDecimalViewModel : INotifyPropertyChanged
    {
        private readonly string _format;

        public FormattedDecimalViewModel()
            : this("F2")
        {

        }

        public FormattedDecimalViewModel(string format)
        {
            _format = format;
        }

        private string _someDecimalAsString;
        // String value that will be displayed on the view.
        // Bind this property to your control
        public string SomeDecimalAsString
        {
            get
            {
                return _someDecimalAsString;
            }
            set
            {
                _someDecimalAsString = value;
                RaisePropertyChanged("SomeDecimalAsString");
                RaisePropertyChanged("SomeDecimal");
            }
        }

        // Converts user input to decimal or initializes view model
        public decimal SomeDecimal
        {
            get
            {
                return decimal.Parse(_someDecimalAsString);
            }
            set
            {
                SomeDecimalAsString = value.ToString(_format);
            }
        }

        // Applies format forcibly
        public void ApplyFormat()
        {
            SomeDecimalAsString = SomeDecimal.ToString(_format);
        }

        public event PropertyChangedEventHandler PropertyChanged;

        private void RaisePropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }

샘플입니다.

네, 그렇습니다.

<TextBox x:Name="tb" Text="{Binding Path=SomeDecimalAsString, UpdateSourceTrigger=PropertyChanged}" />

뒤에 코드가 있습니다.

public MainWindow()
{
    InitializeComponent();
    FormattedDecimalViewModel formattedDecimalViewModel = new FormattedDecimalViewModel { SomeDecimal = (decimal)2.50 };
    tb.LostFocus += (s, e) => formattedDecimalViewModel.ApplyFormat(); // when user finishes to type, will apply formatting
    DataContext = formattedDecimalViewModel;
}

사용할 때 사용자 커서를 소수점 이후로 이동하기 위해 다음과 같은 사용자 지정 동작을 만들었습니다.StringFormat={}{0:0.00}이 경우 소수점 이하가 로 표시되지만, 이 경우 다음과 같은 문제가 발생할 수 있습니다

목표 2를 위반합니다.SomeDecimal이 2.5라고 가정하고 TextBox에 "2.50"이 표시됩니다.모두 선택하고 "13.5"를 입력하면 형식 지정기가 소수점과 0을 "도움"으로 삽입하기 때문에 텍스트 상자에 "13.5.00"으로 표시됩니다.

사용자가 .키를 누르면 커서가 소수점 뒤로 이동하는 사용자 지정 동작을 사용하여 해킹했습니다.

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.Interactivity;

namespace GUI.Helpers.Behaviors
{
    public class DecimalPlaceHotkeyBehavior : Behavior<TextBox>
    {
        #region Methods
        protected override void OnAttached()
        {
            base.OnAttached();
            AssociatedObject.PreviewKeyDown += AssociatedObject_PreviewKeyDown;
        }

        protected override void OnDetaching()
        {
            base.OnDetaching();
            AssociatedObject.PreviewKeyDown -= AssociatedObject_PreviewKeyDown;
        }

        protected override Freezable CreateInstanceCore()
        {
            return new DecimalPlaceHotkeyBehavior();
        }
        #endregion

        #region Event Methods
        private void AssociatedObject_PreviewKeyDown(object sender, System.Windows.Input.KeyEventArgs e)
        {
            if (e.Key == System.Windows.Input.Key.OemPeriod || e.Key == System.Windows.Input.Key.Decimal)
            {
                var periodIndex = AssociatedObject.Text.IndexOf('.');
                if (periodIndex != -1)
                {
                    AssociatedObject.CaretIndex = (periodIndex + 1);
                    e.Handled = true;
                }
            }
        }
        #endregion

        #region Initialization
        public DecimalPlaceHotkeyBehavior()
            : base()
        {
        }
        #endregion
    }
}

다음과 같이 사용합니다.

<TextBox xmlns:Behaviors="clr-namespace:GUI.Helpers.Behaviors" 
         xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity" 
         Text="{Binding Value, UpdateSourceTrigger=PropertyChanged, StringFormat={}{0:0.00}}">
        <i:Interaction.Behaviors>
            <Behaviors:DecimalPlaceHotkeyBehavior></Behaviors:DecimalPlaceHotkeyBehavior>
        </i:Interaction.Behaviors>
</TextBox>

WPF Extended Toket Masked TextBox를 사용하여 입력 마스크를 구현해 보십시오. http://wpftoolkit.codeplex.com/wikipage?title=MaskedTextBox

예를 들어 다음과 같습니다.

<toolkit:MaskedTextBox Mask="(000) 000-0000" Value="(555) 123-4567" 
IncludeLiterals="True" />

언급URL : https://stackoverflow.com/questions/11477821/how-can-i-format-a-decimal-bound-to-textbox-without-angering-my-users 입니다.

반응형