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

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

show decimal places of number in WPF according to user selection

问题

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

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

MainWindow.xaml:

  1. <Window x:Class="WpfApp1.MainWindow"
  2. xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  3. xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  4. xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
  5. xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  6. mc:Ignorable="d"
  7. DataContext="{Binding RelativeSource={RelativeSource Self}}"
  8. Width="400" Height="100">
  9. <StackPanel Orientation="Horizontal" Height="30">
  10. <TextBlock Margin="5" >Precision:</TextBlock>
  11. <ComboBox x:Name="cbPrecision"
  12. Margin="5"
  13. MinWidth="80"
  14. ItemsSource="{Binding Path=DecimalPlaces}"
  15. DisplayMemberPath="Value"
  16. SelectedValuePath="Key"/>
  17. <TextBlock Margin="5" >
  18. <TextBlock.Text>
  19. <MultiBinding StringFormat="Number: {0:N{1}}">
  20. <Binding Path="SomeNumber"/>
  21. <Binding Path="SelectedValue" ElementName="cbPrecision"/>
  22. </MultiBinding>
  23. </TextBlock.Text>
  24. </TextBlock>
  25. </StackPanel>

MainWindow.xaml.cs:

  1. using System.Collections.Generic;
  2. using System.Windows;
  3. namespace WpfApp1
  4. {
  5. public partial class MainWindow : Window
  6. {
  7. public MainWindow()
  8. {
  9. InitializeComponent();
  10. }
  11. public Dictionary<int, double> DecimalPlaces { get; } = new() {
  12. { 0, 1 },
  13. { 1, 0.1 },
  14. { 2, 0.01 },
  15. { 3, 0.001 }
  16. };
  17. public double SomeNumber { get; set; } = 123.45678;
  18. }
  19. }

结果是 TextBox 根本没有显示出来:

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

期望的结果是:

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

观察:

可能是嵌套的字符串格式化有问题

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

因为将包含 StringFormat 的代码行更改为

  1. <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:

  1. &lt;Window x:Class=&quot;WpfApp1.MainWindow&quot;
  2. xmlns=&quot;http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;
  3. xmlns:x=&quot;http://schemas.microsoft.com/winfx/2006/xaml&quot;
  4. xmlns:d=&quot;http://schemas.microsoft.com/expression/blend/2008&quot;
  5. xmlns:mc=&quot;http://schemas.openxmlformats.org/markup-compatibility/2006&quot;
  6. mc:Ignorable=&quot;d&quot;
  7. DataContext=&quot;{Binding RelativeSource={RelativeSource Self}}&quot;
  8. Width=&quot;400&quot; Height=&quot;100&quot;&gt;
  9. &lt;StackPanel Orientation=&quot;Horizontal&quot; Height=&quot;30&quot;&gt;
  10. &lt;TextBlock Margin=&quot;5&quot; &gt;Precision:&lt;/TextBlock&gt;
  11. &lt;ComboBox x:Name=&quot;cbPrecision&quot;
  12. Margin=&quot;5&quot;
  13. MinWidth=&quot;80&quot;
  14. ItemsSource=&quot;{Binding Path=DecimalPlaces}&quot;
  15. DisplayMemberPath=&quot;Value&quot;
  16. SelectedValuePath=&quot;Key&quot;/&gt;
  17. &lt;TextBlock Margin=&quot;5&quot; &gt;
  18. &lt;TextBlock.Text&gt;
  19. &lt;MultiBinding StringFormat=&quot;Number: {0:N{1}}&quot;&gt;
  20. &lt;Binding Path=&quot;SomeNumber&quot;/&gt;
  21. &lt;Binding Path=&quot;SelectedValue&quot; ElementName=&quot;cbPrecision&quot;/&gt;
  22. &lt;/MultiBinding&gt;
  23. &lt;/TextBlock.Text&gt;
  24. &lt;/TextBlock&gt;
  25. &lt;/StackPanel&gt;

</Window>

MainWindow.xaml.cs:

  1. using System.Collections.Generic;
  2. using System.Windows;
  3. namespace WpfApp1
  4. {
  5. public partial class MainWindow : Window
  6. {
  7. public MainWindow()
  8. {
  9. InitializeComponent();
  10. }
  11. public Dictionary&lt;int, double&gt; DecimalPlaces { get; } = new() {
  12. { 0, 1 },
  13. { 1, 0.1 },
  14. { 2, 0.01 },
  15. { 3, 0.001 }
  16. };
  17. public double SomeNumber { get; set; } = 123.45678;
  18. }
  19. }

As result the TextBox is not displayed at all:

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

Expected result:

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


Observation:

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

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

because changing the code line containing the StringFormat to

  1. &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接口的类。

具体的实现如下所示:

  1. // 在 Window_Loaded 中
  2. panel.DataContext = new PanelModel();
  3. class PanelModel : INotifyPropertyChanged
  4. {
  5. public Dictionary<string, double> DecimalPlaces { get; } = new() {
  6. { "N0", 1 },
  7. { "N1", 0.1 },
  8. { "N2", 0.01 },
  9. { "N3", 0.001 },
  10. { "N4", 0.0001 }
  11. };
  12. string _ContentFormat = "N0";
  13. double _SomeNumber = 123.45678;
  14. public string ContentFormat {
  15. get => _ContentFormat;
  16. set
  17. {
  18. if (value != _ContentFormat)
  19. {
  20. _ContentFormat = value;
  21. OnPropertyChanged(nameof(ContentFormat));
  22. OnPropertyChanged(nameof(SomeNumber));
  23. }
  24. }
  25. }
  26. public double SomeNumber
  27. {
  28. get => _SomeNumber;
  29. set
  30. {
  31. if (value != _SomeNumber)
  32. {
  33. _SomeNumber = value;
  34. OnPropertyChanged(nameof(SomeNumber));
  35. }
  36. }
  37. }
  38. public event PropertyChangedEventHandler PropertyChanged;
  39. protected void OnPropertyChanged(string propertyName)
  40. {
  41. PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
  42. }
  43. }

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

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

  1. <StackPanel x:Name="panel" Orientation="Horizontal">
  2. <Label Content="Precision: " />
  3. <ComboBox x:Name="comboBox1"
  4. ItemsSource="{Binding Path=DecimalPlaces}"
  5. DisplayMemberPath="Value"
  6. SelectedValuePath="Key"
  7. SelectedValue="{Binding Path=ContentFormat}"
  8. VerticalContentAlignment="Center"
  9. Width="120"/>
  10. <TextBox Text="{Binding Path=SomeNumber, UpdateSourceTrigger=PropertyChanged}"
  11. Width="120"
  12. Margin="15,0"/>
  13. <Label x:Name="labelSomeNumber"
  14. Content="{Binding Path=SomeNumber}"
  15. ContentStringFormat="{Binding Path=ContentFormat}" />
  16. </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:

  1. // in Window_Loaded
  2. panel.DataContext = new PanelModel();
  3. class PanelModel : INotifyPropertyChanged
  4. {
  5. public Dictionary&lt;string, double&gt; DecimalPlaces { get; } = new() {
  6. { &quot;N0&quot;, 1 },
  7. { &quot;N1&quot;, 0.1 },
  8. { &quot;N2&quot;, 0.01 },
  9. { &quot;N3&quot;, 0.001 },
  10. { &quot;N4&quot;, 0.0001 }
  11. };
  12. string _ContentFormat = &quot;N0&quot;;
  13. double _SomeNumber = 123.45678;
  14. public string ContentFormat {
  15. get =&gt; _ContentFormat;
  16. set
  17. {
  18. if (value != _ContentFormat)
  19. {
  20. _ContentFormat = value;
  21. OnPropertyChanged(nameof(ContentFormat));
  22. OnPropertyChanged(nameof(SomeNumber));
  23. }
  24. }
  25. }
  26. public double SomeNumber
  27. {
  28. get =&gt; _SomeNumber;
  29. set
  30. {
  31. if (value != _SomeNumber)
  32. {
  33. _SomeNumber = value;
  34. OnPropertyChanged(nameof(SomeNumber));
  35. }
  36. }
  37. }
  38. public event PropertyChangedEventHandler PropertyChanged;
  39. protected void OnPropertyChanged(string propertyName)
  40. {
  41. PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
  42. }
  43. }

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.

  1. &lt;StackPanel x:Name=&quot;panel&quot; Orientation=&quot;Horizontal&quot;&gt;
  2. &lt;Label Content=&quot;Precision: &quot; /&gt;
  3. &lt;ComboBox x:Name=&quot;comboBox1&quot;
  4. ItemsSource=&quot;{Binding Path=DecimalPlaces}&quot;
  5. DisplayMemberPath=&quot;Value&quot;
  6. SelectedValuePath=&quot;Key&quot;
  7. SelectedValue=&quot;{Binding Path=ContentFormat}&quot;
  8. VerticalContentAlignment=&quot;Center&quot;
  9. Width=&quot;120&quot;/&gt;
  10. &lt;TextBox Text=&quot;{Binding Path=SomeNumber, UpdateSourceTrigger=PropertyChanged}&quot;
  11. Width=&quot;120&quot;
  12. Margin=&quot;15,0&quot;/&gt;
  13. &lt;Label x:Name=&quot;labelSomeNumber&quot;
  14. Content=&quot;{Binding Path=SomeNumber}&quot;
  15. ContentStringFormat=&quot;{Binding Path=ContentFormat}&quot; /&gt;
  16. &lt;/StackPanel&gt;

答案2

得分: 0

以下是翻译好的内容:

根据Clemens的评论,我创建了一个转换器。完整的解决方案:

MainWindow.xaml:

  1. <Window x:Class="WpfApp1.MainWindow"
  2. xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  3. xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  4. xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
  5. xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  6. mc:Ignorable="d"
  7. xmlns:local="clr-namespace:WpfApp1"
  8. DataContext="{Binding RelativeSource={RelativeSource Self}}"
  9. Width="400" Height="100">
  10. <StackPanel Orientation="Horizontal" Height="30">
  11. <TextBlock Margin="5" >Precision:</TextBlock>
  12. <ComboBox x:Name="cbPrecision"
  13. Margin="5"
  14. MinWidth="80"
  15. ItemsSource="{Binding Path=DecimalPlaces}"
  16. DisplayMemberPath="Value"
  17. SelectedValuePath="Key"/>
  18. <TextBlock Margin="5" >
  19. <TextBlock.Text>
  20. <MultiBinding>
  21. <MultiBinding.Converter>
  22. <local:DecimalPlacesConverter />
  23. </MultiBinding.Converter>
  24. <Binding Path="SomeNumber"/>
  25. <Binding Path="SelectedValue" ElementName="cbPrecision"/>
  26. </MultiBinding>
  27. </TextBlock.Text>
  28. </TextBlock>
  29. </StackPanel>

MainWindow.xaml.cs:

  1. public partial class MainWindow : Window
  2. {
  3. public MainWindow()
  4. {
  5. InitializeComponent();
  6. }
  7. public Dictionary<int, double> DecimalPlaces { get; } = new() {
  8. { 0, 1 },
  9. { 1, 0.1 },
  10. { 2, 0.01 },
  11. { 3, 0.001 }
  12. };
  13. public double SomeNumber { get; set; } = 123.45678;
  14. }

DecimalPlacesConverter.cs:

  1. public class DecimalPlacesConverter : IMultiValueConverter
  2. {
  3. public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
  4. {
  5. try
  6. {
  7. return Math.Round((double)values[0], (int)values[1]).ToString("N" + values[1]);
  8. }
  9. catch (Exception)
  10. {
  11. return double.NaN;
  12. }
  13. }
  14. public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
  15. {
  16. throw new NotImplementedException();
  17. }
  18. }
英文:

Following Clemens comment I created a converter. Complete solution:

MainWindow.xaml:

  1. &lt;Window x:Class=&quot;WpfApp1.MainWindow&quot;
  2. xmlns=&quot;http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;
  3. xmlns:x=&quot;http://schemas.microsoft.com/winfx/2006/xaml&quot;
  4. xmlns:d=&quot;http://schemas.microsoft.com/expression/blend/2008&quot;
  5. xmlns:mc=&quot;http://schemas.openxmlformats.org/markup-compatibility/2006&quot;
  6. mc:Ignorable=&quot;d&quot;
  7. xmlns:local=&quot;clr-namespace:WpfApp1&quot;
  8. DataContext=&quot;{Binding RelativeSource={RelativeSource Self}}&quot;
  9. Width=&quot;400&quot; Height=&quot;100&quot;&gt;
  10. &lt;StackPanel Orientation=&quot;Horizontal&quot; Height=&quot;30&quot;&gt;
  11. &lt;TextBlock Margin=&quot;5&quot; &gt;Precision:&lt;/TextBlock&gt;
  12. &lt;ComboBox x:Name=&quot;cbPrecision&quot;
  13. Margin=&quot;5&quot;
  14. MinWidth=&quot;80&quot;
  15. ItemsSource=&quot;{Binding Path=DecimalPlaces}&quot;
  16. DisplayMemberPath=&quot;Value&quot;
  17. SelectedValuePath=&quot;Key&quot;/&gt;
  18. &lt;TextBlock Margin=&quot;5&quot; &gt;
  19. &lt;TextBlock.Text&gt;
  20. &lt;MultiBinding&gt;
  21. &lt;MultiBinding.Converter&gt;
  22. &lt;local:DecimalPlacesConverter /&gt;
  23. &lt;/MultiBinding.Converter&gt;
  24. &lt;Binding Path=&quot;SomeNumber&quot;/&gt;
  25. &lt;Binding Path=&quot;SelectedValue&quot; ElementName=&quot;cbPrecision&quot;/&gt;
  26. &lt;/MultiBinding&gt;
  27. &lt;/TextBlock.Text&gt;
  28. &lt;/TextBlock&gt;
  29. &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;
}
}

  1. public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
  2. {
  3. throw new NotImplementedException();
  4. }
  5. }

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

发表评论

匿名网友

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

确定