根据用户选择,在WPF中显示数字的小数位数。

huangapple go评论74阅读模式
英文:

show decimal places of number in WPF according to user selection

问题

我想在WPF窗口中显示一个double类型的数字。用户应该能够选择显示多少位小数。

我想在视图(XAML)中直接解决这个问题,而不是在代码中格式化数字。我尝试使用StringFormat和MultiBinding来显示绑定的数字,并选择显示多少位小数:

MainWindow.xaml:

<Window x:Class="WpfApp1.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    DataContext="{Binding RelativeSource={RelativeSource Self}}"
    Width="400" Height="100">

    <StackPanel Orientation="Horizontal" Height="30">

        <TextBlock Margin="5">Precision:</TextBlock>

        <ComboBox x:Name="cbPrecision" 
                    Margin="5" 
                    MinWidth="80"
                    ItemsSource="{Binding Path=DecimalPlaces}" 
                    DisplayMemberPath="Value" 
                    SelectedValuePath="Key"/>

        <TextBlock Margin="5">
            <TextBlock.Text>
                <MultiBinding StringFormat="Number: {0:N{1}}">
                    <Binding Path="SomeNumber"/>
                    <Binding Path="SelectedValue" ElementName="cbPrecision"/>
                </MultiBinding>
            </TextBlock.Text>
        </TextBlock>

    </StackPanel>

</Window>

MainWindow.xaml.cs:

using System.Collections.Generic;
using System.Windows;

namespace WpfApp1
{
    public partial class MainWindow : Window
    {

        public MainWindow()
        {
            InitializeComponent();
        }

        public Dictionary<int, double> DecimalPlaces { get; } = new() {
            { 0, 1 },
            { 1, 0.1 },
            { 2, 0.01 },
            { 3, 0.001 }
        };

        public double SomeNumber { get; set; } = 123.45678;
    }
}

结果是TextBox根本没有显示:

根据用户选择,在WPF中显示数字的小数位数。

预期结果:

根据用户选择,在WPF中显示数字的小数位数。

观察:

StringFormat中的嵌套字符串格式化可能存在问题:

<MultiBinding StringFormat="Number: {0:N{1}}">

因为将StringFormat中的代码行更改为:

<MultiBinding StringFormat="Number: {0:N4}-DecimalPlaces: {1}">

会正确显示TextBox中的值:

根据用户选择,在WPF中显示数字的小数位数。

英文:

I want to display a number of type double in a WPF window. The user should be enabled to select how many decimal places should be displayed.

I want to solve this in the view (XAML) directly without formatting the number in code behind. I try to display the bound number with selected amount of decimal places using StringFormat and MultiBinding:

MainWindow.xaml:

&lt;Window x:Class=&quot;WpfApp1.MainWindow&quot;
    xmlns=&quot;http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;
    xmlns:x=&quot;http://schemas.microsoft.com/winfx/2006/xaml&quot;
    xmlns:d=&quot;http://schemas.microsoft.com/expression/blend/2008&quot;
    xmlns:mc=&quot;http://schemas.openxmlformats.org/markup-compatibility/2006&quot;
    mc:Ignorable=&quot;d&quot;
    DataContext=&quot;{Binding RelativeSource={RelativeSource Self}}&quot;
    Width=&quot;400&quot;  Height=&quot;100&quot;&gt;

&lt;StackPanel Orientation=&quot;Horizontal&quot; Height=&quot;30&quot;&gt;

    &lt;TextBlock  Margin=&quot;5&quot; &gt;Precision:&lt;/TextBlock&gt;
    
    &lt;ComboBox x:Name=&quot;cbPrecision&quot; 
                Margin=&quot;5&quot; 
                MinWidth=&quot;80&quot;
                ItemsSource=&quot;{Binding Path=DecimalPlaces}&quot; 
                DisplayMemberPath=&quot;Value&quot; 
                SelectedValuePath=&quot;Key&quot;/&gt;

    &lt;TextBlock Margin=&quot;5&quot; &gt;
        &lt;TextBlock.Text&gt;
            &lt;MultiBinding StringFormat=&quot;Number: {0:N{1}}&quot;&gt;
                &lt;Binding Path=&quot;SomeNumber&quot;/&gt;
                &lt;Binding Path=&quot;SelectedValue&quot; ElementName=&quot;cbPrecision&quot;/&gt;
            &lt;/MultiBinding&gt;
        &lt;/TextBlock.Text&gt;
    &lt;/TextBlock&gt;

&lt;/StackPanel&gt;

</Window>

MainWindow.xaml.cs:

using System.Collections.Generic;
using System.Windows;

namespace WpfApp1
{
    public partial class MainWindow : Window
    {

        public MainWindow()
        {
            InitializeComponent();
        }

        public Dictionary&lt;int, double&gt; DecimalPlaces { get; } = new() {
            { 0, 1 },
            { 1, 0.1 },
            { 2, 0.01 },
            { 3, 0.001 }
        };

        public double SomeNumber { get; set; } = 123.45678;
    }
}

As result the TextBox is not displayed at all:

根据用户选择,在WPF中显示数字的小数位数。

Expected result:

根据用户选择,在WPF中显示数字的小数位数。


Observation:

There must be some problem with the "nested" string formatting

&lt;MultiBinding StringFormat=&quot;Number: {0:N{1}}&quot;&gt;

because changing the code line containing the StringFormat to

&lt;MultiBinding StringFormat=&quot;Number: {0:N4}-DecimalPlaces: {1}&quot;&gt;

displays the values in the TextBox correctly:

根据用户选择,在WPF中显示数字的小数位数。

答案1

得分: 0

首先,将TextBlock替换为具有ContentStringFormat属性的Label,用于绑定。

然后,定义一个实现INotifyPropertyChanged接口的类。

具体的实现如下:

// 在Window_Loaded事件中
panel.DataContext = new PanelModel();

class PanelModel : INotifyPropertyChanged
{
    public Dictionary<string, double> DecimalPlaces { get; } = new() {
        { "N0", 1 },
        { "N1", 0.1 },
        { "N2", 0.01 },
        { "N3", 0.001 },
        { "N4", 0.0001 }
    };

    string _ContentFormat = "N0";
    double _SomeNumber = 123.45678;

    public string ContentFormat {
        get => _ContentFormat;
        set
        {
            if (value != _ContentFormat)
            {
                _ContentFormat = value;
                OnPropertyChanged(nameof(ContentFormat));
                OnPropertyChanged(nameof(SomeNumber));
            }
        }
    }

    public double SomeNumber
    {
        get => _SomeNumber;
        set
        {
            if (value != _SomeNumber)
            {
                _SomeNumber = value;
                OnPropertyChanged(nameof(SomeNumber));
            }
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

我尝试将comboBox1.SelectedValue绑定到ContentStringFormat,但它不起作用。

因此,当ContentFormat更改时,我使用INotifyPropertyChanged接口并触发了SomeNumber的更改通知。

<StackPanel x:Name="panel" Orientation="Horizontal">

    <Label Content="Precision: " />

    <ComboBox x:Name="comboBox1"
                ItemsSource="{Binding Path=DecimalPlaces}"
                DisplayMemberPath="Value"
                SelectedValuePath="Key"
                SelectedValue="{Binding Path=ContentFormat}"
                VerticalContentAlignment="Center"
                Width="120"/>

    <TextBox Text="{Binding Path=SomeNumber, UpdateSourceTrigger=PropertyChanged}"
             Width="120"
             Margin="15,0"/>

    <Label x:Name="labelSomeNumber"
            Content="{Binding Path=SomeNumber}"
            ContentStringFormat="{Binding Path=ContentFormat}" />

</StackPanel>
英文:

First, replace TextBlock with Label, which has a ContentStringFormat property for binding.

Then, define a class that implements the INotifyPropertyChanged interface.

The specific implementation is as follows:


// in Window_Loaded
panel.DataContext = new PanelModel();

class PanelModel : INotifyPropertyChanged
{
    public Dictionary&lt;string, double&gt; DecimalPlaces { get; } = new() {
        { &quot;N0&quot;, 1 },
        { &quot;N1&quot;, 0.1 },
        { &quot;N2&quot;, 0.01 },
        { &quot;N3&quot;, 0.001 },
        { &quot;N4&quot;, 0.0001 }
    };

    string _ContentFormat = &quot;N0&quot;;
    double _SomeNumber = 123.45678;

    public string ContentFormat {
        get =&gt; _ContentFormat;
        set
        {
            if (value != _ContentFormat)
            {
                _ContentFormat = value;
                OnPropertyChanged(nameof(ContentFormat));
                OnPropertyChanged(nameof(SomeNumber));
            }
        }
    }

    public double SomeNumber
    {
        get =&gt; _SomeNumber;
        set
        {
            if (value != _SomeNumber)
            {
                _SomeNumber = value;
                OnPropertyChanged(nameof(SomeNumber));
            }
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

I tried to bind comboBox1.SelectedValue to ContentStringFormat, but it doesn't work.

So, I used the INotifyPropertyChanged interface and triggered the SomeNumber Changed notification when ContentFormat changes.

&lt;StackPanel x:Name=&quot;panel&quot; Orientation=&quot;Horizontal&quot;&gt;

    &lt;Label Content=&quot;Precision: &quot; /&gt;

    &lt;ComboBox x:Name=&quot;comboBox1&quot;
                ItemsSource=&quot;{Binding Path=DecimalPlaces}&quot;
                DisplayMemberPath=&quot;Value&quot;
                SelectedValuePath=&quot;Key&quot;
                SelectedValue=&quot;{Binding Path=ContentFormat}&quot;
                VerticalContentAlignment=&quot;Center&quot;
                Width=&quot;120&quot;/&gt;

    &lt;TextBox Text=&quot;{Binding Path=SomeNumber, UpdateSourceTrigger=PropertyChanged}&quot;
             Width=&quot;120&quot;
             Margin=&quot;15,0&quot;/&gt;

    &lt;Label x:Name=&quot;labelSomeNumber&quot;
            Content=&quot;{Binding Path=SomeNumber}&quot;
            ContentStringFormat=&quot;{Binding Path=ContentFormat}&quot; /&gt;

&lt;/StackPanel&gt;

答案2

得分: 0

以下是翻译好的部分:

MainWindow.xaml:

<Window x:Class="WpfApp1.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    xmlns:local="clr-namespace:WpfApp1"
    DataContext="{Binding RelativeSource={RelativeSource Self}}"
    Width="400" Height="100">

    <StackPanel Orientation="Horizontal" Height="30">

        <TextBlock Margin="5">Precision:</TextBlock>

        <ComboBox x:Name="cbPrecision"
                Margin="5"
                MinWidth="80"
                ItemsSource="{Binding Path=DecimalPlaces}"
                DisplayMemberPath="Value"
                SelectedValuePath="Key"/>

        <TextBlock Margin="5">
            <TextBlock.Text>
                <MultiBinding>
                    <MultiBinding.Converter>
                        <local:DecimalPlacesConverter />
                    </MultiBinding.Converter>
                    <Binding Path="SomeNumber"/>
                    <Binding Path="SelectedValue" ElementName="cbPrecision"/>
                </MultiBinding>
            </TextBlock.Text>
        </TextBlock>
    </StackPanel>
</Window>

MainWindow.xaml.cs:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }
    public Dictionary<int, double> DecimalPlaces { get; } = new() {
        { 0, 1 },
        { 1, 0.1 },
        { 2, 0.01 },
        { 3, 0.001 }
    };
    public double SomeNumber { get; set; } = 123.45678;
}

DecimalPlacesConverter.cs:

public class DecimalPlacesConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        try
        {
            return Math.Round((double)values[0], (int)values[1]).ToString("N" + values[1]);
        }
        catch (Exception)
        {
            return double.NaN;
        }
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}
英文:

Following Clemens comment I created a converter. Complete solution:

MainWindow.xaml:

&lt;Window x:Class=&quot;WpfApp1.MainWindow&quot;
    xmlns=&quot;http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;
    xmlns:x=&quot;http://schemas.microsoft.com/winfx/2006/xaml&quot;
    xmlns:d=&quot;http://schemas.microsoft.com/expression/blend/2008&quot;
    xmlns:mc=&quot;http://schemas.openxmlformats.org/markup-compatibility/2006&quot;
    mc:Ignorable=&quot;d&quot;
    xmlns:local=&quot;clr-namespace:WpfApp1&quot;
    DataContext=&quot;{Binding RelativeSource={RelativeSource Self}}&quot;
    Width=&quot;400&quot;  Height=&quot;100&quot;&gt;

&lt;StackPanel Orientation=&quot;Horizontal&quot; Height=&quot;30&quot;&gt;

    &lt;TextBlock  Margin=&quot;5&quot; &gt;Precision:&lt;/TextBlock&gt;
    
    &lt;ComboBox x:Name=&quot;cbPrecision&quot; 
                Margin=&quot;5&quot; 
                MinWidth=&quot;80&quot;
                ItemsSource=&quot;{Binding Path=DecimalPlaces}&quot; 
                DisplayMemberPath=&quot;Value&quot; 
                SelectedValuePath=&quot;Key&quot;/&gt;

    &lt;TextBlock Margin=&quot;5&quot; &gt;
        &lt;TextBlock.Text&gt;
            &lt;MultiBinding&gt;
                &lt;MultiBinding.Converter&gt;
                    &lt;local:DecimalPlacesConverter /&gt;
                &lt;/MultiBinding.Converter&gt;
                &lt;Binding Path=&quot;SomeNumber&quot;/&gt;
                &lt;Binding Path=&quot;SelectedValue&quot; ElementName=&quot;cbPrecision&quot;/&gt;
            &lt;/MultiBinding&gt;
        &lt;/TextBlock.Text&gt;
    &lt;/TextBlock&gt; 
&lt;/StackPanel&gt;

</Window>

MainWindow.xaml.cs:
<!-- language: c# -->
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
public Dictionary<int, double> DecimalPlaces { get; } = new() {
{ 0, 1 },
{ 1, 0.1 },
{ 2, 0.01 },
{ 3, 0.001 }
};
public double SomeNumber { get; set; } = 123.45678;
}

DecimalPlacesConverter.cs:
<!-- language: c# -->
public class DecimalPlacesConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
try
{
return Math.Round((double)values[0], (int)values1).ToString("N" + values1);
}
catch (Exception)
{
return double.NaN;
}
}

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

huangapple
  • 本文由 发表于 2023年8月9日 16:28:19
  • 转载请务必保留本文链接:https://go.coder-hub.com/76865906-2.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定