Silverlight 3 - binding enum to combobox from DomainServiceContext - silverlight

I just started playing around with Silverlight and a supposedly simple thing like binding a Combobox is driving me nuts. I read a bunch of articles now but none really address the issue that I'm after or were made for Silverlight 2 and don't seem to work.
Let's say I have an entity object "User" which has a "UserStatus" field. In the database UserStatus field is defined as byte and in code it's defined as:
public enum UserStatus : byte
{
Active = 1,
Locked = 2,
Suspended = 3,
}
When ADO.NET entity framework creates the user entity it leaves the UserStatus field as byte. So, to address this I stumbled across IValueConverter and implemented the following:
public class EnumConverter : IValueConverter
{
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
switch (parameter.ToString())
{
case "UserStatus":
return ((UserStatus)value).ToString();;
}
return "?";
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
#endregion
}
Now, I also need to supply the Combobox an ItemSource, so I implemented this:
internal static class EnumValueCache
{
private static readonly IDictionary<Type, object[]> Cache = new Dictionary<Type, object[]>();
public static object[] GetValues(Type type)
{
if (!type.IsEnum)
throw new ArgumentException("Type '" + type.Name + "' is not an enum");
object[] values;
if (!Cache.TryGetValue(type, out values))
{
values = type.GetFields()
.Where(f => f.IsLiteral)
.Select(f => f.GetValue(null))
.ToArray();
Cache[type] = values;
}
return values;
}
}
public class EnumValuesConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value == null)
return null;
switch (parameter.ToString())
{
case "UserStatus":
return EnumValueCache.GetValues(typeof(UserStatus));
}
return null;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
And then finally, this is how I added it to my XAML:
<ComboBox ItemsSource="{Binding UserStatus, Mode=TwoWay, Converter={StaticResource EnumValuesConverter}, ConverterParameter='UserStatus'}"
SelectedItem="{Binding UserStatus, Mode=TwoWay, Converter={StaticResource EnumConverter}, ConverterParameter='UserStatus'}" />
What happens now though is that the ItemsSource gets correctly bound and I see all the options in the dropdown, however, the SelectedItem is not set.
Even when I try to manually set SelectedItem to ="1" or "Active", none of them work.
Can anyone help me out and tell me what's wrong, why I can't seem to get the SelectedItem set?
Thanks,
Tom

I can see two problems with your code.
First, in silverlight 3 the ComboBox matches the object in the SelectedItem to the set of objects in the ItemsSource via the object.Equals method. However your GetValues method returns an array of Boxed enum values. Whereas your EnumConverter returns a string. Hence you asking Silverlight to compare a byte with a string, these are never equal.
Secondly, you need to place some code in the ConvertBack method if you are going to two way bind the SelectedItem (BTW there is no need for a twoway binding the ItemsSource).

Related

WPF MVVM binding with specific types

I'm trying to implement the MVVM design pattern in my WPF application but I have some problems to bind my Views with my ViewModels.
In one of my ViewModels, I have the following property :
public IPEndPoint EndPoint
{
get { return _serverInfos.EndPoint; }
private set
{
_serverInfos.EndPoint = value;
RaisePropertyChanged("EndPoint");
}
}
I want to bind this property in the related View like that :
<TextBox Text="{Binding EndPoint.Address}" />
<TextBox Text="{Binding EndPoint.Port}" />
The EndPoint.Port binding works as expected but the other one doesn't because EndPoint.Address is not a string (it's an IPAddress). Of course, I could define two string properties instead of one IPEndPoint but I think that it's not a good solution.
I also have the same problem using Enums when I want to convert them into int :
<ComboBox SelectedIndex="{Binding MyEnumProperty}" />
How could I solve these problems ?
Thank you for your help.
Normally you want a view model to take things from the model and expose them in a way that the view can consume. As such
MyEnumProperty should be an System.Int32 (int) for ComboBox.SelectedIndex to consumer
You should probably implement two separate properties for EndPointPort and EndPointAddress, and EndPointAddress should be a string that converts to an IPAddress when working with the model
You can use IValueConverters for both of those, but then you are reducing some of the utility of a separate view model in the first place if all it does is act like the model.
A converter you can use for converting between IPAddress and string.
public class IPAddressConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var ipAddress = value as IPAddress;
if (ipAddress != null)
{
return ipAddress.ToString();
}
return DependencyProperty.UnsetValue;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
var text = value as string;
IPAddress ipAddress;
if (text != null && IPAddress.TryParse(text, out ipAddress))
{
return ipAddress;
}
return DependencyProperty.UnsetValue;
}
}
And then in a ResourceDictionary or a Resources collection of a FrameworkElement
<IPAddressConverter x:Key="IpAddressConverter" />
And in the binding:
<TextBox Text="{Binding EndPoint.Address, Converter={StaticResource IpAddressConverter}}" />
As far as the IPEndPoint type, this is a perfect case for a type converter (IValueConverter). It would look something like this, assuming your type has a valid ToString implementation:
public class IPEndPointConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
IPEndPoint endPoint = (IPEndPoint)value;
return endPoint.ToString();
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
You could then add the converter to your XAML file with a ResourceDictionary as follows (assuming you've added the relevant namespace, here called "converters", to your XAML):
<Window.Resources>
<converters:IPEndPointConverter x:Key="ipEndPointConverter" />
</Window.Resources>
You would then simply use this anywhere you need it within your XAML by adding it to the binding:
<TextBox Text="{Binding Path=EndPoint.Address, Converter={StaticResource ResourceKey=ipEndPointConverter}}" />

WPF AutoCompleteBox Multiple ValueMemberPath

I have a CollectionViewSource populated with business objects from a database. Setting
the AutoCompleteBox ValueMemberPath="LNAME" works as intended for all last names. However, I would like to search first name and order number concurrently without having to resort to radio buttons or a dropdown to define search type.
I have changed ValueMemberPath to ValueMemberBinding:
ItemsSource="{Binding Source={StaticResource TheCollectionViewSource}}"
ValueMemberBinding="{Binding Converter={StaticResource ValueMemberPathConverter}}"
I am not sure how to combine the LNAME, FNAME etc in the converter
public class Converter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return foo;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return foo;
}
}
public Binding ValueMemberBinding
{
get
{
return _valueBindingEvaluator != null ?
_valueBindingEvaluator.ValueBinding : null;
}
set
{
if (_valueBindingEvaluator == null)
{
_valueBindingEvaluator = new BindingEvaluator<string>();
AddLogicalChild(_valueBindingEvaluator);
}
_valueBindingEvaluator.ValueBinding = value;
}
}

Nested binding and piped conversion

To have less redundant XAML markup i try to get a radiobutton-type selection control to be populated generically, i.e. i use an ItemsControl with an enum as ItemsSource and create a DataTemplate which shows which item is selected by checking whether the enum value of the item is the same as the current setting.
This alone cannot be done using a simple converter or DataTrigger because two bindings are needed, so i created a generic MutliValueConverter to check for equality:
<CheckBox.Visibility>
<MultiBinding Converter="{StaticResource EqualityComparisonConv}">
<Binding Path="Key"/>
<Binding Path="DisplayMode_Current" Source="{x:Static local:App.Settings}"/>
</MultiBinding>
</CheckBox.Visibility>
public class EqualityComparisonConverter : IMultiValueConverter
{
#region IMultiValueConverter Members
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values.Length < 2) throw new Exception("At least two inputs are needed for comparison");
bool output = (bool)values.Skip(1).Aggregate(values[0], (x1, x2) => { return x1.Equals(x2); });
return output;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
#endregion
}
The obvious problem here is that the converter returns a boolean which i would need to convert to Visibility first.
Wrapping the MultiBinding in another binding with just a converter does not work because the properties are not dependency properties (hence cannot have a binding assigned to them). I could think of a few workarounds like storing the bool in some Tag property, so i can use that as a new binding source, but i would be more interested in something like this:
<CheckBox.Visibility>
<local:PipeConverter Converter="{StaticResource BooleanToVisibilityConv}">
<MultiBinding Converter="{StaticResource EqualityComparisonConv}">
<Binding Path="Key"/>
<Binding Path="DisplayMode_Current" Source="{x:Static local:App.Settings}"/>
</MultiBinding>
</local:PipeConverter>
</CheckBox.Visibility>
This class would need to update its output when changes in the original binding occur and it would need to be able to expose its output value to the Visibility property but i do not know how to achieve either. One problem one runs into is that there is a need for dependency properties so inheriting from DependencyObject would be nice, but inheriting from a Binding class would also make sense because the PipeConverter should bind and needs to be set as the value of another dependency property.
Based on the idea (see publicgk's answer, option C) that one can create a converter which contains a collection of converters which internally are used in sequence i wrote a shoddy implementation which fits my needs. i.e. i can use a MultiValueConverter at the beginning and pipe the output into a list of normal converters:
[ContentProperty("Converters")]
public class GroupConverter : IValueConverter, IMultiValueConverter
{
private IMultiValueConverter _multiValueConverter;
public IMultiValueConverter MultiValueConverter
{
get { return _multiValueConverter; }
set { _multiValueConverter = value; }
}
private List<IValueConverter> _converters = new List<IValueConverter>();
public List<IValueConverter> Converters
{
get { return _converters; }
set { _converters = value; }
}
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return GroupConvert(value, Converters);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return GroupConvertBack(value, Converters.ToArray().Reverse());
}
private static object GroupConvert(object value, IEnumerable<IValueConverter> converters)
{
return converters.Aggregate(value, (acc, conv) => { return conv.Convert(acc, typeof(object), null, null); });
}
private static object GroupConvertBack(object value, IEnumerable<IValueConverter> converters)
{
return converters.Aggregate(value, (acc, conv) => { return conv.ConvertBack(acc, typeof(object), null, null); });
}
#endregion
#region IMultiValueConverter Members
private InvalidOperationException _multiValueConverterUnsetException =
new InvalidOperationException("To use the converter as a MultiValueConverter the MultiValueConverter property needs to be set.");
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (MultiValueConverter == null) throw _multiValueConverterUnsetException;
var firstConvertedValue = MultiValueConverter.Convert(values, targetType, parameter, culture);
return GroupConvert(firstConvertedValue, Converters);
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
if (MultiValueConverter == null) throw _multiValueConverterUnsetException;
var tailConverted = GroupConvertBack(value, Converters.ToArray().Reverse());
return MultiValueConverter.ConvertBack(tailConverted, targetTypes, parameter, culture);
}
#endregion
}
(As you can see i pretty much completely disregard the ConverterParameters, TargetTypes and CultureInfo parameters, further the ConvertBack methods are untested so i do not advise anyone to actually use this.)
XAML usage:
<vc:GroupConverter MultiValueConverter="{StaticResource EqualityComparisonConv}">
<StaticResource ResourceKey="BoolToVisibilityConv"/>
</vc:GroupConverter>
Three options:
Option A: Convert your bool to visibility in your multivalueconverter (its just one line)
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values.Length < 2)
throw new Exception("At least two inputs are needed for comparison");
bool output = (bool)values.Skip(1).Aggregate(values[0], (x1, x2) =>
{ return x1.Equals(x2); });
return output ? Visibility.Visible : Visibility.Collapsed;
}
Option B: Use the existing booltovisibilityconverter programatically
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values.Length < 2)
throw new Exception("At least two inputs are needed for comparison");
bool output = (bool)values.Skip(1).Aggregate(values[0], (x1, x2) =>
{ return x1.Equals(x2); });
System.Windows.Controls.BooleanToVisibilityConverter booltovisibilityconverter = new System.Windows.Controls.BooleanToVisibilityConverter();
return booltovisibilityconverter.Convert(output, System.Type.GetType("System.Boolean"), parameter, culture);
}
PS: You may want to cache the booltovisibilityconverter instead of creating it everytime.
Option C: Pipe your converters
Piping Value Converters in WPF
PS: Beaware that this article is quite old.

Is there an easy solution to show Yes and No in a silverlight combobox and bind to database?

I have a bit defined in my database 0=no, 1=yes. I have a silverlight combo with the values "Yes" and "No" in it. How can I bind my bit value to the combo?
You don't state what data access machinary you are using but the typical tools will expose a bit field as a boolean property. The easiest approach would be to use a value converter.
Here is the basic idea (might need some more defensive coding):-
public class BoolToStringConverter : IValueConverter
{
public String FalseString { get; set; }
public String TrueString { get; set; }
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value == null)
return FalseString;
else
return (bool)value ? TrueString : FalseString;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return value.Equals(TrueString);
}
}
With that in your application you can now add it to a Resources property (typically the App.xaml)
<Resources>
<local:BoolToStringConverter x:Key="CvtYesNo" FalseString="No" TrueString="Yes" />
</Resources>
Now you would create your combobox like this:-
<ComboBox SelectedItem="{Binding YourBitField, Converter={StaticResource CvtYesNo}, Mode=TwoWay}">
<sys:String>Yes<sys:String>
<sys:String>No<sys:String>
</ComboBox>

WPF: how to use 2 converters in 1 binding?

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}}"/>

Resources