英文:
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>
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 根本没有显示出来:
期望的结果是:
观察:
可能是嵌套的字符串格式化有问题
<MultiBinding StringFormat="Number: {0:N{1}}">
因为将包含 StringFormat 的代码行更改为
<MultiBinding StringFormat="Number: {0:N4}-DecimalPlaces: {1}">
可以正确显示 TextBox 中的值:
英文:
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:
<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;
}
}
As result the TextBox is not displayed at all:
Expected result:
Observation:
There must be some problem with the "nested" string formatting
<MultiBinding StringFormat="Number: {0:N{1}}">
because changing the code line containing the StringFormat to
<MultiBinding StringFormat="Number: {0:N4}-DecimalPlaces: {1}">
displays the values in the TextBox correctly:
答案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
,但它不起作用。
因此,我使用了INotifyPropertyChanged
接口,并在ContentFormat
更改时触发了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<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));
}
}
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.
<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>
答案2
得分: 0
以下是翻译好的内容:
根据Clemens的评论,我创建了一个转换器。完整的解决方案:
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>
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:
<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:
<!-- 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();
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论