带有CheckBoxes的WPF ComboBox绑定到标志枚举TwoWay

时间:2013-04-19 10:56:58

标签: wpf binding combobox enums checkbox

我正在创建WPF实用程序库,但我仍然坚持如何创建一个易于使用的枚举转换器,这将有助于将任何枚举绑定到ComboBox控件以及使用[Flags]声明枚举时属性,ComboBox将包含绑定到它的复选框数据。解决方案需要是通用的,以便它可以很容易地用于任何枚举,我想它需要经验丰富的WPF开发人员的新眼睛。

到目前为止,我已经提出了ObjectDataProvider作为枚举项和2个转换器的组合,一个用于ItemsSource,另一个用于SelectedValue。 我使用CollectionView作为ItemsSource的基类来使用CurrentChanged,但它不起作用,因为我试图在收集任何CheckBox被检查后没有转换器的ConvertBack被执行时尝试引发OnCurrentChanged。

问题是,除非我更改所选项目(不勾选复选框),否则不会更新ComboBox SelectedValue属性绑定到的数据绑定属性。 我设置了IsSynchronizedWithCurrentItem但它没有用。

如何从数据绑定对象强制更新SelectedValue?我有选择的自由,因此解决方案可能涉及自定义附加属性,继承自ComboBox等。作为WPF的新手,我认为虽然解决方案几乎正确,因为它几乎可以工作,但可以有一种更简单的方法来实现它。 / p>

另一个问题是如何自定义ComboBox上显示的文本,以便它包含勾选复选框的聚合选择。

我上传了VS2012项目here。项目的结构类似于实际应用程序,资源是单独保存的(为简单起见,在App.xaml中)。

抱歉,我没有包含任何代码,但涉及的代码很多。

下面是绑定到标准枚举的CheckBox的XAML声明(尽管使用的枚举是[Flags]),它工作正常,并且CheckBox绑定到[Flags]枚举。

<ComboBox ItemsSource="{Binding Source={StaticResource someItemsSource}}"
              SelectedValue="{Binding BindToSomeItems}"
              Style="{StaticResource enumComboBox}"/>
<ComboBox ItemsSource="{Binding BindToSomeItems, Converter={local:LocalizedFlagsEnumConverter}}"
              IsSynchronizedWithCurrentItem="True"
              SelectedValue="{Binding BindToSomeItems, Mode=TwoWay, Converter={local:SelectedFlagsEnumConverter}}"
              Style="{StaticResource flagsComboBox}"
    />

资源(解释价值转换器的使用):

<Application.Resources>
    <Style x:Key="enumComboBox" TargetType="{x:Type ComboBox}">
        <Setter Property="ItemTemplate">
            <Setter.Value>
                <DataTemplate>
                    <TextBlock Text="{Binding Path=.,Mode=OneWay, Converter={local:LocalizedEnumConverter}}" Style="{x:Null}"/>
                </DataTemplate>
            </Setter.Value>
        </Setter>
    </Style>
    <Style x:Key="flagsComboBox" TargetType="{x:Type ComboBox}">
        <Setter Property="ItemTemplate">
            <Setter.Value>
                <DataTemplate>
                    <StackPanel Orientation="Horizontal">
                        <CheckBox IsChecked="{Binding Path=Checked,Mode=TwoWay}"/>
                        <TextBlock Text="{Binding Path=Name,Mode=OneWay}"/>
                    </StackPanel>
                </DataTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</Application.Resources>

1 个答案:

答案 0 :(得分:3)

我使用自定义复选框来执行此操作。它完美而简单。请参阅课程顶部的评论以了解如何使用它。

希望它有所帮助...

using System;
using System.Windows;
using System.Windows.Controls;

namespace HQ.Wpf.Util.MyControl
{
    /// <summary>
    /// Usage: Bind EnumFlag and Two way binding on EnumValue instead of IsChecked
    /// Example: <myControl:CheckBoxForEnumWithFlagAttribute 
    ///                 EnumValue="{Binding SimulationNatureTypeToCreateStatsCacheAtEndOfSimulation, Mode=TwoWay}" 
    ///                 EnumFlag="{x:Static Core:SimulationNatureType.LoadFlow }">Load Flow results</myControl:CheckBoxForEnumWithFlagAttribute>
    /// </summary>
    public class CheckBoxForEnumWithFlagAttribute : CheckBox
    {
        // ************************************************************************
        public static DependencyProperty EnumValueProperty =
            DependencyProperty.Register("EnumValue", typeof(object), typeof(CheckBoxForEnumWithFlagAttribute), new PropertyMetadata(EnumValueChangedCallback));

        public static DependencyProperty EnumFlagProperty =
            DependencyProperty.Register("EnumFlag", typeof(object), typeof(CheckBoxForEnumWithFlagAttribute), new PropertyMetadata(EnumFlagChangedCallback));

        // ************************************************************************
        public CheckBoxForEnumWithFlagAttribute()
        {
            base.Checked += CheckBoxForEnumWithFlag_Checked;
            base.Unchecked += CheckBoxForEnumWithFlag_Unchecked;
        }

        // ************************************************************************
        private static void EnumValueChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
        {
            var checkBoxForEnumWithFlag = dependencyObject as CheckBoxForEnumWithFlagAttribute;
            if (checkBoxForEnumWithFlag != null)
            {
                checkBoxForEnumWithFlag.RefreshCheckBoxState();
            }
        }

        // ************************************************************************
        private static void EnumFlagChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
        {
            var checkBoxForEnumWithFlag = dependencyObject as CheckBoxForEnumWithFlagAttribute;
            if (checkBoxForEnumWithFlag != null)
            {
                checkBoxForEnumWithFlag.RefreshCheckBoxState();
            }
        }

        // ************************************************************************
        public object EnumValue
        {
            get { return GetValue(EnumValueProperty); }
            set { SetValue(EnumValueProperty, value); }
        }

        // ************************************************************************
        public object EnumFlag
        {
            get { return GetValue(EnumFlagProperty); }
            set { SetValue(EnumFlagProperty, value); }
        }

        // ************************************************************************
        private void RefreshCheckBoxState()
        {
            if (EnumValue != null)
            {
                if (EnumValue is Enum)
                {
                    Type underlyingType = Enum.GetUnderlyingType(EnumValue.GetType());
                    dynamic valueAsInt = Convert.ChangeType(EnumValue, underlyingType);
                    dynamic flagAsInt = Convert.ChangeType(EnumFlag, underlyingType);

                    base.IsChecked = ((valueAsInt & flagAsInt) > 0);
                }
            }
        }

        // ************************************************************************
        private void CheckBoxForEnumWithFlag_Checked(object sender, RoutedEventArgs e)
        {
            RefreshEnumValue();
        }

        // ************************************************************************
        void CheckBoxForEnumWithFlag_Unchecked(object sender, RoutedEventArgs e)
        {
            RefreshEnumValue();
        }

        // ************************************************************************
        private void RefreshEnumValue()
        {
            if (EnumValue != null)
            {
                if (EnumValue is Enum)
                {
                    Type underlyingType = Enum.GetUnderlyingType(EnumValue.GetType());
                    dynamic valueAsInt = Convert.ChangeType(EnumValue, underlyingType);
                    dynamic flagAsInt = Convert.ChangeType(EnumFlag, underlyingType);

                    dynamic newValueAsInt = valueAsInt;
                    if (base.IsChecked == true)
                    {
                        newValueAsInt = valueAsInt | flagAsInt;
                    }
                    else
                    {
                        newValueAsInt = valueAsInt & ~flagAsInt;
                    }

                    if (newValueAsInt != valueAsInt)
                    {
                        object o = Enum.ToObject(EnumValue.GetType(), newValueAsInt);

                        EnumValue = o;
                    }
                }
            }
        }

        // ************************************************************************
    }
}
相关问题