Binding to double field with validation - wpf

I'm trying to bind TextBox to double property of some object with UpdateSourceTrigger=PropertyChanged. The goal is to immediately during editing validate entered value to be in allowed range (and display an error if not). I want to implement validation on Model level, i.e. via IDataErrorInfo.
All works great when I bind to int property, but if property is double then a frustrating editing behavior appears: after erasing last significant digit in fractional part of number - the decimal separator is automatically erased (with all possible fractional zeroes). For example, after erasing digit '3' from number '12.03' the text is changed to '12' instead of '12.0'.
Please, help.
Here is the sample code:
MainWindow.xaml:
<Window x:Class="BindWithValidation.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="80" Width="200" WindowStartupLocation="CenterOwner">
<StackPanel>
<TextBox Width="100" Margin="10" Text="{Binding DoubleField, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}">
<TextBox.Style>
<Style TargetType="TextBox">
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="ToolTip" Value="{Binding RelativeSource={RelativeSource Self}, Path=(Validation.Errors)[0].ErrorContent}"/>
</Trigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
</StackPanel>
</Window>
MainWindow.xaml.cs:
namespace BindWithValidation
{
public partial class MainWindow : Window
{
private UISimpleData _uiData = new UISimpleData();
public MainWindow()
{
InitializeComponent();
DataContext = _uiData;
}
}
}
UISimpleData.cs:
namespace BindWithValidation
{
public class UISimpleData : INotifyPropertyChanged, IDataErrorInfo
{
private double _doubleField = 12.03;
public double DoubleField
{
get
{
return _doubleField;
}
set
{
if (_doubleField == value)
return;
_doubleField = value;
RaisePropertyChanged("DoubleField");
}
}
public string this[string propertyName]
{
get
{
string validationResult = null;
switch (propertyName)
{
case "DoubleField":
{
if (DoubleField < 2 || DoubleField > 5)
validationResult = "DoubleField is out of range";
break;
}
default:
throw new ApplicationException("Unknown Property being validated on UIData");
}
return validationResult;
}
}
public string Error { get { return "not implemented"; } }
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string property)
{
if ( PropertyChanged != null )
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
}

The behavior of binding float values to a textbox has been changed
from .NET 4 to 4.5. With .NET 4.5 it is no longer possible to enter a
separator character (comma or dot) with ‘UpdateSourceTrigger =
PropertyChanged’ by default.
Microsoft says, this (is) intended
If you still want to use ‘UpdateSourceTrigger = PropertyChanged’, you
can force the .NET 4 behavior in your .NET 4.5 application by adding
the following line of code to the constructor of your App.xaml.cs:
public App()
{
System.Windows.FrameworkCompatibilityPreferences
.KeepTextBoxDisplaySynchronizedWithTextProperty = false;
}
(Sebastian Lux - Copied verbatim from here)

I realize I'm a little late to the party but I found a (I think) rather clean solution to this problem.
A clever converter that remembers the last string converted to double and returns that if it exists should do everything you want.
Note that when the user changes the contents of the textbox, ConvertBack will store the string the user input, parse the string for a double, and pass that value to the view model. Immediately after, Convert is called to display the newly changed value. At this point, the stored string is not null and will be returned.
If the application instead of the user causes the double to change only Convert is called. This means that the cached string will be null and a standard ToString() will be called on the double.
In this way, the user avoids strange surprises when modifying the contents of the textbox but the application can still trigger a change.
public class DoubleToPersistantStringConverter : IValueConverter
{
private string lastConvertBackString;
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (!(value is double)) return null;
var stringValue = lastConvertBackString ?? value.ToString();
lastConvertBackString = null;
return stringValue;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (!(value is string)) return null;
double result;
if (double.TryParse((string)value, out result))
{
lastConvertBackString = (string)value;
return result;
}
return null;
}
}

Tried formatting the value with decimal places?
It may be weird though since you will then always have N decimal places.
<TextBox.Text>
<Binding Path="DoubleField" StringFormat="{}{0:0.00}" UpdateSourceTrigger="PropertyChanged" ValidatesOnDataErrors="True"/>
</TextBox.Text>
If having fixed decimal places is not good enough, you may have to write a converter that treats the value as a string and converts it back to a double.

The problem is that you are updating your property every time the value changes. When you change 12.03 to 12.0 it is rounded to 12.
You can see changes by providing delay by changing the TextBox in xaml like this
<TextBox Width="100" Margin="10" Text="{Binding DoubleField, UpdateSourceTrigger=PropertyChanged,Delay=500, ValidatesOnDataErrors=True}">
but delay will notify and set the property after the delay time in mili sec.
Better use StringFormat like this
<TextBox Width="100" Margin="10" Text="{Binding DoubleField, UpdateSourceTrigger=PropertyChanged,StringFormat=N2, ValidatesOnDataErrors=True}">

I have run into the same problem, and have found a quite simple solution: use a custom validator, which does not return "valid" when the Text ends in "." or "0":
double val = 0;
string tmp = value.ToString();
if (tmp.EndsWith(",") || tmp.EndsWith("0") || tmp.EndsWith("."))
{
return new ValidationResult(false, "Enter another digit, or delete the last one.");
}
else
{
return ValidationResult.ValidResult;
}

Try using StringFormat on your binding:
<TextBox Width="100" Margin="10" Text="{Binding DoubleField, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True, StringFormat='0.0'}">
Not sure if that string format is even right since I've not done one for a while but it's just an example

Related

Possible to use constant/static value inside a binding in XAML?

We are using the following mechanism/syntax to bind commands in XAML:
Command="{Binding CommandAggregator[FooCmd], Mode=OneTime}"
Here, CommandAggregator is an object that you can use an indexer (with string parameter) on to get back the actual command.
The command registrations with the aggregator are bugging me a bit, because we are still using magic strings for the command names like this:
this.CommandAggregator.SetCommand("FooCmd", new RelayCommand(execute, canExecute));
While I don't necessarily like this whole process, I cannot change much. The one thing I would like to do for now is quit using magic strings by making them constants or static readonly string objects inside a static CommandName class.
But is it possible to define the binding inside the XAML and reference the constant (say CommandName.Foo)? I thought about using {x:Static ...}, but I don't know how to get the returned value into the indexer.
you can implement an IValueConverter which will return a command from CommandAggregator based on converter Parameter:
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var aggregator = value as CommandAggregator;
var cmd = parameter as string;
if (aggregator != null && cmd != null)
return aggregator[cmd];
return null;
}
and pass the parameter from xaml:
Command="{Binding Path=CommandAggregator,
Converter={StaticResource MyConverter},
ConverterParameter={x:Static Constants.FooCmd},
Mode=OneTime}"
Not sure if this is the best, but it works for me. I didn't like so many CmdWhatEvers for my Relay commands showing up in my debugger so moved them to a dictionary, which seems similar to your CommandAggregator, then indexed them with a static string (magic strings have caused so many binding failures for me.)
Anyway, my string constants live in a static class
internal static class Str
{
public static readonly string CmdReset = "CmdReset";
}
In the View Model
internal class CtrlVm : ViewModelBase
{
public Dictionary<string, IRelayCommand> Commands { get; }
public CtrlVm()
{
Commands = new Dictionary<string, IRelayCommand>()
{
// My relay command class takes, Execute, CanExecute
// and a Header parameter, that I use in binding
Str.CmdReset, new RelayCommand(Reset, CanReset, "Reset");
},
}
private bool CanReset(object parameter)
{
return bHasChanges; // or whatever
}
private void Reset(object parameter)
{
// do the reset work
}
}
Then in the Xaml
<Button Command= "{Binding Commands[CmdReset]}"
Content= "{Binding Commands[CmdReset].Header}"/>

How do I detect if a property of type int is valid if the user deletes the value

How do I detect if a property of type int is valid if the user deletes the value.
My application has a textbox and a button. If the value in the textbox is valid, the button is shown, if not, the button is invisible. In the constructor, the button is set to invisible.
I'm using binding and my property is
private int _myProperty;
public int MyProperty
{
get { return _myProperty; }
set
{
//various logic about if the value parses validation (if else statements), if it fails the value is set back to 0, the visibility of the button is made invisible and the code returns at this point
this._myProperty = value;
OnPropertyChanged("MyProperty");
this.MyVis = System.Windows.Visibility.Visible;// it'valid
}
}
Now, if I type in something like "12A", the code inside the setter will simply detect it's not a valid string, set this._myProperty = 0 and then return (which is fine for my needs!)
The problem I have is, deleting! I am allowed to delete the content of the textbox but when I do, the code doesn't hit setter! Therefore the logic inside the property is never hit and my button's visibility remains untouched (So it could remain visible). And when I click thebuttonit shows the value 0 where as 0 is not what the user has entered (the textbox is null). I'd rather show my invalid numbers as -99 or null (usingint?but even changing my properties to typeint?` does not affect this behaviour)
I'm now beginning to think using integer with textboxes is a bad thing in WPF but, it's not realistic to show my integers as strings and do various casting etc (although I guess it would fix it, I don't consider this a real fix)
Not only that but my range goes from 0 to a very large number and as such, a combobox or a dial is not a suitable solution to the UX.
If I change MyProperty to type string, the setter value is set when I hit delete! But as it's a number I'd like to keep the property as type int. I don't know what I've done wrong.
This is a typical case for Commands.
Your XAML should look like:
<TextBox x:Name="SomeNumberTextBlock"/>
<Button Command="{Binding CmdToDoSomething, Mode=OneWay}" CommandParameter="{Binding ElementName=SomeNumberTextBlock, Path=Text, , UpdateSourceTrigger=PropertyChanged, Mode=OneWay}"/>
Your ViewModel:
#region command ToDoSomething
private DelegateCommand _cmdToDoSomething;
public DelegateCommand CmdToDoSomething {
get { return _cmdToDoSomething ?? (_cmdToDoSomething = new DelegateCommand(ToDoSomething, CanToDoSomething)); }
}
private void ToDoSomething(Object parameter) {
//button logic
}
private bool CanToDoSomething(Object parameter) {
int dummyInt;
return int.TryParse((String)parameter, out dummyInt);
}
#endregion
This will disable the Button when the input is not right.
If you really want to hide it:
<Button Command="{Binding CmdToDoSomething, Mode=OneWay}" CommandParameter="{Binding ElementName=SomeNumberTextBlock, Path=Text, Mode=OneWay}">
<Button.Style>
<Style.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Visibility" Value="Collapsed"/>
</Trigger>
</Style.Triggers>
</Button.Style>
</Button>
NB: I'm using DelegateCommand (you can found it here) but any type of Command will do.
One of possible solutions is create private field with type int and public property with type string. In setter you can convert string to int - and will know if your input string is not correct.
Another just prevent input incorrect character.
private int? _myProperty = null;
public string MyProperty
{
get { return _myProperty==null ? "" : _myProperty.ToString(); }
set
{
int number = 0;
if (Int32.TryParse(value, out number))
_myProperty = number;
else
{
// you can generate exception and
// use ExceptionValidationRule in Binding.ValidationRules
// in such case WPF will show red border around your textBox
_myProperty = null;
}
OnPropertyChanged("MyProperty");
// If you use MVVM than more properly will create converter
// {Binding MyProperty, Converter=StringToIsEnabledConverter}
// instead of dirrectly set visibility
this.MyVis = System.Windows.Visibility.Visible;// it'valid
}
}

Binding float data type with UpdateSourceTrigger set toPropertyChanged in WPF

I have a problem with Float data type while using UpdateSourceTrigger in WPF.I have a property with float data type and it is binded it to a TextBox and set its UpdateSourceTrigger of the binding to PropertyChanged,but WPF dosen't let me type '.' in the TextBox unless i change UpdateSourceTrigger to LostFocus.I think it's because of we can not type '.' in the end of float value.I don't have any idea how can i fix it because i need to type '.' and set UpdateSourceTrigger to PropertyChanged.
I have designed my textbox in such a way that it can take only 7 characters
for example
1) 12.3456
2) 1234.56 e.t.c
The property is:`
public float? Expenditure
{
get;set;
}
And in the XAML:
<TextBox Text="{Binding Expenditure, UpdateSourceTrigger=PropertyChanged}"/>
StringFormat does not help as decimal can be put anywhere.
Any help would be great.
Just change StringFormat property of the binding to display two decimal places of the property:
<TextBox Text="{Binding Expenditure, UpdateSourceTrigger=PropertyChanged, StringFormat='{}{0:F2}'}"/>
Also you can write a custom FloatToStringConverter (here is an example). Your own float-to-string and string-to-float conversion methods will allow you to handle empty text field of TextBox and convert it to null.
I wrote a value converter, that solves your problem:
Usage:
<Window x:Class="BindingExample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:BindingExample"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<local:DoubleToStringConverter x:Key="DoubleToStringConverter" DigitsCount="5"/>
</Window.Resources>
<StackPanel>
<TextBox Text="{Binding FloatProperty, UpdateSourceTrigger=PropertyChanged, Converter={StaticResource DoubleToStringConverter}}" Margin="5"/>
</StackPanel>
</Window>
Converter:
[ValueConversion(typeof(double), typeof(string))]
public class DoubleToStringConverter : IValueConverter
{
#region IValueConverter Members
public DoubleToStringConverter()
{
// Default value for DigitsCount
DigitsCount = 7;
}
// Convert from double to string
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value == null)
return null;
double doubleValue = System.Convert.ToDouble(value);
// Calculate digits count
int digitsBeforePoint = System.Convert.ToInt32(Math.Ceiling(Math.Log10(doubleValue)));
int digitsAfterPoint = DigitsCount - digitsBeforePoint;
// TODO: You have to handle cases where digitsAfterPoint < 0
// Create formatString that is used to present doubleValue in desired format
string formatString = String.Format("{{0:F{0}}}", digitsAfterPoint);
return String.Format(formatString, doubleValue);
}
// Convert from string to double
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value == null)
return null;
double? result = null;
try
{
result = System.Convert.ToDouble(value);
}
catch
{
}
return result.HasValue ? (object)result.Value : DependencyProperty.UnsetValue;
}
public int DigitsCount { get; set; }
#endregion
}

WPF Textblock Convert Issue

am usina text block in usercontrol, but am sending value to textblock from other form, when i pass some value it viewed in textblock, but i need to convert the number to text. so i used converter in textblock. but its not working
<TextBlock Height="21" Name="txtStatus" Width="65" Background="Bisque" TextAlignment="Center" Text="{Binding Path=hM1,Converter={StaticResource TextConvert},Mode=OneWay}"/>
converter class
class TextConvert : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value != null)
{
if (value.ToString() == "1")
{
return value = "Good";
}
if (value.ToString() == "0")
{
return value = "NIL";
}
}
return value = "";
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return (string)value;
}
}
is it right? whats wrong in it??
ok I think I know what the problem is - let see if I can define it for you :)
in your xaml file where you want to use TextConvert, define Resource for it (unless you are doing it already, then I haven't a clue why its not working)
<Grid.Resources>
<Shared:TextConvert x:Key="TextConvertKey" />
</Grid.Resources>
shared being the xmlns ofcourse.
Then in the textbox use it like:
Text="{Binding Path=hM1,Converter={StaticResource TextConvertKey},Mode=OneWay}"/>
EDIT:
If you set a breakpoint in the converter class, does the debugger go in there?????
EDIT 2:
am using like this voodoo
local:HealthTextConvert x:Key="TextConvert"
This is absolutely wrong. How can you Call it HealthTextConvert when the converter name is TextConvert???
it should be
local:TextConvert x:Key="whateverKeyNameYouWant"
and
in the textbox is should be
Text="{Binding Path=hM1,Converter={StaticResource whateverKeyNameYouWant},Mode=OneWay}"
I can see immediately a problem with your converter definition.
class TextConvert : IValueConverter
{
...
Should be declared public to be able to use it as a resource.
public class TextConvert : IValueConverter
{
...
Also, its not a good thing to be doing this...
return value = "Good";
...
return value = "NIL";
It should just be (even though it will not matter if you leave it, just bad programming =P):
return "Good";
...
return "Nill";
Try by removing Path in the below line
Text="{Binding **Path**=hM1,Converter={StaticResource TextConvert},Mode=OneWay}".
Sometimes it works without Path :).
Also look into the output window(Alt+Cntl+O)...to see where the issue is.

WPF DataBinding to Flag Enum (in a PropertyGrid)

I need to have the ability to select multiple values as is the nature of a Flag enumeration from a WPF view (all be it, in a PropertyGrid).
The properties in question are dynamic and no pre-defined DataTemplates can be used as the type of the properties will be discovered at runtime. (A DataTemplate which can detect if an enumeration is a Flag may prove helpful, but from my understanding I would need to know the Flag Enum types ahead of time to achieve this and that will not be the case).
I have tried out a number of proprietary and open source property grids for WPF and none seem to support 'Flags' attributed enum types out of the box.
A solution to this issue would be anything that would allow me to databind to + select multiple values for said Flags Enum for any commercial or open source WPF PropertyGrid.
Code:
Example PropertyType:
public class PropertyTypeOne
{
public PropertyTypeOne()
{
IntProp = 1;
InProp2 = 2;
BoolProp = true;
Boolprop2 = false;
StringProp = "string1";
DoubleProp = 2.3;
EnumProp = FlagEnumDataTYpe.MarketDepth;
}
public int IntProp { get; set; }
public int InProp2 { get; set; }
public bool BoolProp { get; set; }
public bool BoolProp2 { get; set; }
public string StringProp { get; set; }
public double DoubleProp { get; set; }
//This is the property in question
public FlagEnumDataType EnumProp { get; set; }
}
Example Flag Enumeration Type:
[Flags]
public enum FlagEnumDataType : byte
{
None = 0,
Trade = 1,
Quote = 2,
MarketDepth = 4,
All = 255
}
Note:
If the solution makes use of the Open Source WPF PropertyGrid (http://www.codeplex.com/wpg) I will implement the changes /additions back into the control.
Thanks.
I haven't found a truly elegant way of doing this, but from talking with the Developers at Mindscape and here's something crude but functional thats works with the Mindscape PropertyGrid.
First, we create a template for the flag-enum editor itself. This is an ItemsControl populated using the EnumValuesConverter from the WPF Property Grid library:
<ms:EnumValuesConverter x:Key="evc" />
<local:FlaggyConverter x:Key="fc" />
<DataTemplate x:Key="FlagEditorTemplate">
<ItemsControl Name="ic" ItemsSource="{Binding Value, Converter={StaticResource evc}}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding}">
</CheckBox>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</DataTemplate>
Now we need to display the check boxes as checked according to whether the flag is on or off. This requires two things: first, an IMultiValueConverter so it can consider both the flag at hand and the context value, and second, a way for individual check boxes to read the context value. (By context value I mean the actual property value. E.g. the context value might be Flag1 | Flag4 | Flag32.) Here's the converter:
public class FlaggyConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
int flagValue = (int)values[0];
int propertyValue = (int)values[1];
return (flagValue & propertyValue) == flagValue;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
For propagating the context value, I'm going to take a shortcut and use Tag. You might prefer to create an attached property with a more meaningful name.
Now the control will display checks for the flags that are set, but won't yet update the value when you click a checkbox on or off. Unfortunately, the only way I've found to make this work is by handling the Checked and Unchecked events and setting the context value by hand. In order to do this, we need place the context value in a place where it can be updated from the check box event handlers. This means two-way binding a property of the check box to the context value. Again I'll use Tag though you may want something a bit cleaner; also, I'm going to use direct event handling, though depending on your design you may want to wrap this up into an attached behaviour (this would work particularly well if you were creating attached properties to carry around the context value).
<DataTemplate x:Key="FlagEditorTemplate">
<ItemsControl Name="ic" ItemsSource="{Binding Value, Converter={StaticResource evc}}" Tag="{Binding Value, Mode=TwoWay}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding}" Tag="{Binding Tag, ElementName=ic, Mode=TwoWay}" Checked="CheckBox_Checked" Unchecked="CheckBox_Unchecked">
<CheckBox.IsChecked>
<MultiBinding Converter="{StaticResource fc}" Mode="OneWay">
<Binding />
<Binding Path="Tag" ElementName="ic" />
</MultiBinding>
</CheckBox.IsChecked>
</CheckBox>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</DataTemplate>
Note the two-way binding of the Tag: this is so that when we set Tag from our event handling code, it propagates back to ic.Tag, and from there to the property's Value.
The event handlers are mostly obvious but with one wrinkle:
<DataTemplate x:Key="FlagEditorTemplate">
<ItemsControl Name="ic" ItemsSource="{Binding Value, Converter={StaticResource evc}}" Tag="{Binding Value, Mode=TwoWay}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding}" Tag="{Binding Tag, ElementName=ic, Mode=TwoWay}" Checked="CheckBox_Checked" Unchecked="CheckBox_Unchecked">
<CheckBox.IsChecked>
<MultiBinding Converter="{StaticResource fc}" Mode="OneWay">
<Binding />
<Binding Path="Tag" ElementName="ic" />
</MultiBinding>
</CheckBox.IsChecked>
</CheckBox>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</DataTemplate>
Event Handlers:
private void CheckBox_Checked(object sender, RoutedEventArgs e)
{
CheckBox cb = (CheckBox)sender;
int val = (int)(cb.Tag);
int flag = (int)(cb.Content);
val = val | flag;
cb.Tag = (Curses)val;
}
private void CheckBox_Unchecked(object sender, RoutedEventArgs e)
{
CheckBox cb = (CheckBox)sender;
int val = (int)(cb.Tag);
int flag = (int)(cb.Content);
val = val & ~flag;
cb.Tag = (Curses)val;
}
Note the cast when setting cb.Tag. Without this, WPF internally fails to convert the value to the enum type when trying to propagate it back to the source. Here Curses is my enum type. If you want a fully flexible, type-agnostic editor, you'll want to provide this externally, for example as an attached property on the check box. You could either infer this using a converter or propagate it from an editor EditContext.
Finally we need to hook this up to the grid. You can do this either on a property-by-property basis:
<ms:PropertyGrid>
<ms:PropertyGrid.Editors>
<ms:PropertyEditor PropertyName="Curses" EditorTemplate="{StaticResource FlagEditorTemplate}" />
</ms:PropertyGrid.Editors>
</ms:PropertyGrid>
or by using a smart editor declaration to hook up all properties whose types have a FlagsAttribute. For info about creating and using smart editors, see http://www.mindscape.co.nz/blog/index.php/2008/04/30/smart-editor-declarations-in-the-wpf-property-grid/.
If you want to save space you can change the ItemsControl to a ComboBox though you'll need to do some additional work to handle the collapsed display; I haven't explored this in detail.
Found on the Internet, slightly improved, but didn't have time to test it yet.
/// <summary>
/// Two-way conversion from flags to bool and back using parameter as mask
/// Warning: The trick is in storing value locally between calls to Convert and ConvertBack
/// You must have a single instance of this converter per flags property per object
/// Do not share this converter between different objects or properties
/// Typical usage:
/// [Flags] enum FlagType { None = 0, Trade = 1, Quote = 2, Report = 4, All = 255 }
/// <local:EditableFlagsToBooleanConverter x:Key="FlagsToBooleanConverter" />
/// <CheckBox IsChecked="{Binding Prop1, Converter={StaticResource FlagsToBooleanConverter}, Mode=TwoWay,
/// ConverterParameter={x:Static local:FlagType.Trade}}" >Trade</CheckBox>
/// <CheckBox IsChecked="{Binding Prop1, Converter={StaticResource FlagsToBooleanConverter}, Mode=TwoWay,
/// ConverterParameter={x:Static local:FlagType.Quote}}" >Quote</CheckBox>
/// <CheckBox IsChecked="{Binding Prop1, Converter={StaticResource FlagsToBooleanConverter}, Mode=TwoWay,
/// ConverterParameter={x:Static local:FlagType.Report}}" >Report</CheckBox>
/// </summary>
public class EditableFlagsToBooleanConverter : IValueConverter
{
private ulong _target;
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (parameter is Enum && value is Enum)
{
var mask = (ulong) parameter;
_target = (ulong) value;
return ((mask & _target) != 0);
}
return Binding.DoNothing;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is bool && parameter is Enum)
{
var mask = (ulong)parameter;
if ((bool)value)
{
_target |= mask;
}
else
{
_target &= ~mask;
}
return _target;
}
return Binding.DoNothing;
}
}

Resources