在WPF中将单个带有标志的枚举绑定到多个复选框?

5

我有以下枚举:

[Flags]
public enum SubscriberLevel {
    BRONZE = 1,
    SILVER1 = 2,
    SILVER2 = 4,
    SILVER = SubscriberLevel.SILVER1 | SubscriberLevel.SILVER2,
    GOLD1 = 8,
    GOLD2 = 16,
    GOLD3 = 32,
    GOLD = SubscriberLevel.GOLD1 | SubscriberLevel.GOLD2 | SubscriberLevel.GOLD3,
    DIAMOND1 = 64,
    DIAMOND2 = 128,
    DIAMOND3 = 256,
    DIAMOND = SubscriberLevel.DIAMOND1 | SubscriberLevel.DIAMOND2 | SubscriberLevel.DIAMOND3,
    ALL = SubscriberLevel.BRONZE | SubscriberLevel.SILVER | SubscriberLevel.GOLD | SubscriberLevel.DIAMOND
}

我正在开发一个控件,它应该允许用户通过一系列复选框来指定此枚举中的一个、多个或所有标志。

在控件中,我有一个枚举成员和一个公开枚举的属性。控件本身继承了INotifyPropertyChanged方法,并且当值被设置时,属性的Setter正确地调用了OnPropertyChanged事件处理程序。

我甚至看到了控件内部某些类似功能的东西,但我没有看到它正常工作:

enter image description here

假设用户取消选中了“全部”选项;我希望每个复选框都反映这一点,并被清除,但我没有看到这种情况发生。

目前,我认为我的问题是我将这些复选框的Checked状态绑定到了枚举本身,而我需要做的是将枚举绑定到这些选中框,并且每个复选框代表枚举集合中的一个特定标志。

我已经注意到,在调试控件时,当我取消选中复选框时,值并没有被设置。为什么会这样?

我知道我需要使用转换器来完成这个操作,但现在,我该如何在XAML方面完成这个操作?将控件的DataContext定义为控件本身,然后将枚举标志与它们各自的复选框联系起来?

这是其中一个复选框的示例:

<CheckBox
    x:Name="chkAll" Grid.Row="1" VerticalContentAlignment="Center" VerticalAlignment="Center"
    IsChecked="{Binding Path=SubLevel, Converter={StaticResource ETBC},
    ConverterParameter={x:Static Enums:SubscriberLevel.ALL}, Mode=TwoWay}"/>

这是转换器:
public class EnumToBoolConverter : IValueConverter {
    public object Convert( object value, Type targetType, object parameter, System.Globalization.CultureInfo culture ) {
        return ( ( Enum )value ).HasFlag( ( Enum )parameter );
    }

    public object ConvertBack( object value, Type targetType, object parameter, System.Globalization.CultureInfo culture ) {
        return value.Equals( true ) ? parameter : Binding.DoNothing;
    }
}

这是该控件的代码(当前状态):

public partial class UCSubscriptionLevels : UserControl, INotifyPropertyChanged {
    private SubscriberLevel _SubLevel = SubscriberLevel.ALL;
    public event PropertyChangedEventHandler PropertyChanged;
    public UCSubscriptionLevels( ) { InitializeComponent( ); }

    /// <summary>
    /// Get or Set Subscriber Levels to Build.
    /// </summary>
    public SubscriberLevel SubLevel {
        get { return this._SubLevel; }
        set {
            this._SubLevel = value;
            if ( this.PropertyChanged != null )
                this.PropertyChanged( this, new PropertyChangedEventArgs( "SubLevel" ) );
        }
    }       
}

编辑

已经有一段时间了,我忘记了依赖属性,所以我选择在控件上实现依赖属性而不是属性成员。但是行为仍然不稳定。以下是控件中该属性的新代码:

public static readonly DependencyProperty SubscriberLevelProperty =
            DependencyProperty.Register( "SubLevel", typeof( SubscriberLevel ),
            typeof( UCSubscriptionLevels ), new PropertyMetadata( SubscriberLevel.ALL ) );
.
.
.
/// <summary>
/// Get or Set Subscriber Levels to Build.
/// </summary>
public SubscriberLevel SubLevel {
    get { return ( SubscriberLevel )this.GetValue( UCSubscriptionLevels.SubscriberLevelProperty ); }
    set { this.SetValue( UCSubscriptionLevels.SubscriberLevelProperty, value ); }
}

然而,它仍然没有正常运作...

此外,我不得不改变枚举(从Bronze = 0 改为 Bronze = 1等)。

1个回答

1

好的,这种方法可能是一种非常烦人的解决方法,也可能是与正确解决方案相去甚远,但我能够想出一种处理我所要做的事情的方法。它全部在代码后台完成;在XAML中没有任何绑定发生。

首先是依赖属性:

public static readonly DependencyProperty SubscriberLevelProperty =
            DependencyProperty.Register( "SubLevel", typeof( SubscriberLevel ),
            typeof( UCSubscriptionLevels ), new FrameworkPropertyMetadata( SubscriberLevel.ALL, FrameworkPropertyMetadataOptions.None, ( S, E ) => {
                SubscriberLevel NV = ( SubscriberLevel )E.NewValue;
                UCSubscriptionLevels sender = S as UCSubscriptionLevels;
                sender.GetAllChildren<CheckBox>( ).ToList( ).ForEach( chk => {
                    chk.Checked -= sender.CheckedChanged;
                    chk.Unchecked -= sender.CheckedChanged;
                    chk.IsChecked = NV.HasFlag( sender.dctChkSL[chk] );
                    chk.Checked += new RoutedEventHandler( sender.CheckedChanged );
                    chk.Unchecked += new RoutedEventHandler( sender.CheckedChanged );
                } );
            } ) );

这里的主要变化在于属性本身处理传入的数据更改。我以前在其他事情上做过这个,也取得了一定的成功,所以我在这里尝试了一下。
在“Loaded”事件中,控件添加了一个事件处理程序,用于处理每个“Checkbox Checked”和“Unchecked”事件。此回调将删除该事件处理程序(以避免无限循环),调整选中状态,如果在控件字典中找到,则将其标记为选中;否则,取消选中,然后再次将事件处理程序添加到事件中。
所有事件处理程序所做的就是...
private void CheckedChanged( object sender, RoutedEventArgs e ) { this.SubLevel ^= this.dctChkSL[sender as CheckBox]; }.

这是字典:
private Dictionary<CheckBox, SubscriberLevel> dctChkSL;

这是字典的初始化程序和复选框勾选/取消事件处理程序。
this.dctChkSL = new Dictionary<CheckBox, SubscriberLevel>( ) {
    {this.chkAll, SubscriberLevel.ALL}, {this.chkBronze, SubscriberLevel.BRONZE},
    {this.chkSilver, SubscriberLevel.SILVER}, {this.chkSilver1, SubscriberLevel.SILVER1},
    {this.chkSilver2, SubscriberLevel.SILVER2},
    {this.chkGold, SubscriberLevel.GOLD}, {this.chkGold1, SubscriberLevel.GOLD1},
    {this.chkGold2, SubscriberLevel.GOLD2}, {this.chkGold3, SubscriberLevel.GOLD3},
    {this.chkDiamond, SubscriberLevel.DIAMOND}, {this.chkDiamond1, SubscriberLevel.DIAMOND1},
    {this.chkDiamond2, SubscriberLevel.DIAMOND2}, {this.chkDiamond3, SubscriberLevel.DIAMOND3}
};
this.Loaded += ( S, E ) =>
    this.GetAllChildren<CheckBox>( ).ToList( ).ForEach( chk => {
        chk.Checked += new RoutedEventHandler( this.CheckedChanged );
        chk.Unchecked += new RoutedEventHandler( this.CheckedChanged );
    } );

尽管我希望有一个更简洁的解决方案,但我并没有找到。在有更好的答案之前,这个方案只能使用。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接