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