52

WPF のデータ バインディングを使用して、書式設定された 10 進数を TextBox に表示しようとしています。

目標

目標 1: コードで小数プロパティを設定するときは、TextBox に小数点以下 2 桁を表示します。

目標 2: ユーザーが TextBox を操作する (入力する) ときに、ユーザーを怒らせないでください。

目標 3: バインディングは PropertyChanged でソースを更新する必要があります。

試み

試行 1: フォーマットなし。

ここでは、ほぼゼロから始めています。

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

目標 1SomeDecimal = 4.5に違反します。TextBox に「4.50000」と表示されます。

試行 2: バインディングで StringFormat を使用します。

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

目標 2 に違反します。SomeDecimal が 2.5 で、TextBox に「2.50」と表示されているとします。すべてを選択して「13.5」と入力すると、フォーマッタが小数とゼロを「うまく」挿入するため、TextBox に「13.5.00」が表示されます。

試行 3: Extended WPF Toolkit の MaskedTextBox を使用します。

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

ドキュメントを正しく読んでいると仮定すると、マスク ##0.00 は「2 つのオプションの数字、その後に必要な数字、小数点、およびさらに 2 つの必要な数字が続く」ことを意味します。この 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=PropertyChanged を喜んで無視します。Extended WPF Toolkit Codeplex ページのコーディネーターの 1 人が、変更された ControlTemplate をhttp://wpftoolkit.codeplex.com/discussions/352551/で提案しています。これは目標 3 を満たしますが、目標 2 に違反し、試行 2 と同じ動作を示します。

試行 5: スタイル データトリガーを使用し、ユーザーが編集していない場合にのみ書式設定を使用します。

TextBox で StringFormat を使用して double にバインドする

これが 3 つの目的をすべて満たしていたとしても、私は使いたくありません。(A) テキストボックスはそれぞれ 1 行ではなく 12 行になり、私のアプリケーションには非常に多くのテキストボックスが含まれています。(B) すべてのテキスト ボックスには、Margin、Height などを設定するグローバルな StaticResource を指す Style 属性が既にあります。(C) 以下のコードがバインド パスを 2 回設定していることに気付いたかもしれませんが、これは 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」と表示されている TextBox をクリックすると、突然「13.5000」と表示されます。これは予期しないことです。次に、空白の TextBox で開始し、「13.50」と入力すると、TextBox には「1350」が含まれます。理由は説明できませんが、カーソルが TextBox の文字列の右端にある場合、ピリオド キーを押しても小数点が挿入されません。TextBox に長さが 0 より大きい文字列が含まれている場合、文字列の右端以外の場所にカーソルを移動すると、小数点を挿入できます。

試行 6: 自分で実行します。

TextBox をサブクラス化するか、添付プロパティを作成し、書式設定コードを自分で作成することによって、苦痛と苦しみの旅に出ようとしています。ひも操作でいっぱいになり、かなりの脱毛を引き起こします。


上記のすべての目標を満たすテキストボックスにバインドされた小数をフォーマットするための提案はありますか?

4

3 に答える 3

5

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));
        }
    }

サンプル

Xaml:

<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;
}
于 2012-07-13T21:17:19.040 に答える
1

WPF Extended Toolkit Masked TextBox を試して入力マスクを実装してください: http://wpftoolkit.codeplex.com/wikipage?title=MaskedTextBox

例:

<toolkit:MaskedTextBox Mask="(000) 000-0000" Value="(555) 123-4567" 
IncludeLiterals="True" />
于 2012-07-13T20:57:58.767 に答える