xaml StringFormat that accepts "," as a decimal point - wpf

We have code for a textbox looking like this:
<TextBox Width="120" Grid.Column="2" Text="{Binding xxxxx, StringFormat=\{0:F2\}%}"
VerticalContentAlignment="Center" HorizontalAlignment="Left" >
The problem is that the user insists on it being necessary that they can use "," as a decimal point since that is the way it has been done over the years.
As it is now the formating just skips it so if you type 22,33% it makes it 2233%.
Is the any way I can make the StringFormat accept both "." and "," as decimal points, or do I have to format it in some other way (I'm new to WPF and xaml so might have missed something obvious)?

You could parse the value programmatically with a converter:
public class FormatConverter : IValueConverter
{
private static readonly CultureInfo s_cultureInfo = new CultureInfo("de");
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) =>
((decimal)value).ToString("F2") + "%";
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
string a = value.ToString();
decimal d;
if ((a?.Contains(",") == true && decimal.TryParse(a, NumberStyles.Any, s_cultureInfo, out d))
|| decimal.TryParse(a, NumberStyles.Any, CultureInfo.InvariantCulture, out d))
return d;
return Binding.DoNothing;
}
}
The above sample implementation uses a culture that supports , when trying to convert a string that contains a comma to a decimal.
This is how you would use it in your XAML markup:
<Window.Resources>
<local:FormatConverter x:Key="conv" />
</Window.Resources>
...
<TextBox Width="120" Grid.Column="2"
Text="{Binding xxxxx, Converter={StaticResource conv}}" />

Related

try to convert textbox value to int within convert method

i trying binding textbox value to button IsEnable property but i get an exception.
this is my convert class:
public class BooleanToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
string num = value as string;
int n = int.Parse(num);
if (n > 10) return true;
return false;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
this is my xaml code:
<Button Content="Button" Grid.Column="1" IsEnabled="{Binding ElementName=mytext,Path=Text,Converter={StaticResource convertIntToBool}}" HorizontalAlignment="Left" Margin="83,181,-156,-179" Grid.Row="1" VerticalAlignment="Top" Width="74"/>
<TextBox Grid.Column="1" x:Name="mytext" HorizontalAlignment="Left" Height="23" Margin="263,163,-382,-163" Grid.Row="1" TextWrapping="Wrap" Text="TextBox" VerticalAlignment="Top" Width="120"/>
i get FormatException: Input string was not in a correct format.
Ok so you want to handle user input in TextBox and make a button enabled or disabled according to this input.
First, you're using int.Parse, meaning that you trust the user to write a number in your TextBox: Don't ever trust user input.
You'll need to use int.TryParse so it doesn't throw an exception if the input isn't a number.
Second, I don't know why you set the TextBox's Text property to "TextBox", that's what is throwing the exception.
Your converter code should look like this:
public class BooleanToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
string num = value as string;
int n;
if(int.TryParse(num, out n)) { //return true is the parse worked
if(n > 10) return true;
}
return false;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}

WPF - binding complex objects to simple controls

I have created a class named "BoundProperty" which contains a property "Value".
Binding to a property which is an instance of that class looks this way
(Age is a BoundProperty):
<TextBox Text="{Binding MyModel.Age.Value, Mode=TwoWay}" />
Is there a way to make the binding look this way and additionally still retain it two-way?
<TextBox Text="{Binding MyModel.Age, Mode=TwoWay}" />
I can't use implicit/explicit conversion operators, for this "BoundProperty" initialization requires special parameters, which need to be copied from original object.
Thanks,
AD
If Value is public you can use ValueConverter:
public class BoundPropertyConverter:IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
var age = value as BoundProperty;
if (age == null)
return string.Empty;
return age.Value;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
int age;
if (int.TryParse(value.ToString(), out age))
return new BoundProperty() {Value = age};
return null;
}
}
Then in xaml define namespace to this converter
xmlns:converters="clr-namespace:Your.Namespace"
And then in Resources area write something like this:
<converters:BoundPropertyConverter x:Key="BoundPropertyConverter"/>
And last but not least:
<TextBox Text="{Binding MyModel.Age, Mode=TwoWay, Converter={StaticResource BoundPropertyConverter}" />

What's the correct way to do this <Image Source="../Images/{Binding Path=Id}.jpg"/>?

I have an Employee ID and an image associated with that employee in as a resource in the project (the image is being shown in a list next to employees name).
So I think something like this
<DataTemplate DataType="{x:Type m:Employee}">
<Grid>
<Image Grid.Column="0" Name="image" Source="../Images/{Binding Path=Id}.jpg"/>
It's not valid XAML.
I suppose I could handle some databinding event in the codebehind and create the path there? Doesn't seem ideal to me.
I could store the path in my Employee class but that's terrible.
You will have to use a IValueConverter
Heres a simple example passing in a String.Format as the converter paramerter
public class StringFormatToImageSourceConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (parameter is string)
{
return string.Format(parameter.ToString(), value);
}
return null;
}
public object ConvertBack(object value, Type targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
return null;
}
}
Usage:
<XXXX.Resources>
<local:StringFormatToImageSourceConverter x:Key="StringToImage" />
</XXXX.Resources>
<Image Source="{Binding Path=Id, Converter={StaticResource StringToImage}
, ConverterParameter=../Images/{0}.jpg}" />
There is a way to keep it all in Xaml by using an invisible TextBlock to format the string, but not the best practice.
<Grid>
<TextBlock x:Name="StringToImage" Visibility="Hidden" Text="{Binding Id, StringFormat=../Images/{0}.jpg}" />
<Image Source="{Binding Text, ElementName=StringToImage}"/>
</Grid>

Binding-driven Indexed Property Doesn't Return

Public Class View
Public Property Items As String() = {"One", "Two", "Three"}
Public Property Index As Integer = 0
End Class
It's instance is set as DataContext of this XAML:
<Window>
<StackPanel>
<ListBox ItemsSource="{Binding Items}" SelectedIndex="{Binding Index}"/>
<Label Content="{Binding Items[Index]}"/>
</StackPanel>
</Window>
But this doesn't work.
<Label Content="{Binding Items[{Binding Index}]}"/>
This neither.
<Label Content="{Binding Items[0]}"/>
This works.
Is there any solution except making extra property in view? Something directly in XAML?
I'm afraid it's not possible without some code-behind, but using reflection and dynamic, you can create a converter that can do this (it would be possible without dynamic, but more complex):
public class IndexerConverter : IValueConverter
{
public string CollectionName { get; set; }
public string IndexName { get; set; }
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
Type type = value.GetType();
dynamic collection = type.GetProperty(CollectionName).GetValue(value, null);
dynamic index = type.GetProperty(IndexName).GetValue(value, null);
return collection[index];
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}
Put following into resources:
<local:IndexerConverter x:Key="indexerConverter" CollectionName="Items" IndexName="Index" />
and use it like this:
<Label Content="{Binding Converter={StaticResource indexerConverter}}"/>
EDIT: The previous solution doesn't update properly when the values change, this one does:
public class IndexerConverter : IMultiValueConverter
{
public object Convert(object[] value, Type targetType, object parameter, CultureInfo culture)
{
return ((dynamic)value[0])[(dynamic)value[1]];
}
public object[] ConvertBack(object value, Type[] targetType, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}
In resources:
<local:IndexerConverter x:Key="indexerConverter"/>
Usage:
<Label>
<MultiBinding Converter="{StaticResource indexerConverter}">
<Binding Path="Items"/>
<Binding Path="Index"/>
</MultiBinding>
</Label>
What you write in the binding markup extension is assigned to the Path property by default, this property is a string so any dynamic content you refer to inside it will not be evaluated. There is no simple XAML-only method to do what you try to do.
Why don't use this:
<StackPanel>
<ListBox Name="lsbItems" ItemsSource="{Binding Items}" SelectedIndex="{Binding Index}"/>
<Label Content="{Binding ElementName=lsbItems, Path=SelectedItem}"/>
</StackPanel>

how to bind a boolean to combobox in wpf

Well I was wondering how to bind a boolean property to a combobox.Combobox will be a yes/no combobox.
You could use a ValueConverter to convert the boolean value to a ComboBox index and back. Like this:
public class BoolToIndexConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return ((bool)value == true) ? 0 : 1;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return ((int)value == 0) ? true : false;
}
}
}
Assuming Yes is on index 0 and No on index 1. Then you'd have to use that converter in binding to the SelectedIndex property. For this, you declare your converter in your resources section:
<Window.Resources>
<local:BoolToIndexConverter x:Key="boolToIndexConverter" />
</Window.Resources>
Then you use it in your binding:
<ComboBox SelectedIndex="{Binding YourBooleanProperty, Converter={StaticResource boolToIndexConverter}}"/>
I have found myself using the IsSelected property of the ComboBox items for this in the past. This method is entirely in xaml.
<ComboBox>
<ComboBoxItem Content="No" />
<ComboBoxItem Content="Yes" IsSelected="{Binding YourBooleanProperty, Mode=OneWayToSource}" />
</ComboBox>
First solution is to replace your 'Yes/No' combobox with a checkbox because, well, checkbox exists for a reason.
Second solution is to fill your combobox with true and false objects and then bind the 'SelectedItem' of your combobox to your Boolean property.
Here is an example (replace enabled/disabled with yes/no):
<ComboBox SelectedValue="{Binding IsEnabled}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Converter={x:Static converters:EnabledDisabledToBooleanConverter.Instance}}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
<ComboBox.Items>
<system:Boolean>True</system:Boolean>
<system:Boolean>False</system:Boolean>
</ComboBox.Items>
</ComboBox>
Here is Converter:
public class EnabledDisabledToBooleanConverter : IValueConverter
{
private const string EnabledText = "Enabled";
private const string DisabledText = "Disabled";
public static readonly EnabledDisabledToBooleanConverter Instance = new EnabledDisabledToBooleanConverter();
private EnabledDisabledToBooleanConverter()
{
}
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return Equals(true, value)
? EnabledText
: DisabledText;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
//Actually won't be used, but in case you need that
return Equals(value, EnabledText);
}
}
And no need to play with indices.

Resources