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.

Constrained StackPanel

|

I’ve been spending a little time today over on stackoverflow.com and just answered a question about constraining the width of a StackPanel, and I thought I’d share my solution here too.

The question in its entirety was:

How can I constrain a vertical WPF StackPanel's width to the most narrow item it contains. The StackPanel's width must not be greater than the width of any other child element.

My initial thought to try and use a simple IValueConverter implementation; hand the in the child collection at the right time and bingo! constrain the size of the panel. However, that proved to be a little problematic as due to timing and data binding issues. One answer already suggested was to create a custom panel (which had no up votes for some reason – go figure); which would have been my next choice after the converter.

So I set about knocking up a ConstrainedStackPanel, which looks something like this:

public class ConstrainedStackPanel : StackPanel
{
public ConstrainedStackPanel()
{
}

protected override Size MeasureOverride(Size constraint)
{
foreach (var item in this.Children)
{
FrameworkElement element = item as FrameworkElement;
if (element != null)
constraint.Width = Math.Min(element.Width, constraint.Width);
}

return base.MeasureOverride(constraint);
}

protected override Size ArrangeOverride(Size arrangeSize)
{
foreach (var item in this.Children)
{
FrameworkElement element = item as FrameworkElement;
if (element != null)
arrangeSize.Width = Math.Min(element.Width, arrangeSize.Width);
}

return base.ArrangeOverride(arrangeSize);
}
}

Here I’m simply enumerating the child collection at the right time, being a good WPF citizen and overriding both Measure and Arrange to get the desired effect, by keeping the smallest Width value at all times during the two stage rendering process. To consume the new panel in XAML, simply use it like any other StackPanel, as shown here:

<l:ConstrainedStackPanel x:Name="panelB">
    <Button Width="100" Content="100" />
    <Button Width="200" Content="200" />
    <Button Width="300" Content="300" />
    <Button Width="400" Content="400" />
</l:ConstrainedStackPanel>

When compared with a regular StackPanel the results look like the following screenshot:

WidthStackPanel

And there you have it. In terms of requirements for a custom panel they don’t get much simpler than that, so it was a pretty straight-forward solution, but I hope it helps someone.

To follow the progress of the question and to see what other people come up with here’s a full link to the question: http://stackoverflow.com/questions/377523/constrain-stackpanels-width-to-smallest-child-element.

Comments and questions welcome as always.