对于那些喜欢 WPF 绑定(bind)挑战的人:
我有一个将 CheckBox
双向绑定(bind)到标志枚举的单个位的几乎实用的示例(感谢 Ian Oakes,original MSDN post)。但问题是绑定(bind)的行为就好像它是一种方式(UI 到 DataContext
,反之亦然)。因此,实际上 CheckBox
不会初始化,但如果它被切换,数据源就会正确更新。 Attached 是定义一些附加依赖属性以启用基于位的绑定(bind)的类。我注意到 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}"
类(class):
/// <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
最佳答案
您可以使用值转换器。这是目标 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>
<l:DepartmentValueConverter x:Key="DeptConverter" />
</Window.Resources>
<StackPanel x:Name="DepartmentsPanel">
<CheckBox Content="A"
IsChecked="{Binding
Path=Department,
Converter={StaticResource DeptConverter},
ConverterParameter={x:Static l:Department.A}}"/>
<!-- more -->
</StackPanel>
编辑:我没有足够的“代表”(还!)在下面发表评论,所以我必须更新我自己的帖子:(
在最后评论Steve Cadwallader说:“但是当谈到双向绑定(bind)时,ConvertBack 会分崩离析”,好吧,我已经更新了上面的示例代码来处理 ConvertBack 场景;我还发布了一个示例工作应用程序 here (编辑:请注意,示例代码下载还包括转换器的通用版本)。
我个人认为这要简单得多,希望对您有所帮助。
关于c# - 如何将复选框双向绑定(bind)到标志枚举的单个位?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/326802/