xaml Binding to enum. Display value instead of "ToString" - wpf

I have a property of an enum type. I bind the content of a wpf control to this property. This will display the name of the enum value. So the ToString Method of enum is called.
But I need to display the value, not the string value. Does anyone know how to do this?
This is my C# code:
public enum Animal
{
cat = 0,
dog = 1,
mouse = 2
}
public Animal MyAnimal { get; set; }
void SomeMethod() { MyAnimal = dog; }
This is in my XAML:
<Label Content="{Binding MyAnimal}">

When you bind to a value of one type and want to display it in another format than the default ToString() method provides you should either use a DataTemplate or an IValueConverter. Since XAML is a markup language you cannot really cast the enumeration value to an int in your markup so you should use a converter:
public class EnumConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
animals enumValue = (animals)value;
return System.Convert.ToInt32(enumValue);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
<Window.Resources>
<local:EnumConverter x:Key="conv" />
</Window.Resources>
...
<ContentControl Content="{Binding TheEnumProperty, Converter={StaticResource conv}}" />

I have found a solution:
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (!(value is Enum)) return value;
return Enum.IsDefined(value.GetType(), value) ? value : System.Convert.ToInt32(value);
}

Related

How to use specific properties of a Value converter in wpf xaml

I have a enum to string converter
public class EnumToStringConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter,
CultureInfo culture)
{
MailSettingsStateEnum enumValue = (MailSettingsStateEnum)value;
// extension method on the enum, to return a string based on enum.
return enumValue.Description();
}
// ConvertBack not relevant here.
}
I am using this in wpf xaml easily as follows to set the Content property of a label.
<Label Content="{Binding MailSettingState, Converter={StaticResource
EnumConverterString}}"
BorderBrush="{Binding MailSettingState, Converter={StaticResource
EnumConverterBorderBrush}}" />
Now as you can see, I have another property BorderBrush. I also have to set this based on the same enum. And so I had to write another converter EnumConverterBorderBrush
So is there a way by which I have only one converter, and it return an object which has two properties and i can use these properties in the xaml? I can create the converter, its easy, but I dont know how to use it in xaml. Say the converter returned an object and has tow property called MessageString(of type string), and another BorderBrush of the type Brush, how do I use it the xaml?
You can switch the output based on the targetType you receive in your converter.
So you could do something like this:
public class EnumToStringConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter,
CultureInfo culture)
{
var enumValue = (MailSettingsStateEnum)value;
switch(targetType)
{
case typeof(string)
return enumValue.Description();
case typeof(Brush)
return enumValue.GetBrush();
default:
throw new NotSupportedException("Type not supported")
}
}
// ConvertBack not relevant here.
}
Now you'll have one converter to rule them all!
converter should return object which match requested targetType. converter can return different values for input enum value depending on parameter. I think it is more flexible than relying on targetType only.
public class SpecEnumConverter: IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is Enum)
{
if ((string) parameter == "brush")
return "Red"; // return brush here!
// if not pre-defined parameter (null or any other), return description
return (int) value; // return enum description here!
}
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
usage:
<Label Content="{Binding MailSettingState, Converter={StaticResource
EnumConverterSpec}}"
BorderBrush="{Binding MailSettingState, Converter={StaticResource
EnumConverterSpec}, ConverterParameter='brush'}" />
I already commented above, but here's the solution.
<Label DataContext="{Binding MailSettingState, Converter={converters:EnumConverter}}" Content="{Binding Label}" BorderBrush="{Binding BorderBrush}"/>
public class EnumConverter: MarkupExtension, IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var enumValue = (MailSettingsStateEnum) value;
return new ConvertedEnum { Label = enumValue.Description(),
BorderBrush = new BorderBrush()};
}
// ConvertBack not relevant here.
public override object ProvideValue(IServiceProvider serviceProvider)
{
return this;
}
}
public class ConvertedEnum
{
public string Label {get; set;}
public BorderBrush {get; set;}
}
Separate converters still look prettier to me.

WPF Cyclic Converter

When you drag the slider, you get a ConvertBack (expected), but why do I then get a "Convert" straight after that? I'd only expect Convert to be called when its first initialized, or if it was raising a property change notification, but it doesn't.
<Window x:Class="WpfApplication10.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:l="clr-namespace:WpfApplication10"
Title="MainWindow">
<Slider Value="{Binding Value, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay, Converter={l:Converter}}"/>
</Window>
public class Converter : MarkupExtension, IValueConverter
{
public override object ProvideValue(IServiceProvider serviceProvider)
{
return this;
}
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return value;
}
}
public partial class MainWindow : Window
{
public double Value { get; set; }
public MainWindow()
{
DataContext = this;
InitializeComponent();
}
}
This is likely occurring because you're using .NET 4, which changed the way bindings work a little bit : http://karlshifflett.wordpress.com/2009/05/27/wpf-4-0-data-binding-change-great-feature/
Whereas prior to 4 the binding wouldn't update back (assuming that it didn't need to), the new behavior is to do that by default. Rationale is explained in the linked blog.
Edit: I suppose I should ask if there is a reason you don't want it to convert back, or if it's just curiosity? If you need to prevent converting back, the clearest strategy is probably to keep track of your last converted value to parrot back :
public class Converter : MarkupExtension, IValueConverter
{
public override object ProvideValue(IServiceProvider serviceProvider)
{
return this;
}
public object lastValue;
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return lastValue;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
lastValue = value;
return value;
}
}
There's also the option of setting the binding to OneWayToSource, which would keep it from setting back to the slider value... assuming you don't need to push data back to the slider.

WPF Radiobutton (two) (binding to boolean value)

I have a property of type boolean presented with checkbox.
I want to change that to two radiobuttons that bind on the same property presenting the value true/false.
How can that be done?
<RadioButton GroupName="Group1"
IsChecked="{Binding PropertyValue}" Content="Yes" />
<RadioButton GroupName="Group1" Content="No"
IsChecked="{Binding PropertyValue,
Converter={StaticResource BoolInverterConverter}}" />
public class BoolInverterConverter : IValueConverter
{
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
if (value is bool)
{
return !(bool)value;
}
return value;
}
public object ConvertBack(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
if (value is bool)
{
return !(bool)value;
}
return value;
}
#endregion
}
The standard binding approach has the unfortunate side effect of firing the binding setter as "unselected" whenever the UI is loaded. So if you've got code to handle the user's clicks in the setter for your bool, it will do some weird stuff like fire the setter to "false" even though you've bound it to a "true" bool.
I got around this with a converter used specifically for radio buttons:
public class BoolRadioConverter : IValueConverter
{
public bool Inverse { get; set; }
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
bool boolValue = (bool) value;
return this.Inverse ? !boolValue : boolValue;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
bool boolValue = (bool)value;
if (!boolValue)
{
// We only care when the user clicks a radio button to select it.
return null;
}
return !this.Inverse;
}
}
In your resources:
<converters:BoolRadioConverter x:Key="BoolRadioConverter" />
<converters:BoolRadioConverter x:Key="InverseBoolRadioConverter" Inverse="True" />
In your xaml:
<RadioButton
Content="True option"
GroupName="radioGroup1"
IsChecked="{Binding MyProperty,
Converter={StaticResource BoolRadioConverter}}" />
<RadioButton
Content="False option"
GroupName="radioGroup2"
IsChecked="{Binding MyProperty,
Converter={StaticResource InverseBoolRadioConverter}}" />
You can use a value-converter that reverts the boolean value:
With that converter, bind one Checkbox.IsChecked-property to the boolean value without the converter and one CheckBox.IsChecked-property with the converter. This should do the trick.
Here the code for such a converter. I have copied it from here and added some lines of code. There you will find more information about.
public class BoolToOppositeBoolConverter : IValueConverter {
public object Convert(object value, Type targetType, object parameter,System.Globalization.CultureInfo culture) {
if (targetType != typeof(bool)) {
throw new InvalidOperationException("The target must be a boolean");
}
if (null == value) {
return null;
}
return !(bool)value;
}
public object ConvertBack(object value, Type targetType, object parameter,System.Globalization.CultureInfo culture) {
if (targetType != typeof(bool)) {
throw new InvalidOperationException("The target must be a boolean");
}
if (null == value) {
return null;
}
return !(bool)value;
}
}
To use it, declare it in the resource-section.
<local:BoolToOppositeBoolConverter x:Key="BoolToOppositeBoolConverter_ValueConverter"/>
And the use it in the binding as a static resource:
<CheckBox IsChecked="{Binding YourProperty}" />
<CheckBox IsChecked="{Binding YourProperty,Converter={StaticResource BoolToOppositeBoolConverter_ValueConverter}}" />
Please note, the converter is only a simple example. Implement it neatly if you want to use it in productive code. I have not tested it. Make a comment if its not working.
You can achieve this without a converter if you set the GroupName property of two radio button to the same value (so only one can be checked at the time). Then, set IsChecked of one radio button to "True", and bind IsChecked of another to your Boolean. Switching radio buttons will change the Boolean value, however, changing the Boolean value to False will not check the other radio button.
Thanks,
Vlad
Here is the solution on how to bind radio buttons to any type (enumeration, Boolean, string, integer, etc.) with the sample code:
http://www.codeproject.com/Tips/720497/Binding-Radio-Buttons-to-a-Single-Property
When using MVVMLight and DataContext is set in XAML as:
DataContext="{Binding <Your ViewModel property name>, Source={StaticResource Locator}}"
BoolInverterConverter causes Stack Overflow second time the window gets opened.
The work around is to remove DataContext from XAML and do it in code in window constructor after InitializeComponent():
DataContext = ServiceLocator.Current.GetInstance<Your View Model class>();
After some testing it was not enough - Stack Overflow error could pop up randomly when clicking on radio button. The solution which worked for me - instead of the converter use another property for other radio button in a group:
public bool Is9to1 { get; set; }
public bool Is1to9 { get { return !Is9to1; } set { Is9to1 = !value; } }
in XAML:
<RadioButton GroupName="Is9to1" IsChecked="{Binding Is1to9}"/>
<RadioButton GroupName="Is9to1" IsChecked="{Binding Is9to1}"/>
Simplified version of ragunathan's answer.
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) =>
Convert(value);
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) =>
Convert(value);
private object Convert(object value) =>
value is bool ? !(bool)value : value;
Little upgrade of RandomEngy's answer if you want your bool nullable (for no default value/Checked Radiobutton)
public class BoolRadioConverter : IValueConverter
{
public bool Inverse { get; set; }
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
bool? boolValue = (bool?)value;
return this.Inverse ? !boolValue : boolValue;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
bool? boolValue = (bool?)value;
if (boolValue != null && (bool)!boolValue)
{
// We only care when the user clicks a radio button to select it.
return null;
}
return !this.Inverse;
}
}
and the rest is the same as his answer.
From The answer of Mr-RandomQC everything works fine. But when I click another radio in the same group. Then the opposite radio will show a Red box around the radio button.
I changed the code a little bit from his answer to return Binding.DoNothing instead of return null like this.
public class BoolRadioConverter : IValueConverter
{
public bool Inverse { get; set; }
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
bool boolValue = (bool)value;
return this.Inverse ? !boolValue : boolValue;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
bool boolValue = (bool)value;
if (!boolValue)
{
// Return Binding.DoNothing instead of Return null
return Binding.DoNothing;
}
return !this.Inverse;
}
}

How do I convert a Color to a Brush in XAML?

I want to convert a System.Windows.Media.Color value to a System.Windows.Media.Brush. The color value is databound to a Rectangle object's Fill property. The Fill property takes a Brush object, so I need an IValueConverter object to perform the conversion.
Is there a built-in converter in WPF or do I need to create my own? How do I go about creating my own if it becomes necessary?
I know I am really late to the party, but you don't need a converter for this.
You could do
<Rectangle>
<Rectangle.Fill>
<SolidColorBrush Color="{Binding YourColorProperty}" />
</Rectangle.Fill>
</Rectangle>
It seems that you have to create your own converter. Here a simple example to start:
public class ColorToSolidColorBrushValueConverter : IValueConverter {
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
if (null == value) {
return null;
}
// For a more sophisticated converter, check also the targetType and react accordingly..
if (value is Color) {
Color color = (Color)value;
return new SolidColorBrush(color);
}
// You can support here more source types if you wish
// For the example I throw an exception
Type type = value.GetType();
throw new InvalidOperationException("Unsupported type ["+type.Name+"]");
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
// If necessary, here you can convert back. Check if which brush it is (if its one),
// get its Color-value and return it.
throw new NotImplementedException();
}
}
To use it, declare it in the resource-section.
<local:ColorToSolidColorBrushValueConverter x:Key="ColorToSolidColorBrush_ValueConverter"/>
And the use it in the binding as a static resource:
Fill="{Binding Path=xyz,Converter={StaticResource ColorToSolidColorBrush_ValueConverter}}"
I have not tested it. Make a comment if its not working.
A Converter is not needed here. You can define a Brush in XAML and use it. It would be better to define the Brush as a Resource so it can be used other places required.
XAML is as below:
<Window.Resources>
<SolidColorBrush Color="{Binding ColorProperty}" x:Key="ColorBrush" />
</Window.Resources>
<Rectangle Width="200" Height="200" Fill="{StaticResource ColorBrush}" />
I wanted to do this HCL's way rather than Jens' way because I have a lot of things bound to the Color, so there's less duplication and boiler-plate .Fill nodes.
However when trying to write it, ReSharper pointed me to the WPF Toolkit's implementation of the ColorToSolidColorBrushConverter. You need to include the following namespace declaration in the main Window/UserControl node:
xmlns:Toolkit="clr-namespace:Microsoft.Windows.Controls.Core.Converters;assembly=WPFToolkit.Extended"
Then a static resource in the Window/UserControl resources:
<Toolkit:ColorToSolidColorBrushConverter x:Key="colorToSolidColorBrushConverter" />
Then you can do it like HCL's answer:
<Rectangle Fill="{Binding Color, Converter={StaticResource colorToSolidColorBrushConverter}}" />
With some more enhancment to HCL answer, I tested it - it works.
public class ColorToSolidColorBrushValueConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value == null)
return null;
if (value is Color)
return new SolidColorBrush((Color)value);
throw new InvalidOperationException("Unsupported type [" + value.GetType().Name + "], ColorToSolidColorBrushValueConverter.Convert()");
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value == null)
return null;
if (value is SolidColorBrush)
return ((SolidColorBrush)value).Color;
throw new InvalidOperationException("Unsupported type [" + value.GetType().Name + "], ColorToSolidColorBrushValueConverter.ConvertBack()");
}
}
Converter:
[ValueConversion(typeof(SolidColorBrush), typeof(Color))]
public class SolidBrushToColorConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (!(value is SolidColorBrush)) return null;
var result = (SolidColorBrush)value;
return result.Color;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
XAML:
//...
<converters:SolidBrushToColorConverter x:Key="SolidToColorConverter" />
//...
<Color>
<Binding Source="{StaticResource YourSolidColorBrush}"
Converter="{StaticResource SolidToColorConverter}">
</Binding>
</Color>
//...
In addition to HCLs answer: If you don't want to care if System.Windows.Media.Color is used or System.Drawing.Color you can use this converter, which accepts both:
public class ColorToSolidColorBrushValueConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
switch (value)
{
case null:
return null;
case System.Windows.Media.Color color:
return new SolidColorBrush(color);
case System.Drawing.Color sdColor:
return new SolidColorBrush(System.Windows.Media.Color.FromArgb(sdColor.A, sdColor.R, sdColor.G, sdColor.B));
}
Type type = value.GetType();
throw new InvalidOperationException("Unsupported type [" + type.Name + "]");
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Using Pattern matching there's no need for neither null checking and double casting:
public class ColorToSolidBrushValueConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is Color color) return new SolidColorBrush(color);
throw new InvalidOperationException(nameof(color));
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) => new NotImplementedException();
}
Optional implementation for ConvertBack:
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is SolidColorBrush brush)
{
return brush.Color;
}
throw new InvalidOperationException(nameof(brush));
}

Set value to null in WPF binding

please take a look at the following line
<TextBox Text="{Binding Price}"/>
This Price property from above is a Decimal? (Nullable decimal).
I want that if user deletes the content of the textbox (i.e. enters empty string, it should automatcally update source with null (Nothing in VB).
Any ideas on how I can do it 'Xamly'?
I am using .NET 3.5 SP1 so it's very simple:
<TextBox Text="{Binding Price, TargetNullValue=''}"/>
Which stands for (thanks Gregor for your comment):
<TextBox Text="{Binding Price, TargetNullValue={x:Static sys:String.Empty}}"/>
sys is the imported xml namespace for System in mscorlib:
xmlns:sys="clr-namespace:System;assembly=mscorlib"
Hope that helped.
This value converter should do the trick :
public class StringToNullableDecimalConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter,
CultureInfo culture)
{
decimal? d = (decimal?)value;
if (d.HasValue)
return d.Value.ToString(culture);
else
return String.Empty;
}
public object ConvertBack(object value, Type targetType,
object parameter, CultureInfo culture)
{
string s = (string)value;
if (String.IsNullOrEmpty(s))
return null;
else
return (decimal?)decimal.Parse(s, culture);
}
}
Declare an instance of this converter in the ressources :
<Window.Resources>
<local:StringToNullableDecimalConverter x:Key="nullDecimalConv"/>
</Window.Resources>
And use it in your binding :
<TextBox Text="{Binding Price, Converter={StaticResource nullDecimalConv}}"/>
Note that TargetNullValue is not appropriate here : it is used to define which value should be used when the source of the binding is null. Here Price is not the source, it's a property of the source...
You can try using a ValueConverter (IValueConverter)
http://msdn.microsoft.com/en-us/library/system.windows.data.ivalueconverter.aspx
Of the back of my head here, something like:
public class DoubleConverter : IValueConverter
{
public object Convert(object value, Type targetType,
object parameter, CultureInfo culture)
{
return (double)value;
}
public object ConvertBack(object value, Type targetType,
object parameter, CultureInfo culture)
{
var doubleValue = Convert.ToDouble(value);
return (doubleValue == 0 ? null : doubleValue);
}
}
(Might need some tweaking though)

Resources