[WPF] CheckListBox的实现方式分析

实际项目中常常要实现有CheckBox列表框。但是WPF没有自带这样的一个控件,下面就用Style来实现这样的功能。而对于CheckBox列表框,又常常会有一个Select AllCheckBox来表示当前列表框的选择状态。这个功能也会被包含在下面的示例之中。效果如下图所示。

 

对于单纯的,没有后台数据绑定的情况下,这个功能可以用ItemContainerStyle来实现。代码如下: 

 

ExpandedBlockStart.gif CheckListBoxItemContainerStyle
< Style  x:Key ="CheckListBoxItemContainerStyle"
       TargetType
=" {x:Type ListBoxItem} " >
  
<!-- Set it un-focusable, becaues the CheckBox in it should be focusable and only it. -->
  
< Setter  Property ="Focusable"  Value ="False" />
  
< Setter  Property ="Template" >
    
< Setter.Value >
      
< ControlTemplate  TargetType =" {x:Type ListBoxItem} " >
        
< CheckBox  Content =" {TemplateBinding Content} "
              ContentTemplate
=" {TemplateBinding ContentTemplate} "
              ContentTemplateSelector
=" {TemplateBinding ContentTemplateSelector} "
              IsChecked
=" {Binding IsSelected, RelativeSource={RelativeSource TemplatedParent}} " />
      
</ ControlTemplate >
    
</ Setter.Value >
  
</ Setter >
</ Style >

 

 

其中要对ContentContentTemplate等属性进行绑定,以方便对其进行扩展,保证其通用性。这个Style一般会放在Application级别的Resource中。

对于有后台数据绑定的情况,一般会有双个属性要绑定,一个是CheckBox里的Content,一个是CheckBoxIsChecked。绑定的路径,只有在用一个StyleListBox那里才知道,所以并不能写在这个Style里,否则会破坏这个Style的通用性。比较合理的方式是基于这个现有的Style进行修改。

 

对于下面的数据类。

ExpandedBlockStart.gif DataItem Class
public   class  DataItem : INotifyPropertyChanged
{
    
private   string  name;
    
private   bool  isEnabled;

    
public   string  Name
    {
        
get  {  return  name; }
        
set
        {
            name 
=  value;
            OnPropertyChanged(
" Name " );
        }
    }

    
public   bool  IsEnabled
    {
        
get  {  return  isEnabled; }
        
set
        {
            isEnabled 
=  value;
            OnPropertyChanged(
" IsEnabled " );
        }
    }

    
#region  INotifyPropertyChanged Members

    
public   event  PropertyChangedEventHandler PropertyChanged;

    
#endregion

    
protected   virtual   void  OnPropertyChanged( string  propertyName)
    {
        PropertyChangedEventHandler temp 
=  PropertyChanged;
        
if  (temp  !=   null )
        {
            temp(
this new  PropertyChangedEventArgs(propertyName));
        }
    }
}

 

 

我们需要下面这个有针对性的Style来应用数据绑定。

ExpandedBlockStart.gif DataItemCheckListBoxStyle
< Style  x:Key ="DataItemCheckListBoxStyle"
       TargetType
=" {x:Type ListBox} "
       BasedOn
=" {StaticResource {x:Type ListBox}} " >
  
< Setter  Property ="ItemTemplate" >
    
< Setter.Value >
      
< DataTemplate >
        
< TextBlock  Text =" {Binding Name} " />
      
</ DataTemplate >
    
</ Setter.Value >
  
</ Setter >
  
< Setter  Property ="ItemContainerStyle" >
    
< Setter.Value >
      
< Style  TargetType =" {x:Type ListBoxItem} "
             BasedOn
=" {StaticResource CheckListBoxItemContainerStyle} " >
        
< Setter  Property ="IsSelected"
                Value
=" {Binding IsEnabled} " />
        
< Setter  Property ="Margin"  Value ="2,2,0,0" />
      
</ Style >
    
</ Setter.Value >
  
</ Setter >
  
< Setter  Property ="SelectionMode"  Value ="Multiple" />
</ Style >

 

在上面的Style中,使用了ItemTemplate来指定CheckBox里的Content绑定到的属性,并把ListBoxItemIsSelected绑定数据的相应属性上。由于这个Style是针对特定数据写的,所以应当放置在使用这个StyleListBox所在的WindowResource中。

 

当然,也可以为ListBox添加两个绑定类型的Attached Property来实现一个通用的Style。不过这个Property一样要在使用的地方设置,其实没有太大区别。有兴趣的读者可以自己试一下。

 

对于Select All这个CheckBox而言,用Attached Property倒是很方便。给CheckBox添加一个SyncTarget属性指向要同步的ListBox,就可以在Window.xaml.cs之外的地方同步CheckBoxListBox了。代码如下:

 

ExpandedBlockStart.gif ToggleButtonProperty
public   class  ToggleButtonProperty
{
    
//  Using a DependencyProperty as the backing store for SyncTarget.  This enables animation, styling, binding, etc...
     public   static   readonly  DependencyProperty SyncTargetProperty  =
        DependencyProperty.RegisterAttached(
" SyncTarget " typeof (ListBox),  typeof (ToggleButtonProperty),  new  UIPropertyMetadata( new  PropertyChangedCallback(OnSyncTargetChanged)));

    
public   static  ListBox GetSyncTarget(DependencyObject obj)
    {
        
return  obj.GetValue(SyncTargetProperty)  as  ListBox;
    }

    
public   static   void  SetSyncTarget(DependencyObject obj, ListBox value)
    {
        obj.SetValue(SyncTargetProperty, value);
    }

    
private   static   void  OnSyncTargetChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
    {
        ToggleButton checker 
=  sender  as  ToggleButton;
        
if  (checker  ==   null )
        {
            
throw   new  InvalidOperationException( " SyncTarget property only works on ToggleButton. " );
        }

        ListBox targetList 
=  e.NewValue  as  ListBox;
        
if  (targetList  ==   null )
        {
            
throw   new  InvalidOperationException( " Sync target must be a ListBox. " );
        }

        
// TODO: Un-subscribe OldValue's Event.

        checker.Checked 
+=  (s, a)  =>
        {
            targetList.SelectAll();
        };

        checker.Unchecked 
+=  (s, a)  =>
        {
            targetList.UnselectAll();
        };

        targetList.SelectionChanged 
+=  (s, a)  =>
        {
            checker.IsChecked 
=  targetList.SelectedItems.Count  ==   0   ?   false  :
                targetList.SelectedItems.Count 
==  targetList.Items.Count  ?  ( bool ? ) true  :  null ;
        };
    }
}

 

使用方式也很简单。如下代码所示。

 

ExpandedBlockStart.gif 用法
< DockPanel  Margin ="12" >
    
< CheckBox  Content ="Select All"
              Margin
="0,0,0,5"
              DockPanel.Dock
="Top"
              ext:ToggleButtonProperty.SyncTarget
=" {Binding ElementName=checkListBox} " />
    
< ListBox  x:Name ="checkListBox"
             Style
=" {StaticResource DataItemCheckListBoxStyle} "
             ItemsSource
=" {Binding Path=Items, ElementName=mainWindow} " />
</ DockPanel >

 

 

完整的项目文件可以从这里下载

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值