Как вы можете привязать флажок к отдельному биту перечисления флагов?

Для тех, кому нравится хорошая проблема привязки WPF:

У меня есть почти функциональный пример двустороннего связывания флажка с отдельным битом перечисления флагов (спасибо Яну Оуэсу, оригинальному сообщению MSDN). Проблема состоит в том, что привязка ведет себя так, как будто это один из способов (UI to DataContext, а не наоборот). Таким образом, флажок не инициализируется, но если он переключен, источник данных будет правильно обновлен. Прикрепленный класс, определяющий некоторые связанные свойства зависимостей, чтобы включить привязку на основе бит. Я заметил, что ValueChanged никогда не вызывается, даже когда я изменяю DataContext.

То, что я пробовал: Изменение порядка определений свойств. Использование метки и текстового поля для подтверждения DataContext - всплывающие обновления. Любые правдоподобные FrameworkMetadataPropertyOptions (AffectsRender, BindsTwoWayByDefault), Явная настройка режима привязки = TwoWay, Beating head on wall, Изменение ************* для Enum************* в случае конфликт.

Любые предложения или идеи будут чрезвычайно оценены, спасибо за все, что вы можете предложить!

Перечисление:

[Flags]
 public enum Department : byte
 {
 None = 0x00,
 A = 0x01,
 B = 0x02,
 C = 0x04,
 D = 0x08
 } // end enum Department

Использование XAML:

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}"

Класс:

/// 
 /// 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(*************);
 } // end GetValue
 public static void SetValue(DependencyObject obj, Enum value)
 {
 obj.SetValue(*************, value);
 } // end SetValue
 public static readonly DependencyProperty ************* =
 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);
 ************ 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, *************);
 object dataItem = GetUnderlyingDataItem(exp.DataItem);
 ************ 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
3 ответа

Вы можете использовать преобразователь значений. Здесь очень конкретная реализация для целевого Enum, но нетрудно понять, как сделать преобразователь более общим:

[Flags]
public enum Department
{
 None = 0,
 A = 1,
 B = 2,
 C = 4,
 D = 8
}
public partial class Window1 : Window
{
 public Window1()
 {
 InitializeComponent();
 this.DepartmentsPanel.DataContext = new DataObject
 {
 Department = Department.A | Department.C
 };
 }
}
public class DataObject
{
 public DataObject()
 {
 }
 public Department Department { get; set; }
}
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;
 }
}

И затем используйте конвертер в XAML:

<window.resources>
 </window.resources>
 <stackpanel x:name="DepartmentsPanel">
 <checkbox content="A" ischecked="{Binding 
 Path=Department,
 Converter={StaticResource DeptConverter},
 ConverterParameter={x:Static l:Department.A}}">
 <!-- more -->
 </checkbox></stackpanel>

EDIT: Мне не хватает "rep" (пока!), чтобы комментировать ниже, поэтому мне нужно обновить свой собственный пост: (

В последнем комментарии demwiz.myopenid.com говорит: "Но когда дело доходит до двусторонней привязки, ConvertBack разваливается", я обновил свой примерный код выше, чтобы обработать сценарий ConvertBack; Я также разместил образец рабочего приложения здесь (изменить:), отметим, что образец загрузка кода также включает в себя общую версию конвертера).

Лично я думаю, что это намного проще, надеюсь, это поможет.


Спасибо за помощь, я, наконец, понял это.

Я привязываюсь к строго типизированному DataSet, поэтому перечисления хранятся как тип System.Byte, а не System.Enum. В моем окне вывода отладки я заметил случайное исключение исключения привязки, которое указывало на эту разницу. Решение такое же, как указано выше, но с *************, имеющим тип Byte вместо Enum.

Вот класс CheckBoxFlagsBehavior, повторенный в его окончательной редакции. Еще раз спасибо Яну Оуксу за оригинальную реализацию!

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 byte GetValue(DependencyObject obj)
 {
 return (byte)obj.GetValue(*************);
 } // end GetValue
 public static void SetValue(DependencyObject obj, byte value)
 {
 obj.SetValue(*************, value);
 } // end SetValue
 public static readonly DependencyProperty ************* =
 DependencyProperty.RegisterAttached("Value", typeof(byte),
 typeof(CheckBoxFlagsBehaviour), new UIPropertyMetadata(default(byte), 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);
 ************ 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, *************);
 object dataItem = GetUnderlyingDataItem(exp.DataItem);
 ************ 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
 private static object GetUnderlyingDataItem(object o)
 {
 return o is DataRowView ? ((DataRowView)o).Row : o;
 } // end GetUnderlyingDataItem
} // end class CheckBoxFlagsBehaviour


Проверьте свой DataObject, который связывается с CheckBoxes, содержит свойство Department, имеет INotifyPropertyChnaged.PropertyChanged, вызываемый его Setter?

licensed under cc by-sa 3.0 with attribution.