Como você pode vincular bidirecionalmente uma caixa de seleção a um bit individual de uma enumeração de sinalizadores?
Para quem gosta de um bom desafio vinculativo do WPF:
Eu tenho um exemplo quase funcional de ligação bidirecional de uma caixa de seleção para um bit individual de uma enumeração de sinalizadores (obrigado Ian Oakes,postagem original do MSDN) O problema, porém, é que a ligação se comporta como se fosse uma maneira (interface do usuário para o DataContext, não vice-versa). Com eficiência, a caixa de seleção não é inicializada, mas se for alternada, a fonte de dados é atualizada corretamente. Em anexo está a classe que define algumas propriedades de dependência anexadas para ativar a ligação baseada em bits. O que eu notei é que ValueChanged nunca é chamado, mesmo quando eu forço o DataContext a mudar.
O que eu tentei: Alterar a ordem das definições de propriedade, Usar um rótulo e uma caixa de texto para confirmar que o DataContext está exibindo atualizações, Qualquer FrameworkMetadataPropertyOptions (AffectsRender, BindsTwoWayByDefault), Configuração explícita de Modo de ligação = Duas vias, Bater na parede, Alterar ValueProperty para EnumValueProperty em caso de conflito.
Todas as sugestões ou idéias serão extremamente apreciadas, obrigado por tudo o que você pode oferecer!
A enumeração:
[Flags]
public enum Department : byte
{
None = 0x00,
A = 0x01,
B = 0x02,
C = 0x04,
D = 0x08
} // end enum Department
O uso de 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 classe:
///
/// 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