Для тех, кто любит хорошую задачу привязки WPF:
У меня есть почти функциональный пример двусторонней привязки CheckBox
к отдельному биту перечисления флагов (спасибо Иану Оуксу, исходное сообщение MSDN). Однако проблема в том, что привязка ведет себя так, как если бы она была односторонней (пользовательский интерфейс к DataContext
, а не наоборот). Таким образом, CheckBox
не инициализируется, но если он включен, источник данных обновляется правильно. Прикреплен класс, определяющий некоторые присоединенные свойства зависимостей для включения привязки на основе битов. Я заметил, что ValueChanged никогда не вызывается, даже когда я заставляю DataContext
измениться.
Что я пробовал: изменение порядка определений свойств, использование метки и текстового поля для подтверждения DataContext
всплывающих обновлений, любые правдоподобные FrameworkMetadataPropertyOptions
(AffectsRender
, BindsTwoWayByDefault
), явная установка Binding Mode=TwoWay
, избиение головы на стене, замена ValueProperty
на EnumValueProperty
в случае конфликта.
Мы будем очень благодарны за любые предложения или идеи, спасибо за все, что вы можете предложить!
Перечисление:
[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}"
Класс:
/// <summary>
/// A helper class for providing bit-wise binding.
/// </summary>
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
/// <summary>
/// Gets the underlying data item from an object.
/// </summary>
/// <param name="o">The object to examine.</param>
/// <returns>The underlying data item if appropriate, or the object passed in.</returns>
private static object GetUnderlyingDataItem(object o)
{
return o is DataRowView ? ((DataRowView)o).Row : o;
} // end GetUnderlyingDataItem
} // end class CheckBoxFlagsBehaviour