WPF - FlagsEnumValueConverter

|

I feel a trend starting… another http://stackoverflow.com/ answer, here’s the question (massively snipped):

I have a nearly functional example of two-way binding a checkbox to an individual bit of a flags enumeration… The problem though is that the binding behaves as if it is one way (UI to DataContext, not vice versa). So effectively the check box does not initialize, but if it is toggled the data source is correctly updated…

Here’s the full link to the post for more context, if required.

The bottom line is that a solution to binding to “bit” flags can be challenging, but the solution initially offered in the question was extremely complex (class with a bunch of Attached Properties yadda, yadda, yadda), when I looked at it thought, I thought "surely that can be done with a value converter”; after a little bit of hacking I have a simple IValueConverter implementation for any flags enumeration.

But first, consider the following Enum definition:

[Flags]
public enum Department
{
    None = 0,
    A = 1,
    B = 2,
    C = 4,
    D = 8
}

My first step was to create a specific converter for the Department Enum:

public class DepartmentValueConverter : IValueConverter
{
    private Department target;

    public DepartmentValueConverter()
    {
    }

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        Department mask = (Department)parameter;
        this.target = (Department)value;
        return ((mask & this.target) != 0);
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        this.target ^= (Department)parameter;
        return this.target;
    }
}

The tricky part, according to the initial question, was the ConvertBack implementation. The solution here is to simply store the incoming value in the Convert method, and when the value is changed the ConvertBack method uses the Exclusive-Or assignment operator to toggle the bit, which works lovely.

However, I also figured that it should be possible to create a generic version of this converter – this type of converter is not something that you’d be creating everyday, so to have a specific version for each Flags Enum would probably be a totally acceptable solution… but… this is what I came up with:

public class FlagsEnumValueConverter : IValueConverter
{
    private int targetValue;

    public FlagsEnumValueConverter()
    {
    }

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        int mask = (int)parameter;
        this.targetValue = (int)value;
        return ((mask & this.targetValue) != 0);
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        this.targetValue ^= (int)parameter;
        return Enum.Parse(targetType, this.targetValue.ToString());
    }
}

So the key to this solution is that it works with the underlying type for most Enum values: Int32. The basic pattern is the same, but it simply replaces the specific Enum value with an Int32; with the only tricky part being in the ConvertBack: When I simply returned an Int32, when an Enum value was expected, WPF did not update the underlying value in the binding. I found I had to convert the Int32 to the specific Enum value represented by the Int32; which I simply handled by using the Enum.Parse method.

The following screenshot then shows how to consume the converter in XAML. Note that the mask value for the Enum value instance is passed in the CommandParameter property on the binding declaration.

FlagsConverterXaml

I have uploaded a sample application here:

PaulJ.FlagsValueConverter.zip

Here’s a screenshot of the app, for what it’s worth given its simplicity :)

FlagsValueConverterScreenShot

Comments and questions welcome as always.

12 comments:

Anonymous said...

Great example!

Paul said...

Thanks, I'm glad you liked it :)

Felix said...

Thanks, exactly what i needed. :-)

Paul said...

Glad I could help.

-PJ

Anonymous said...

I'm having problem when the check box is using explicit update trigger. any idea/

RV1987 said...

Nice solution but this have limitation in case items have to be generated dynamically. Lets say i have 20 enum values then i have to place 20 checkboxes instead of using ItemsControl.

Tobias said...

Awesome, thank you very much! That´s exactly what i was searching for...

Lawrence Mantin said...

Just what I needed. Thanks for sharing.

Marcin Sulecki said...

Excelent solution! Thank you.

Moses said...

Thanks!!! It's Great!!

Helton Moraes said...

The best part of this solution: it has a sample code that simply works!! Thanks a lot for the effort!!

Richard Blank said...

Thank you, simple sample and works wonderful