Binding Flagged Enumerations

by TheJet 13. October 2011 06:23

Introduction

Recently a tweet brought my attention to this blog post by @san0x, regarding the difficulties of binding flagged enumerations to UI fields in a meaningful and easy-to-use fashion.  The post was largely dealing with ways to work around the limitation of the IValueConverter, and its ‘context agnostic’ processing of values during the conversion process.  The IValueConverter does not know the context of the value being converted, only the value itself and an [unbindable] conversion parameter.

Scenario

I’ll avoid going into great detail about the scenario here, as it’s explained in detail in the linked post above, but the main focus is binding a series of checkboxes to a single flagged enumeration value.  The classes I’ll use in my example are adapted from the original post, I’ve included them here as a convenience to the reader.

    public class Species : ViewModelBase
    {
        /// 
        /// The  property's name.
        /// 
        public const string NamePropertyName = "Name";

        private string _name = String.Empty;

        /// 
        /// The species name
        /// 
        public string Name
        {
            get
            {
                return _name;
            }

            set
            {
                if (_name == value)
                {
                    return;
                }

                var oldValue = _name;
                _name = value;

                // Update bindings, no broadcast
                RaisePropertyChanged(NamePropertyName);
            }
        }

        /// 
        /// The  property's name.
        /// 
        public const string FlagsPropertyName = "Flags";

        private SpeciesFlag _flags = SpeciesFlag.None;
        
        /// 
        /// The properties of the species, as a flagged enumeration
        /// 
        public SpeciesFlag Flags
        {
            get
            {
                return _flags;
            }

            set
            {
                if (_flags == value)
                {
                    return;
                }

                var oldValue = _flags;
                _flags = value;

                // Update bindings, no broadcast
                RaisePropertyChanged(FlagsPropertyName);
            }
        }
    }

 

    [Flags]
    public enum SpeciesFlag : byte
    {
        None = 0,
        BreathesAir = 0x1,
        BreathesWater = 0x2,
        Photosynthesizes = 0x4,
        Eats = 0x8
    }

Note: My examples use the excellent MVVM Light Toolkit by @LBugnion, but are NOT intended to be examples of best practices when using the toolkit

 

Potential Solutions

Many developers [myself included], when presented with these types of problems, take a similar route to @san0x, attempting to coerce the value converter into something that is context-aware in any number of ways.  My personal investigations led me to first investigate using WPF MultiBinding [a little used, but occasionally very handy feature].  The promise of the MultiBinding was that I could pass in a number of bound values, and get back a suitable result [true/false].  As things usually go [for me at least], this worked fabulously for the first 50% of the problem, binding from the source –> the CheckBox.IsSelected property worked just as expected with a binding very similar to this:

<CheckBox.IsChecked>
    <MultiBinding Mode="TwoWay" 
                   Converter="{StaticResource FlaggedEnumToBoolMultiConverter}" ConverterParameter="BreathesAir">
      <Binding Path="Species" />
      <Binding Path="Species.Flags" />
    </MultiBinding>
</CheckBox.IsChecked>

 

This allowed me to reflect the flag value on the CheckBox, and changes to the value were automatically reflected on the UI without a lot of additional work.  The issue, of course, was how to update the underlying object when the CheckBox itself is used to change the value.  This is much harder, because the IMultiValueConverter::ConvertBack method has two problems:

  1. Only a single [boolean] value, and [object] parameter are passed to the ConvertBack method.
  2. ConvertBack must pass back an array of values, one for each Binding contained in the MultiBinding.

So… we’re stuck… there’s no way to convert from a single boolean back into a Species and its flags while only effecting the single ‘bit’ or ‘bits’ we care about.  We simply do not have enough information available to us as the time of conversion.  Sure, there are workarounds, and one such workaround is listed in @san0x’s follow-up post [using a standard IValueConverter], but in the end, those workarounds all ‘smell’ like hacks to me.  They all involve keeping the context of the binding around in some fashion during conversion.

Reframing the Problem

My suggestion on Twitter was to utilize the ViewModel to expose the individual flags as bindable properties to the View.  This successfully reframes the problem, but does suffer from the requirement that you update your ViewModel every time your enumeration changes [the View needs to be updated with any solution, since we’re using individual CheckBox(s)].  This ‘smells’ better to me simply because it does what ViewModel’s are supposed to do, abstract away the underlying model when necessary to make data binding feasible.  It also doesn’t resort to any of the same hacks required to get binding to work properly, and context is retained because it’s handled by the bound view model.

Proposed Solution

So with the reframing of the question in mind, I moved on to whether or not it would be possible to simplify the process of binding to that flagged enumeration.  I wanted to minimize coding effort, and have the solution work for any general flagged enumeration.  Back in the .NET 1.x/2.x days, I had worked on a number of projects that leveraged the ICustomTypeDescriptor and it’s siblings to provide customized design and run-time behavior for objects and properties.  Silverlight 5 and .NET 4.5 bring the ICustomTypeProvider to bear on the problem, and could be used in those environments.  For this post, since we’re running WPF and .NET 4.0, I’m going to utilize DynamicObject to provide for runtime binding of the flagged enumeration values.

The FlaggedEnumViewModel Class

I’ll start by introducing the class that enables runtime binding and implements the bits necessary for the bindings to resolve properly:

    public class FlaggedEnumViewModel<TEnum>: DynamicObject, INotifyPropertyChanged
    {
        private Func<TEnum> _getter;
        private Action<TEnum> _setter;

        public FlaggedEnumViewModel(Func<TEnum> getter, Action<TEnum> setter)
        {
            if (!typeof(TEnum).IsEnum) 
                throw new ArgumentException("This type must only be used with enumerations.");
            
            _getter = getter;
            _setter = setter;
        }

        public override DynamicMetaObject GetMetaObject(System.Linq.Expressions.Expression parameter)
        {
            return base.GetMetaObject(parameter);
        }
        public override IEnumerable<string> GetDynamicMemberNames()
        {
            return Enum.GetNames(typeof(TEnum)).Select(i => "Has" + i);
        }

        public override bool TryGetMember(GetMemberBinder binder, out object result)
        {
            if (!binder.Name.ToLower().StartsWith("has") || binder.Name.Length < 4)
            {
                result = null;
                return false;
            }

            long checkedFlag = Convert.ToInt64(Enum.Parse(typeof(TEnum), binder.Name.Substring(3), true));
            long currentValue = Convert.ToInt64(_getter());

            
            result = (checkedFlag & currentValue) != 0;
            return true;
        }

        public override bool TrySetMember(SetMemberBinder binder, object value)
        {
            if (!binder.Name.ToLower().StartsWith("has") || binder.Name.Length < 4)
            {
                return false;
            }

            long checkedFlag = Convert.ToInt64(Enum.Parse(typeof(TEnum), binder.Name.Substring(3), true));
            long currentValue = Convert.ToInt64(_getter());

            if ((bool)value == true)
            {
                currentValue |= checkedFlag;
            }
            else
            {
                currentValue &= ~checkedFlag;
            }

            
            _setter((TEnum)Enum.ToObject(typeof(TEnum), currentValue));

            OnPropertyChanged(binder.Name);

            return true;
        }

        protected void OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;
    }

 

In the above code, I’ll call attention to three bits:

  1. The constructor takes a ‘getter’ and ‘setter’ argument that wraps access to the underlying flagged enum store.
  2. The TryGetMember method handles converting from the flagged value to a boolean
  3. The TrySetMember method handles converting from the boolean back to the flagged value

 

The View/ViewModel

Utilizing the class is fairly straightforward, in my ViewModel, I’ve exposed a property [WrappedFlags] of the FlaggedEnumViewModel type that I use for binding on the UI, and the ‘Species’ property notifies that both properties change whenever the Species property changes.  This is sufficient to enable TwoWay binding on the View.

    public class MainViewModel : ViewModelBase
    {
        public string Welcome
        {
            get
            {
                return "Sample Flagged Enum Editor";
            }
        }

        /// <summary>
        /// Initializes a new instance of the MainViewModel class.
        /// </summary>
        public MainViewModel()
        {
            if (IsInDesignMode)
            {
                // Code runs in Blend --> create design time data.
            }
            else
            {
                // Code runs "for real"
            }

            LoadRandomSpecies = new RelayCommand(() => AssignRandomSpecies());
            WrappedFlags = new FlaggedEnumViewModel<SpeciesFlag>(
                                   () => Species.Flags, 
                                   (v) => Species.Flags = v);
        }

        /// <summary>
        /// The <see cref="Species" /> property's name.
        /// </summary>
        public const string SpeciesPropertyName = "Species";

        private Species _species = new Species() { Name = "Sample Species" };

        /// <summary>
        /// The Species we're working with
        /// </summary>
        public Species Species
        {
            get
            {
                return _species;
            }

            set
            {
                if (_species == value)
                {
                    return;
                }

                var oldValue = _species;
                _species = value;

                // Update bindings, no broadcast
                RaisePropertyChanged(SpeciesPropertyName);
                RaisePropertyChanged("WrappedFlags");
            }
        }

        public FlaggedEnumViewModel<SpeciesFlag> WrappedFlags { get; private set; }

        public RelayCommand LoadRandomSpecies { get; private set; }

        protected void AssignRandomSpecies()
        {
            Random rnd = new Random();
            byte value = (byte)rnd.Next(0, 16);

            Species = new Species()
            {
                Name = "Random Species " + value.ToString(),
                Flags = (SpeciesFlag)value
            };
        }
    }

 

Lastly, the view code is pretty basic, I’ll just call out one of the CheckBox elements to show how the binding was done:

    <CheckBox Content="Breathes Air" 
               IsChecked="{Binding Path=WrappedFlags.HasBreathesAir, Mode=TwoWay}" />

 

Conclusion

Transitioning from a converter-based approach to enhancing the ViewModel makes the problem much easier to implement and troubleshoot, it also avoids many of the pitfalls of introducing state to your I[Multi]ValueConverter instances.  Utilizing a DynamicObject class derivative enables you to write much less code and get the same overall benefit at runtime.  In a future post, I hope to find time to walk through enhancing the design-time experience to support setting up bindings in the VS2010 XAML designer.  I look forward to hearing your comments, or seeing alternative solutions to the problem.

Thanks for reading!

Sample Code: WPFBindingFlaggedEnum-20111013.zip (19 kb)

Creating a bootable Win8 VHD from the #BldWin USB Drive

by TheJet 14. September 2011 00:07

Intro

Everyone who attended //BUILD this year received two things:

  1. A new Samsung Developer tablet which I won’t detail here, there’s sure to be loads of information out there beyond what I could provide here
  2. A 32GB USB stick with all the Win8 //BUILD bits on it

So, naturally, my quest was to get my laptop and tablet talking to each other, both using the Windows 8 developer preview.  To do that, I needed to get Win8 running on the laptop.  The ‘best’ option I saw, given that the USB stick contains ONLY 64-bit stuff, was to go ‘to the metal’ and create a bootable VHD.

How to do it

The steps here are pretty straightforward, so I’ll just lay them out for you here.  NOTE:  This requires Window 7 Enterprise or Ultimate to be installed on the destination machine:

1.  Create a new empty VHD

  1. Right-click your computer and choose ‘Manage’
  2. Switch to the ‘Disk Management’ area
  3. Choose Action->Create VHD
  4. I chose a basic 32GB partition [fixed size] and choose a location.  For this example, I created one on my hard drive in “C:\Virtual Machines\Win8.vhd” and click ‘OK’
  5. Right-click on the newly attached VHD and choose ‘Initialize Disk’, I chose the MBR option, then click OK
  6. Detach the resulting VHD

2. Plug in //BUILD USB Drive

You can do this step whenever you like while the computer is rebooting, I chose to just do it before I started the reboot process.

3.  Reboot your Computer

This step is crucial, and your machine MUST be setup to boot from any plugged in USB device.

4.  Enter Win8 setup [but not all the way]

When the computer boots, you’ll be presented with the Win8 setup process, choose your keyboard layout and such and enter the setup process BUT DO NOT CLICK ‘INSTALL’.

5.  Enter the ‘Repair’ area

Below the ‘Install Now’ button, there should be a repair link that lets you repair a PC.  Somewhere in this menu is an option to launch a command window [under Advanced –> Command Prompt ??].  Do this and then enter the DISKPART utility.

6.  SELECT and ATTACH the empty VHD

Once in DISKPART, you need to select and attach your VHD [yes, this feels backwards].  This will enable setup to see the drive and continue.  This requires two commands:

  1. SELECT VDISK FILE=”C:\Virtual Machines\Win8.vhd”
  2. ATTACH VDISK
  3. EXIT

7.  Resume installation process

Once done attaching the VHD, you can proceed with the install.  To do this, I just went back up to the root drive ‘X:’ in my case, and typed ‘setup.exe’.  This re-launches the setup UI and allows you to click ‘Install Now’.  During the install process, choose ‘Custom’.

8.  Finish VHD Setup

Inside the custom setup, when choosing the destination for the install, make sure you choose the newly attached VHD [usually the 30ish GB area listed as ‘Unallocated’].  Select that partition and click ‘New’ to create a new partition on the VHD, selecting the default size.  Then click ‘Format’ to format the associated partition.  Finally select the newly formatted partition and click ‘Next’.

NOTE:  The UI will tell you that it can’t install to that partition, but ignore the error and just click ‘Next’.

9.  WAIT… until the install process finishes, and when it starts to reboot, pull the USB stick

I don’t know if this is strictly necessary, but I did it to ensure I didn’t boot back to the USB again in error.  Win8 will have taken over your boot process and will complete the install and present you with the ‘developer preview’ software agreements and whatnot.

10.  There is no #10

That’s it'!  Everything is working as expected, enjoy your new Win8 laptop!

 

It should be noted, that just like Win7, when the machine is starting up [after BIOS], you can hit F8 to get the boot menu [this is slow to come up], and choose ‘another OS’ and then choose to boot onto your Windows 7 machine as you used to.  You can probably also use bcdedit to set your Win7 install to be the default and use F8 from there to get into Win8 as desired.  I won’t detail those bits here, since they’ve been covered numerous times on other blogs.

 

That’s it, and as always, anything you do to your machine using these steps IS YOUR RESPONSIBILITY.  I accept no liability for any damage resulting from following these instructions Smile

Who am I?

Me

Husband/Father, Solutions Architect [MCPD/MCSD], .NET Developer, Tech Enthusiast

 

Blogging primarily on Silverlight/WPF development, with an occasional foray into all things .NET, including Windows Phone 7, RIA Services, ASP.NET, etc.  And yes, some day, I'll even come up with a non-canned blog theme :)

 

Twitter: @virtualolympus

LinkedIn: Ben Gavin