To convert Enums to Icons I use a value converter like that:
public class IconConverter : IValueConverter
{
public ResourceDictionary Items { get; set; }
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
string key = Enum.GetName(value.GetType(), value);
return Items[key];
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
I use it in my XAML like this:
<UserControl.Resources>
<local:IconConverter x:Key="IconConverter">
<ResourceDictionary Source="/Leister.WPFControls;component/ButtonStyles.xaml" />
</local:IconConverter>
</UserControl.Resources>
When I start the application all works fine, the Converter converts the Name of a value and gets the Icon from the ResourceDictionary by its key. But in my Designer, Visual Studio 2010 alwas complains:
The object of type System.Windows.ResourceDictionary" can not be cast to type "Microsoft.Expression.DesignModel.DocumentModel.DocumentNode".
at Microsoft.Expression.DesignModel.Core.InstanceBuilderOperations.SetValue(Object target, IProperty propertyKey, Object value)
at Microsoft.Expression.DesignModel.InstanceBuilders.ClrObjectInstanceBuilder.ModifyValue(IInstanceBuilderContext context, ViewNode target, IProperty propertyKey, Object value, PropertyModification modification)
at Microsoft.Expression.DesignModel.InstanceBuilders.ClrObjectInstanceBuilder.UpdateProperty(IInstanceBuilderContext context, ViewNode viewNode, IProperty propertyKey, DocumentNode valueNode)
This is anoying! Any idea? Is there a simpler solution to convert Enums to XAML-Icon Resources?
I know its late but maybe the ContentPropertyAttribute would help the designer:
[ContentProperty("Items")]
public class IconConverter : IValueConverter
{
public ResourceDictionary Items { get; set; }
http://msdn.microsoft.com/en-us/library/system.windows.markup.contentpropertyattribute.aspx
Related
In WPF one has the possibility to use a converter in binding, so that one can bind for instance a Visibility property of a control to a Boolean property in the view model.
For this specific pairing (Visibility and Boolean) WPF does offer an out-of-the-box converter called BooleanToVisibilityConverter.
But let's say I'd like to bind a Boolean property of a control to a Visibility property in the view model. Is there any way to use the standard BooleanToVisibilityConverter and tell the binding to invert it (to use ConvertBack instead on Convert and vice versa)?
Or do I have to write another converter for that case?
So, there is no built-in way of inverting the converter. We can, however, work around that by introducing a "shim" converter like this one:
public class InverterConverter : IValueConverter
{
public IValueConverter Converter { get; set; }
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return Converter.ConvertBack(value, targetType, parameter, culture);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return Converter.Convert(value, targetType, parameter, culture);
}
}
With the usage as follows:
<ContentControl>
<ContentControl.Content>
<Binding>
<Binding.Converter>
<InverterConverter Converter="{StaticResource YourConverter}" />
</Binding.Converter>
</Binding>
</ContentControl.Content>
</ContentControl>
This, obviously, is some heavy syntax but we can simplify it with this little markup extension:
public class InvertedExtension : MarkupExtension
{
public IValueConverter Converter { get; set; }
public InvertedExtension(IValueConverter converter)
{
Converter = new InverterConverter() { Converter = converter };
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
return Converter;
}
}
<ContentControl Content="{Binding Converter={Inverted {StaticResource MyConverter}}}" />
Is there any way to use the standard BooleanToVisibilityConverter and tell the binding to invert it (to use ConvertBack instead on Convert and vice versa)?
No.
Or do I have to write another converter for that case?
Yes.
You could implement a generic converter that accepts "true" and a "false" values of any type:
public class BooleanConverter<T> : IValueConverter
{
public T True { get; set; }
public T False { get; set; }
public virtual object Convert(object value, Type targetType, object parameter, CultureInfo culture) =>
value is bool && ((bool)value) ? True : False;
public virtual object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) =>
value is T && EqualityComparer<T>.Default.Equals((T)value, True);
}
...and derive from this for each type that you want to handle:
public class BooleanToVisibilityNegationConverter : BooleanConverter<Visibility>
{
public BooleanToVisibilityNegationConverter()
: base()
{
True = Visibility.Hidden;
False = Visibility.Visible;
}
}
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);
}
I need to define a DependencyProperty in a converter class because I need this data to make the conversion and this data is in another object, not the one I'm binding to.
My converter class is the following:
public class LEGOMaterialConverter : DependencyObject, IValueConverter
{
public DependencyProperty MaterialsListProperty = DependencyProperty.Register("MaterialsList", typeof(Dictionary<int, LEGOMaterial>), typeof(LEGOMaterialConverter));
public Dictionary<int, LEGOMaterial> MaterialsList
{
get
{
return (Dictionary<int, LEGOMaterial>)GetValue(MaterialsListProperty);
}
set
{
SetValue(MaterialsListProperty, value);
}
}
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
LEGOMaterial material = null;
MaterialsList.TryGetValue((int)value, out material);
return material;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Then I'm instanciating it on the Window.REsources area:
<Window.Resources>
<local:LEGOMaterialConverter x:Key="MaterialsConverter" MaterialsList="{Binding Path=Materials}" />
</Window.Resources>
I'm getting the following error:
'MaterialsList' property was already registered by 'LEGOMaterialConverter'.
Does anyone have a clue on this error?
Try doing it like this (just an example):
public class ValueConverterWithProperties : MarkupExtension, IValueConverter
{
public int TrueValue { get; set; }
public override object ProvideValue(IServiceProvider serviceProvider)
{
return this;
}
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if ((int) value == TrueValue)
{
return true;
}
return false;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
notice I derive from markup extension to allow me to use it like this:
<Window x:Class="Converter.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:converter="clr-namespace:Converter"
Title="MainWindow" Height="350" Width="525">
<Grid>
<CheckBox IsChecked="{Binding item, Converter={converter:ValueConverterWithProperties TrueValue=5}}"></CheckBox>
<CheckBox IsChecked="{Binding item2, Converter={converter:ValueConverterWithProperties TrueValue=10}}"></CheckBox>
</Grid>
Btw, this error is caused by your dependency property in the converter not being static (and after having created an instance of this converter somewhere before).
EDIT
So the problem is with this line:
public DependencyProperty MaterialsListProperty = DependencyProperty.Register("MaterialsList", typeof(Dictionary<int, LEGOMaterial>), typeof(LEGOMaterialConverter));
Here the Dependency Property MaterialsListProperty is being registered with every instantiation of an object of this type (i.e. LEGOMaterialConverter).
However Dependency Properties should be defined static, like so:
public static readonly DependencyProperty MaterialsListProperty = DependencyProperty.Register("MaterialsList", typeof(Dictionary<int, LEGOMaterial>), typeof(LEGOMaterialConverter));
A static variable is initialized (and the Dependency Property is registered) only once for all future instances of this type and that is what we need here. Failing to declare a Dependency Property as static results in the error from above.
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.
I have a control that I want to show/hide, depending on the value of a boolean.
I have a NegatedBooleanConverter (switches true to false and vice versa) and I need to run this converter first.
I have a BooleanToVisibilityConverter and I need to run this converter after the NegatedBoolConverter.
How can I fix this problem? I want to do this in XAML.
edit: this is a possible solution.
That doesn't seem to work. It first converts the value with the separate converters and then does something with the converted values.
What I need is:
Convert the value with the first converter (this gives convertedValue).
Convert convertedValue with the second converter and it's this result that I need.
This is what I did:
public class CombiningConverter : IValueConverter
{
public IValueConverter Converter1 { get; set; }
public IValueConverter Converter2 { get; set; }
public object Convert(
object value, Type targetType, object parameter, CultureInfo culture)
{
object convertedValue =
Converter1.Convert(value, targetType, parameter, culture);
return Converter2.Convert(
convertedValue, targetType, parameter, culture);
}
public object ConvertBack(
object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
and I call it like this:
<converters:CombiningConverter
x:Key="negatedBoolToVisibilityConverter"
Converter1="{StaticResource NegatedBooleanConverter}"
Converter2="{StaticResource BoolToVisibilityConverter}" />
A MultiValueConverter might also be possible I think. Maybe I'll try that later.
Expanding on Natrium's great answer...
XAML
<conv:ConverterChain x:Key="convBoolToInverseToVisibility">
<conv:BoolToInverseConverter />
<BooleanToVisibilityConverter />
</conv:ConverterChain>
Class
/// <summary>Represents a chain of <see cref="IValueConverter"/>s to be executed in succession.</summary>
[ContentProperty("Converters")]
[ContentWrapper(typeof(ValueConverterCollection))]
public class ConverterChain : IValueConverter
{
private readonly ValueConverterCollection _converters= new ValueConverterCollection();
/// <summary>Gets the converters to execute.</summary>
public ValueConverterCollection Converters
{
get { return _converters; }
}
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return Converters
.Aggregate(value, (current, converter) => converter.Convert(current, targetType, parameter, culture));
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return Converters
.Reverse()
.Aggregate(value, (current, converter) => converter.Convert(current, targetType, parameter, culture));
}
#endregion
}
/// <summary>Represents a collection of <see cref="IValueConverter"/>s.</summary>
public sealed class ValueConverterCollection : Collection<IValueConverter> { }
In this case, you don't need a converter chain. You just need a configurable converter. This is similar to Carlo's answer above, but explicitly defines the true and false values (which means you can use the same converters for Hidden, Visible or Collapsed conversions).
[ValueConversion(typeof(bool), typeof(Visibility))]
public class BoolToVisibilityConverter : IValueConverter
{
public Visibility TrueValue { get; set; }
public Visibility FalseValue { get; set; }
public BoolToVisibilityConverter()
{
// set defaults
FalseValue = Visibility.Hidden;
TrueValue = Visibility.Visible;
}
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return (bool)value ? TrueValue : FalseValue;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Then in XAML:
<BoolToVisibilityConverter x:Key="BoolToVisibleConverter"
FalseValue="Hidden"
TrueValue="Visible" />
What we do in our project is make a regular BooleanToVisibilityConverter, said converter takes one parameter (anything at all, a string, an int, bool, whatever). If the parameter is set it inverts the result, if not, it spits out the regular result.
public class BooleanToVisibilityConverter : IValueConverter
{
#region IValueConverter Members
public object Convert(object value, System.Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
bool? isVisible = value as bool?;
if (parameter != null && isVisible.HasValue)
isVisible = !isVisible;
if (isVisible.HasValue && isVisible.Value == true)
return Visibility.Visible;
else
return Visibility.Collapsed;
}
public object ConvertBack(object value, System.Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new System.NotImplementedException();
}
#endregion
}
To answer my own question again: I have been using this solution for years now:
Piping Value Converters in WPF - CodeProject
It makes a new converter of 2 existing converters, calling the first one first, and then the second one etc etc.
I'm pretty pleased with this solution.
At this point I'd like to suggest ValueConverters.NET (NuGet) which has a ton of useful ValueConverters, including the ValueConverterGroup that can be used to combine ValueConverters.
BoolToValueConverters also offer fields to define the TrueValue, FalseValue as well as if the input IsInverted, so a ValueConverterGroup is not even necessary in most cases.
Just to illustrate how easy the life can get, here is a sample demonstration which shows a converter that displays an element if the binding is not null:
<Window ...
xmlns:vc="clr-namespace:ValueConverters;assembly=ValueConverters"
...>
...
<vc:ValueConverterGroup x:Key="IsNotNullToVisibilityConverter">
<vc:NullToBoolConverter IsInverted="True" />
<vc:BoolToVisibilityConverter />
</vc:ValueConverterGroup>
ValueConverters are a prime example of reinventions of the wheel in a lot of WPF applications. Why it has to be?
Also complex things can often be solved with StyleTriggers or within the ViewModels logic itself.
It almost never happens that I need to build a custom converter. In my opinion WPF has enough engineer requirements already.
Personally I would just make 1 single converter that does the full conversion. Unless you desperately need the converters (like the negation) in other places, it will be easier to maintain (imo) if the conversion is done once, in one place.
I think you may want to use a Multiconverter here instead of two separate converters. You should be able to reuse the logic from your existing converters. Check out this discussion for a start.
To address this specific problem, instead of using two converters your could write your own BoolToVisibilityConverter that uses the ConverterParameter (as a bool) to determine whether or not to negate the original Boolean.
I've just created what I've called ReversedBooleanToVisibilityConverter to basically do what those 2 would do for you but in one step.
Here is a combination of Natrium and metao answers to save you some time:
public class ComparisonConverter : IValueConverter
{
public object TrueValue { get; set; } = true;
public object FalseValue { get; set; } = false;
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return value?.Equals(parameter) == true? TrueValue : FalseValue;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return value?.Equals(TrueValue) == true ? parameter : Binding.DoNothing;
}
}
And how you use it:
<converter:ComparisonConverter x:Key="ComparisonConverter" />
<converter:ComparisonConverter TrueValue="{x:Static Visibility.Visible}"
FalseValue="{x:Static Visibility.Collapsed}"
x:Key="ComparisonToVisibilityConverter" />
...
<RadioButton IsChecked="{Binding Type, ConverterParameter={x:Static entities:LimitType.MinMax}, Converter={StaticResource ComparisonConverter}}"/>
<TextBox Visibility="{Binding Type, ConverterParameter={x:Static entities:LimitType.MinMax}, Converter={StaticResource ComparisonToVisibilityConverter}}"/>