Bagaimana cara memformat jumlah tempat desimal di wpf menggunakan gaya / template?

94

Saya menulis program WPF dan saya mencoba mencari cara untuk memformat data dalam TextBox melalui beberapa metode berulang seperti gaya atau template. Saya memiliki banyak TextBox (95 tepatnya) dan masing-masing terikat pada data numeriknya sendiri yang masing-masing dapat memiliki resolusi sendiri. Misal jika datanya 99.123 dengan resolusi 2 maka seharusnya menampilkan 99.12. Demikian pula nilai data 99 dan resolusi 3 harus ditampilkan sebagai 99.000 (bukan 99). Apakah ada cara untuk melakukan ini?

Sunting: Saya harus menjelaskan, ada 95 Kotak Teks di layar saat ini yang saya kerjakan, tetapi saya ingin setiap Kotak Teks di berbagai layar dalam program saya menampilkan jumlah tempat desimal yang benar. Sekarang saya memikirkannya, beberapa di antaranya adalah TextBoxes (seperti layar yang sedang saya kerjakan sekarang) dan beberapa adalah DataGrids atau ListView, tetapi jika saya dapat mengetahui cara membuatnya berfungsi untuk TextBox, saya yakin saya bisa memahami itu untuk kontrol lainnya juga.

Tidak banyak kode untuk dibagikan dalam kasus ini, tetapi saya akan mencoba membuatnya lebih jelas:

Saya memiliki Model Tampilan yang berisi properti berikut (vb.net):

    Public ReadOnly Property Resolution As Integer
        Get
            Return _signal.DisplayResolution
        End Get
    End Property

    Public ReadOnly Property Value As Single
        Get
            Return Math.Round(_signal.DisplayValue, Resolution)
        End Get
    End Property

dan di XAML saya punya:

<UserControl.Resources>
    <vm:SignalViewModel x:Key="Signal" SignalPath="SomeSignal"/>
</UserControl.Resources>
<TextBox Grid.Column="3" IsEnabled="False" Text="{Binding Path=Value, Source={StaticResource Signal}, Mode=OneWay}" />

EDIT2 (solusi saya): Ternyata setelah meninggalkan komputer beberapa lama, saya kembali untuk menemukan jawaban sederhana yang menatap wajah saya. Format data dalam model tampilan!

    Public ReadOnly Property Value As String
        Get
            Return (Strings.FormatNumber(Math.Round(_signal.DisplayValue, _signal.DisplayResolution), _signal.DisplayResolution))
        End Get
    End Property
AXG1010
sumber
1
menggunakan IValueConverter? Berikan nilai aktual dan resolusi ke konverter dan biarkan konverter melakukan pembulatan untuk Anda di dalamnya. Sulit untuk menyarankan StringFormattanpa mengetahui bagaimana tepatnya 95 TextBoxini dihasilkan.
Viv
Posting kode saat ini dan XAML. Jika tidak, itu semua spekulasi dan tebakan yang tidak membantu.
Federico Berasategui
Saya menambahkan beberapa informasi lagi ke pertanyaan yang semoga membuatnya lebih jelas.
AXG1010

Jawaban:

197

Anda harus menggunakan StringFormatdi Binding. Anda dapat menggunakan format string standar , atau format string kustom :

<TextBox Text="{Binding Value, StringFormat=N2}" />
<TextBox Text="{Binding Value, StringFormat={}{0:#,#.00}}" />

Perhatikan bahwa StringFormathanya bekerja jika properti target berjenis string. Jika Anda mencoba menyetel sesuatu seperti Contentproperti ( typeof(object)), Anda perlu menggunakan custom StringFormatConverter( seperti di sini ), dan meneruskan string format Anda sebagai ConverterParameter.

Edit untuk pertanyaan yang diperbarui

Jadi, jika Anda ViewModelmendefinisikan presisi, saya sarankan melakukan ini sebagai MultiBinding, dan membuatnya sendiri IMultiValueConverter. Ini cukup menjengkelkan dalam praktiknya, untuk beralih dari pengikatan sederhana ke pengikatan yang perlu diperluas ke a MultiBinding, tetapi jika presisi tidak diketahui pada waktu kompilasi, cukup banyak yang dapat Anda lakukan. Anda IMultiValueConverterperlu mengambil nilainya, dan presisi, dan mengeluarkan string yang diformat. Anda dapat melakukannya dengan menggunakan String.Format.

Namun, untuk hal-hal seperti a ContentControl, Anda dapat lebih mudah melakukannya dengan Style:

<Style TargetType="{x:Type ContentControl}">
    <Setter Property="ContentStringFormat" 
            Value="{Binding Resolution, StringFormat=N{0}}" />
</Style>

Kontrol apa pun yang mengekspos a ContentStringFormatdapat digunakan seperti ini. Sayangnya, TextBoxtidak ada yang seperti itu.

Abe Heidebrecht
sumber
6
Contoh dengan StringFormat yang disetel ke #,#.00tidak dapat dikompilasi - koma diartikan sebagai pemisah untuk atribut di Bindingekstensi markup.
Gigi
@Gigi, Anda benar sedang, tetapi Anda dapat dengan mudah menggunakannya suka begitu: StringFormat={}{0:#,#.00}. Saya akan memperbarui jawaban agar berfungsi dengan benar.
Abe Heidebrecht
'StringFormat = N {0}' berfungsi dengan baik. Untuk presisi 2, saya ingin menampilkan dua desimal, kecuali '10 .00 ', dalam hal ini saya ingin menampilkan' 10 '. Adakah cara untuk melakukan ini saat mengikat presisi? Sepertinya saya harus menggunakan konverter.
Gordon Slysz
Saya tidak berpikir ada cara untuk mengubah desimal yang disajikan menggunakan format string .NET, jadi Anda mungkin lebih baik menulis konverter.
Abe Heidebrecht
Dapatkah Anda menjelaskan mengapa menggunakan dua penentu 0: #, #. 00, bukankah hanya satu saja yang cukup?
Lei Yang
7

Jawaban yang diterima tidak menunjukkan 0 di tempat bilangan bulat pada pemberian input seperti 0.299. Ini menunjukkan .3 di WPF UI. Jadi saran saya untuk menggunakan format string berikut

<TextBox Text="{Binding Value,  StringFormat={}{0:#,0.0}}" 
Manish Dubey
sumber
Hai, solusi Anda baik-baik saja tetapi saya lebih suka menggunakan kata kunci N1 (2,3 ...) karena menghindari kesalahan ketik dan setidaknya Anda yakin bagaimana itu akan ditampilkan. Namun memang saran kedua tidak menunjukkan 0 jika nilainya <0 seperti yang Anda sebutkan.
Kevin VDF
-2
    void NumericTextBoxInput(object sender, TextCompositionEventArgs e)
    {
        TextBox txt = (TextBox)sender;
        var regex = new Regex(@"^[0-9]*(?:\.[0-9]{0,1})?$");
        string str = txt.Text + e.Text.ToString();
        int cntPrc = 0;
        if (str.Contains('.'))
        {
            string[] tokens = str.Split('.');
            if (tokens.Count() > 0)
            {
                string result = tokens[1];
                char[] prc = result.ToCharArray();
                cntPrc = prc.Count();
            }
        }
        if (regex.IsMatch(e.Text) && !(e.Text == "." && ((TextBox)sender).Text.Contains(e.Text)) && (cntPrc < 3))
        {
            e.Handled = false;
        }
        else
        {
            e.Handled = true;
        }
    }
Monali Pawar
sumber
9
Beberapa penjelasan akan secara signifikan meningkatkan kualitas jawaban Anda.
mrun