1

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

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

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

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

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

我在这里上传了 VS2012 项目。项目的结构类似于具有单独保存资源的真实应用程序(在 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>
4

1 回答 1

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

        // ************************************************************************
    }
}
于 2014-02-26T19:15:31.587 回答