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;
}
}
Related
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 have a property defined in my ViewModel as such:
public IEnumerable<SubsystemFilterState> CurrentFilterStates { get; private set; }
That I want to display in my view. I've done the binding to a Label and provided a converter:
XAML:
<Label Style="{StaticResource LabelFilterIndicator}"
Content="{Binding CurrentFilterStates,
Converter={StaticResource FilterConverter},
Mode=OneWay}" />
Converter:
public class FilterIndicatorConverter
: IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
try
{
var indicator = "No Filter Selected";
if (value != null)
{
IEnumerable<SubsystemFilterState> states = (IEnumerable<SubsystemFilterState>)value;
indicator = states.Count() > 1
?
"Multiple Systems"
:
states.First().Acronym.ToString();
}
return indicator;
}
catch (Exception ex)
{
return ex.Message;
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return null;
}
}
This works fine at run-time. At design-time, it's throwing an exception which is displayed in my label. Here's the exception:
Unable to cast object of type
'System.Collections.ObjectModel.ObservableCollection1[Mocks.Models_SubsystemFilterState_14_1174211]'
to type
'System.Collections.Generic.IEnumerable1[Models.SubsystemFilterState]'.
I can't figure out where this magical ObservableCollection is coming from. I don't have a Mocks namespace and have never mocked any value into that property. I changed the code in my converter to the following, to find out where they're coming from:
Quick and/or dirty:
public class FilterIndicatorConverter
: IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
try
{
var indicator = "";
if (value != null)
{
foreach (dynamic test in value as IEnumerable)
{
Type type = test.GetType();
indicator += string.Format("Start{0}" +
"AssemblyQualifiedName: {0}{6}{1}{0}" +
"Namespace: {0}{6}{2}{0}" +
"Assembly: {0}{6}{3}{0}" +
"BaseType: {0}{6}{4}{0}" +
"FullName: {0}{6}{5}{0}{0}",
"\n",
type.AssemblyQualifiedName,
type.Namespace,
type.Assembly,
type.BaseType,
type.FullName,
" ");
}
}
return indicator;
}
catch (Exception ex)
{
return ex.Message;
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return null;
}
}
Which displays this:
It looks like Visual Studio is adding these in at design time but why does it not match the type of my property? Can I turn this off? What is the DesignTools namespace?
This is an "extension" question to the 1/1/0001 question:
Original Question
The original Q. Solution -
To set the DateTime field to be nullable (DateTime?) solves this problem.
What is the solution when the underlying field must be NOT NULL ?
(In that case, the binding cause the DataGrid column Date Time Picker to set it's self to 01/01/0001)
The Actual Entity in my case is generate by Entity Framework 6.
correct me if I am wrong. You fill your not-null-field with 01/01/0001 to indicate that it's null. (not recommended)
One solution is to define a converter. something like this :
public class NotNullDateConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
var aDateTime = value as DateTime;
if (aDateTime != null && aDateTime == .... )
return null
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
Then you can define a static field to a converter to use it later in xaml.
public static NotNullDateConverter NotNullDateConverter = new NotNullDateConverter();
Then you use this convert inside your xaml for binding:
SelectedDate="{Binding Path=DueDate, Converter={x:Static local:SomeClass.NotNullDateConverter}}"
I had the exact same problem, so I wrote a converter that could weed out non-dates.
//Just weeds out non-dates. Format should be set with StringFormat on the binding
public class DateConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
DateTime date = (DateTime)value;
if (date != null && date.Year != 1)
{
return date;
}
return null;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
I use it like so:
<DataTemplate.Resources>
<logic:DateConverter x:Key="DateConverter"/>
</DataTemplate.Resources>
<TextBlock Text="{Binding LastUpdateTime, StringFormat={}{0:MM/dd/yyyy H:mm:ss}, Converter={StaticResource DateConverter}}"/>
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;
}
}
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).