Note that there are some explanatory texts on larger screens.

plurals
  1. POHow can you two-way bind a checkbox to an individual bit of a flags enumeration?
    primarykey
    data
    text
    <p>For those who like a good WPF binding challenge:</p> <p>I have a nearly functional example of two-way binding a checkbox to an individual bit of a flags enumeration (thanks Ian Oakes, <a href="http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/c05b7e7e-25cd-4a41-8bf5-e35d2caff797/" rel="noreferrer">original MSDN post</a>). 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. Attached is the class defining some attached dependency properties to enable the bit-based binding. What I've noticed is that ValueChanged is never called, even when I force the DataContext to change.</p> <p>What I've tried: Changing the order of property definitions, Using a label and text box to confirm the DataContext is bubbling out updates, Any plausible FrameworkMetadataPropertyOptions (AffectsRender, BindsTwoWayByDefault), Explicitly setting Binding Mode=TwoWay, Beating head on wall, Changing ValueProperty to EnumValueProperty in case of conflict.</p> <p>Any suggestions or ideas would be extremely appreciated, thanks for anything you can offer!</p> <p>The enumeration:</p> <pre><code> [Flags] public enum Department : byte { None = 0x00, A = 0x01, B = 0x02, C = 0x04, D = 0x08 } // end enum Department </code></pre> <p>The XAML usage:</p> <pre><code> CheckBox Name="studentIsInDeptACheckBox" ctrl:CheckBoxFlagsBehaviour.Mask="{x:Static c:Department.A}" ctrl:CheckBoxFlagsBehaviour.IsChecked="{Binding Path=IsChecked, RelativeSource={RelativeSource Self}}" ctrl:CheckBoxFlagsBehaviour.Value="{Binding Department}" </code></pre> <p>The class:</p> <pre><code> /// /// A helper class for providing bit-wise binding. /// public class CheckBoxFlagsBehaviour { private static bool isValueChanging; public static Enum GetMask(DependencyObject obj) { return (Enum)obj.GetValue(MaskProperty); } // end GetMask public static void SetMask(DependencyObject obj, Enum value) { obj.SetValue(MaskProperty, value); } // end SetMask public static readonly DependencyProperty MaskProperty = DependencyProperty.RegisterAttached("Mask", typeof(Enum), typeof(CheckBoxFlagsBehaviour), new UIPropertyMetadata(null)); public static Enum GetValue(DependencyObject obj) { return (Enum)obj.GetValue(ValueProperty); } // end GetValue public static void SetValue(DependencyObject obj, Enum value) { obj.SetValue(ValueProperty, value); } // end SetValue public static readonly DependencyProperty ValueProperty = DependencyProperty.RegisterAttached("Value", typeof(Enum), typeof(CheckBoxFlagsBehaviour), new UIPropertyMetadata(null, ValueChanged)); private static void ValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { isValueChanging = true; byte mask = Convert.ToByte(GetMask(d)); byte value = Convert.ToByte(e.NewValue); BindingExpression exp = BindingOperations.GetBindingExpression(d, IsCheckedProperty); object dataItem = GetUnderlyingDataItem(exp.DataItem); PropertyInfo pi = dataItem.GetType().GetProperty(exp.ParentBinding.Path.Path); pi.SetValue(dataItem, (value & mask) != 0, null); ((CheckBox)d).IsChecked = (value & mask) != 0; isValueChanging = false; } // end ValueChanged public static bool? GetIsChecked(DependencyObject obj) { return (bool?)obj.GetValue(IsCheckedProperty); } // end GetIsChecked public static void SetIsChecked(DependencyObject obj, bool? value) { obj.SetValue(IsCheckedProperty, value); } // end SetIsChecked public static readonly DependencyProperty IsCheckedProperty = DependencyProperty.RegisterAttached("IsChecked", typeof(bool?), typeof(CheckBoxFlagsBehaviour), new UIPropertyMetadata(false, IsCheckedChanged)); private static void IsCheckedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { if (isValueChanging) return; bool? isChecked = (bool?)e.NewValue; if (isChecked != null) { BindingExpression exp = BindingOperations.GetBindingExpression(d, ValueProperty); object dataItem = GetUnderlyingDataItem(exp.DataItem); PropertyInfo pi = dataItem.GetType().GetProperty(exp.ParentBinding.Path.Path); byte mask = Convert.ToByte(GetMask(d)); byte value = Convert.ToByte(pi.GetValue(dataItem, null)); if (isChecked.Value) { if ((value & mask) == 0) { value = (byte)(value + mask); } } else { if ((value & mask) != 0) { value = (byte)(value - mask); } } pi.SetValue(dataItem, value, null); } } // end IsCheckedChanged /// /// Gets the underlying data item from an object. /// /// The object to examine. /// The underlying data item if appropriate, or the object passed in. private static object GetUnderlyingDataItem(object o) { return o is DataRowView ? ((DataRowView)o).Row : o; } // end GetUnderlyingDataItem } // end class CheckBoxFlagsBehaviour </code></pre>
    singulars
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload