WPF: Got Unable to cast object exception when try to convert - wpf

I try to convert simple string and add markup string for example:
Value is: bla bla
into: Value is <Span Foreground="Red">bla bla</Span>
So i want to use MultiValueConverter and add simple converter (so far without any implementation):
public class StatusConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
Window.Resources:
<my:StatusConverter x:Key="StatusConverterToColor"/>
Usage:
<TextBlock Text="{Binding Status, Converter={StaticResource StatusConverterToColor}}" />
But got this error:
{"Unable to cast object of type
'MyApplication.classes.StatusConverter' to type
'System.Windows.Data.IValueConverter'."}
What i am doing wrong ?

Change IMultiValueConverter to IValueConverter. Declaration should be
public class StatusConverter : IValueConverter
{ ... }

Related

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

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

Binding to an array

I added a custom control library to my project and in that control there is an array DependencyProperty. Now when I try to bind that property in the client I get:
Tags of type 'PropertyArrayStart' are not supported in template sections.
The message is self explanatory but how do you set array properties in a DataTemplate?
You can use a IMultiValueConverter to build an array from multiple bindings. A converter is only required as you cannot use a MultiBinding without one.
public class MultipleValuesToArrayConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
return values.ToArray();
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}

Why does the MultiBinding not work for CornerRadius

If i use an IValueConverter it works, while with an IMultiValueConverter returning the same value it does not, why is that?
<Border Background="Red" Width="100" Height="100"
CornerRadius="{Binding Converter={vc:SingleAndMultiConverter}}" />
<Border Background="Red" Width="100" Height="100"
CornerRadius="{MultiBinding Converter={vc:SingleAndMultiConverter}}" />
public class SingleAndMultiConverter : MarkupExtension, IValueConverter, IMultiValueConverter
{
public override object ProvideValue(IServiceProvider serviceProvider)
{
return this;
}
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return Convert();
}
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return Convert();
}
private object Convert()
{
return 15;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotSupportedException();
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotSupportedException();
}
}
The multi-binding throws this error:
Value produced by BindingExpression is not valid for target property.; Value='15'
Border.CornerRadius is of type CornerRadius. Value converters should always return the right type for the property.
It is hard to tell why they behave differently, presumably there is no default value conversion using type converters when using a multi-binding for some unexplained reason. If you were to dig into the source code you might find something but it probably won't be a pleasant journey.
What H.B. said +1
[ValueConversion(typeof(object[]),typeof(CornerRadius))]
public class Multi : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
return new CornerRadius(Double.Parse("15"));
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}

WPF Ivalueconverter empty values

I use a Ivalueconverter to convert a string to a boolean using an xml datasource. This works fine until I manually change the xml like so:
myelement.InnerXml = "true"
I then receive a formatexception saying the string is not a valid boolean, I check the value that goes in to my converter and it is equal to ""
Here is my converter:
[ValueConversion(typeof(string), typeof(bool))]
public class StringToBoolConverter : IValueConverter {
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
return TypeDescriptor.GetConverter(typeof(bool)).ConvertFrom(value); }
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
return value.ToString();
}
}
I bind the converter like so:
<local:StringToBoolConverter x:Key="stringbool"></local:StringToBoolConverter>
And apply it in the binding:
IsChecked="{Binding Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, XPath=myelement, Converter={StaticResource stringbool}}"
I'm not sure, but if you use "XPath=myelement", the string should be
myCheckBox.DataContext="<myelement>true</myelement>";

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

Resources