zoukankan      html  css  js  c++  java
  • [WPF] CheckListBox的实现方式分析

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

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

     

    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进行修改。

     

    对于下面的数据类。

    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
    <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了。代码如下:

     

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

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

     

    用法
    <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>

     

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

  • 相关阅读:
    浅析ES6中的iterator
    nodejs中的异步回调机制
    用好js与nodejs中的try...catch
    vscode设置html默认浏览器
    nodejs中相互引用(循环引用)的模块分析
    ES6—带默认值的函数参数及其作用域
    函数声明与函数表达式的区别
    let块级引起的闭包思考
    进程与线程
    angular(^4)-监控表格按键行为的问题
  • 原文地址:https://www.cnblogs.com/nankezhishi/p/checkablelistbox.html
Copyright © 2011-2022 走看看