您双向如何复选框绑定到一个标志枚举的个别位? [英] How can you two-way bind a checkbox to an individual bit of a flags enumeration?

查看:106
本文介绍了您双向如何复选框绑定到一个标志枚举的个别位?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

对于那些谁喜欢一个良好的WPF绑定的挑战:

我有双向复选框绑定到一个标志枚举(感谢伊恩·奥克斯,<一个单独的位近功能例子href=\"http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/c05b7e7e-25cd-4a41-8bf5-e35d2caff797/\">original MSDN帖子)。这个问题虽然是绑定的行为,就好像它是单向的(UI到DataContext的,而不是相反)。因此,有效的复选框不会初始化,但如果是翻转的数据源是正确更新。附件是定义一些附加的依赖项属性,使位基于绑定的类。我已经注意到的是,是的ValueChanged从来没有所谓,甚至当我强迫DataContext的改变。

我已经试过:
更改属性定义的顺序,使用标签和文本框,确认DataContext的是汩汩流出更新,任何合理的FrameworkMetadataPropertyOptions(AffectsRender,BindsTwoWayByDefault),明确设置结合模式=双向,打在墙上头,在以下情况下更改ValueProperty到EnumValueProperty冲突。

任何建议或想法会非常AP preciated,谢谢任何你可以提供!

枚举:

 
    [国旗]
    公共枚举部:字节
    {
        无= 0×00,
        A = 0×01,
        B = 0×02,
        C = 0×04,
        D = 0×08
    } //结束枚举部

该XAML用法:

 
    复选框名称=studentIsInDeptACheckBox
             CTRL:CheckBoxFlagsBehaviour.Mask ={X:静态C:Department.A}
             CTRL:CheckBoxFlagsBehaviour.IsChecked ={绑定路径=器isChecked,的RelativeSource = {自我的RelativeSource}}
             CTRL:CheckBoxFlagsBehaviour.Value ={结合部}

类:

 
    ///
    ///提供逐位结合一个辅助类。
    ///
    公共类CheckBoxFlagsBehaviour
    {
        私人静态布尔isValueChanging;        公共静态枚举GetMask(DependencyObject的OBJ)
        {
            返回(枚举)obj.GetValue(MaskProperty);
        } //结束GetMask        公共静态无效SetMask(OBJ的DependencyObject,枚举值)
        {
            obj.SetValue(MaskProperty,值);
        } //结束SetMask        公共静态只读的DependencyProperty MaskProperty =
            DependencyProperty.RegisterAttached(面具的typeof(枚举)
            typeof运算(CheckBoxFlagsBehaviour),新UIPropertyMetadata(NULL));        公共静态枚举的GetValue(DependencyObject的OBJ)
        {
            返回(枚举)obj.GetValue(ValueProperty);
        } //结束的GetValue        公共静态无效的SetValue(OBJ的DependencyObject,枚举值)
        {
            obj.SetValue(ValueProperty,值);
        } //结束的SetValue        公共静态只读的DependencyProperty ValueProperty =
            DependencyProperty.RegisterAttached(值的typeof(枚举)
            typeof运算(CheckBoxFlagsBehaviour),新UIPropertyMetadata(NULL,的ValueChanged));        私有静态无效的ValueChanged(DependencyObject的D,DependencyPropertyChangedEventArgs E)
        {
            isValueChanging = TRUE;
            字节屏蔽= Convert.ToByte(GetMask(d)条);
            字节值= Convert.ToByte(e.NewValue);            BindingEx pression EXP = BindingOperations.GetBindingEx pression(D,IsCheckedProperty);
            反对的DataItem = GetUnderlyingDataItem(exp.DataItem);
            PI的PropertyInfo = dataItem.GetType()的getProperty(exp.ParentBinding.Path.Path)。
            pi.SetValue(DataItem的,(价值和面罩)= 0,空!);            ((复选框)D).IsChecked =(值&面膜)!= 0;
            isValueChanging = FALSE;
        } //的ValueChanged结束        公共静态布尔? GetIsChecked(DependencyObject的OBJ)
        {
            返回(布尔?)obj.GetValue(IsCheckedProperty);
        } //结束GetIsChecked        公共静态无效SetIsChecked(OBJ的DependencyObject,布尔?值)
        {
            obj.SetValue(IsCheckedProperty,值);
        } //结束SetIsChecked        公共静态只读的DependencyProperty IsCheckedProperty =
            DependencyProperty.RegisterAttached(器isChecked的typeof(布尔),
            typeof运算(CheckBoxFlagsBehaviour),新UIPropertyMetadata(假,IsCheckedChanged));        私有静态无效IsCheckedChanged(DependencyObject的D,DependencyPropertyChangedEventArgs E)
        {
            如果(isValueChanging)回报;            布尔? =器isChecked(布尔?)e.NewValue;
            如果(器isChecked!= NULL)
            {
                BindingEx pression EXP = BindingOperations.GetBindingEx pression(D,ValueProperty);
                反对的DataItem = GetUnderlyingDataItem(exp.DataItem);
                PI的PropertyInfo = dataItem.GetType()的getProperty(exp.ParentBinding.Path.Path)。                字节屏蔽= Convert.ToByte(GetMask(d)条);
                字节值= Convert.ToByte(pi.GetValue(DataItem的,NULL));                如果(isChecked.Value)
                {
                    如果((价值和面罩)== 0)
                    {
                        值=(字节)(值+面罩);
                    }
                }
                其他
                {
                    如果((价值和面罩)!= 0)
                    {
                        值=(字节)(价值 - 面罩);
                    }
                }                pi.SetValue(DataItem的,值,NULL);
            }
        } //结束IsCheckedChanged        ///
        ///从对象获取基础数据项。
        ///
        ///对象审查。
        ///底层数据项目,如果合适,或者传入的对象。
        私有静态对象GetUnderlyingDataItem(对象o)
        {
            回复O是DataRowView的? ((DataRowView的)O).Row:O;
        } //结束GetUnderlyingDataItem
    } //结束类CheckBoxFlagsBehaviour


解决方案

您可以使用一个值转换器。这里的目标枚举一个非常具体的实现,但不会很难看到如何使转换器更通用的:

  [国旗]
公共枚举部
{
    无= 0,
    A = 1,
    B = 2,
    C = 4,
    D = 8
}公共部分类窗口1:窗口
{
    公共窗口1()
    {
        的InitializeComponent();        this.DepartmentsPanel.DataContext =新的DataObject
        {
            部= Department.A | Department.C
        };
    }
}公共类数据对象
{
    公开数据对象()
    {
    }    公共部门部门{搞定;组; }
}公共类DepartmentValueConverter:的IValueConverter
{
    私人部门的目标;    公共DepartmentValueConverter()
    {
    }    公共对象转换(对象的值,类型TARGETTYPE,对象参数,CultureInfo的文化)
    {
        部屏蔽=(部)参数;
        this.target =(部)值;
        返回((掩模&放大器;!this.target)= 0);
    }    公共对象ConvertBack(对象的值,类型TARGETTYPE,对象参数,CultureInfo的文化)
    {
        this.target ^ =(部)参数;
        返回this.target;
    }
}

然后用转换器中的XAML:

 &LT; Window.Resources&GT;
    &LT; L:DepartmentValueConverter X:键=DeptConverter/&GT;
&LT; /Window.Resources> &LT; StackPanel的X:名称=DepartmentsPanel&GT;
    &LT;复选框CONTENT =A
              =器isChecked{结合
                            路径=部门,
                            转换器= {StaticResource的DeptConverter},
                            ConverterParameter = {X:静态L:Department.A}}/&GT;
    &LT;! - 更多 - &GT;
 &LT; / StackPanel的&GT;

编辑:我没有足够的代表(尚未!)下面发表评论,所以我必须更新自己的帖子:(

在过去的评论demwiz.myopenid.com说,但是当涉及到​​双向绑定的ConvertBack分崩离析,以及我已经更新了我的样本code以上处理ConvertBack场景;我也贴一个示例应用程序的工作 这里 (< STRONG>编辑:注意样品code下载还包括转换器的通用版本)

我个人认为这是简单了很多,我希望这有助于。

For those who like a good WPF binding challenge:

I have a nearly functional example of two-way binding a checkbox to an individual bit of a flags enumeration (thanks Ian Oakes, original MSDN post). 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.

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.

Any suggestions or ideas would be extremely appreciated, thanks for anything you can offer!

The enumeration:


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

The XAML usage:


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

The class:


    /// 
    /// 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

解决方案

You could use a value converter. Here's a very specific implementation for the target Enum, but would not be hard to see how to make the converter more generic:

[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;
    }
}

And then use the converter in the 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>

EDIT: I don't have enough "rep" (yet!) to comment below so I have to update my own post :(

In the last comment demwiz.myopenid.com says "but when it comes to two-way binding the ConvertBack falls apart", well I've updated my sample code above to handle the ConvertBack scenario; I've also posted a sample working application here (edit: note that the sample code download also includes a generic version of the converter).

Personally I think this is a lot simpler, I hope this helps.

这篇关于您双向如何复选框绑定到一个标志枚举的个别位?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆