CheckListBox的实现方式分析

 

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

 

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

 

CheckListBoxItemContainerStyle 

Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/--><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>
CheckListBoxItemContainerStyle

 

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

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

 

对于下面的数据类。

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(thisnew PropertyChangedEventArgs(propertyName));         }     } }

 

 

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

DataItemCheckListBoxStyle 

Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/--><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>
DataItemCheckListBoxStyle

 

在上面的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了。代码如下:

ToggleButtonProperty 

Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->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;
        };
    }
}
ToggleButtonProperty

 

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

用法

Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/--><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>
用法

 

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

 

转载至http://www.cnblogs.com/nankezhishi/archive/2009/12/30/checkablelistbox.html

posted @ 2013-08-06 01:28  heavi  阅读(489)  评论(0编辑  收藏  举报