程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> 自制CheckListBox控件

自制CheckListBox控件

編輯:關於.NET

雖然CodePlex上的WPF擴展的XXX ToolKit源碼中有一個CheckListBox控件,但是,就看它那源代碼,也過於復雜了。並 且,我也希望自己來編寫一個CheckListBox控件。

所謂CheckListBox控件嘛,就是既可以Select又可以Check的 ListBox控件。有人會說,不用寫控件,自定義一個ListBoxItem的模板就行了,也確實可以這樣做,不過,還是有些問題的 ,如果只是重定義ListBoxItem的模板,那僅僅是為其UI上加了個可以顯示一個“勾”的東東而已,而對於邏輯是沒有任何 變化。

既然要可以Select又能Check,那顯然只是重定義模板是不行的。ListBoxItem類本身有一個IsSelected屬性 ,指示列表項是否被選中,而且,人家在ListBox中也有一個SelectedItems屬性,可以獲得ListBox控件的當前選中的所有 項。

很明顯,我們的CheckableListBoxItem要有一個IsChecked屬性來指示列表項是否被Check,而在CheckListBox 控件上應當有一個CheckedItems屬性,可以獲取當前所有被Checked的項。

剛開始,我是計劃讓 CheckableListBoxItem從ContentControl類派生,CheckListBox從ItemsControl派生。但是,轉念一想,其實這所謂的可以 Check的ListBox就是ListBox和CheckBox控件的結合體,而大多數功能與ListBox控件相似,是沒有必要自己重新來寫 ListBox的功能,所以,後來我決定:CheckableListBoxItem從ListBoxItem類派生,CheckListBox則從ListBox派生,但其 中的項目的容器已經不是ListBox了,而是我繼承的CheckableListBoxItem類。

有一點我們要明確的,熟悉WPF的朋 友都知道,在WPF/SL/WP/Store App這一堆使用XAML布局UI的開發框架中,列表控件所獲出來的項並不是項的容器,除非你 在ListBox中直接用ListBoxItem作為對象加進列表控件的集合中,不然會根據你添加的項返回對應的內容,如果你放進去的 是String,那麼拿出來也是String;你放進去的是int,拿出來的也是int。

至於說為什麼要這樣做嘛,很多人不解 了,ListBox裡面明明是放ListBoxItem的,怎麼直接返回其對象了?WPF說的是啥?MVVM,既然要MVVM,當然是你在綁定了 哪個對象,取出來還是那個對象好了,這樣就方便了。

好了,理論的扯完了,就上代碼吧。

using 

System;  
using System.Collections.Generic;  
using System.Collections;  
using System.Linq;  
using System.Text;  
using System.Windows;  
using System.Windows.Controls;  
using System.Windows.Data;  
using System.Windows.Documents;  
using System.Windows.Input;  
using System.Windows.Media;  
using System.Windows.Media.Imaging;  
using System.Windows.Navigation;  
using System.Windows.Shapes;  
using System.Collections.ObjectModel;  
      
namespace MyListBox  
{  
    [StyleTypedProperty(Property = "ItemContainerStyle", StyleTargetType = typeof(CheckableListBoxItem))]  
    public class CheckListBox : ListBox  
    {  
        ObservableCollection<object> m_checkedItems = null;  
        Type m_itemContainerType = typeof(FrameworkElement);//項容器的類型  
        public CheckListBox()  
        {  
            m_checkedItems = new ObservableCollection<object>();  
            // 從CheckListBox類附加的特性中獲取項目容器的類型  
            var attr = this.GetType().GetCustomAttributes(typeof(StyleTypedPropertyAttribute), false);  
            if (attr != null && attr.Length != 0)  
            {  
                StyleTypedPropertyAttribute sty = attr[0] as StyleTypedPropertyAttribute;  
                if (sty != null)  
                {  
                    this.m_itemContainerType = sty.StyleTargetType;  
                }  
            }  
        }  
      
        public static DependencyProperty CheckedItemsProperty = DependencyProperty.Register("CheckedItems", typeof(IList), typeof(CheckListBox), new PropertyMetadata(null));  
      
        public IList CheckedItems  
        {  
            get { return (IList)GetValue(CheckedItemsProperty); }  
        }  
      
        /// <summary>  
        /// 創建項目容器  
        /// </summary>  
        protected override DependencyObject GetContainerForItemOverride()  
        {  
            return Activator.CreateInstance(this.m_itemContainerType) as DependencyObject;  
        }  
      
        /// <summary>  
        /// 當從項目創建項容時,  
        /// 為項目容器注冊事件處理。  
        /// </summary>  
        protected override void PrepareContainerForItemOverride(DependencyObject element, object item)  
        {  
            CheckableListBoxItem ckItem = element as CheckableListBoxItem;  
            ckItem.Checked += clbitem_Checked;  
            ckItem.UnChecked += clbitem_UnChecked;  
            base.PrepareContainerForItemOverride(element, item);  
        }  
      
        /// <summary>  
        /// 當項容被清空時,  
        /// 解除事件處理程序。  
        /// </summary>  
        protected override void ClearContainerForItemOverride(DependencyObject element, object item)  
        {  
            CheckableListBoxItem ckItem = element as CheckableListBoxItem;  
            ckItem.Checked -= clbitem_Checked;  
            ckItem.UnChecked -= clbitem_UnChecked;  
            base.ClearContainerForItemOverride(element, item);  
        }  
      
      
        void clbitem_UnChecked(object sender, RoutedEventArgs e)  
        {  
            CheckableListBoxItem citem = (CheckableListBoxItem)e.Source;  
            object value = citem.Content;  
            m_checkedItems.Remove(value);  
            SetValue(CheckedItemsProperty, m_checkedItems);  
        }  
      
        void clbitem_Checked(object sender, RoutedEventArgs e)  
        {  
            CheckableListBoxItem citem = (CheckableListBoxItem)(e.Source);  
            object value = citem.Content;  
            if (m_checkedItems.SingleOrDefault(o => object.ReferenceEquals(o, value)) == null)  
            {  
                m_checkedItems.Add(value);  
                SetValue(CheckedItemsProperty, m_checkedItems);  
            }  
        }  
    }  
      
      
    public class CheckableListBoxItem : ListBoxItem  
    {  
        static CheckableListBoxItem()  
        {  
            DefaultStyleKeyProperty.OverrideMetadata(typeof(CheckableListBoxItem),  
                new FrameworkPropertyMetadata(typeof(CheckableListBoxItem)));  
        }  
     
     
        #region 屬性  
        public static readonly DependencyProperty IsCheckedProperty =  
            DependencyProperty.Register("IsChecked", typeof(bool), typeof(CheckableListBoxItem), new PropertyMetadata(new PropertyChangedCallback(IsCheckedPropertyChanged)));  
      
        private static void IsCheckedPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)  
        {  
            CheckableListBoxItem lt = d as CheckableListBoxItem;  
            if (lt !=null)  
            {  
                if (e.NewValue != e.OldValue)  
                {  
                    bool b = (bool)e.NewValue;  
                    if (b== true)  
                    {  
                        lt.RaiseCheckedEvent();  
                    }  
                    else
                    {  
                        lt.RaiseUnCheckedEvent();  
                    }  
                }  
            }  
        }  
      
        /// <summary>  
        /// 獲取或設置控件是否被Check  
        /// </summary>  
        public bool IsChecked  
        {  
            get { return (bool)GetValue(IsCheckedProperty); }  
            set { SetValue(IsCheckedProperty, value); }  
        }  
        #endregion  
     
        #region 事件  
        public static readonly RoutedEvent CheckedEvent =  
            EventManager.RegisterRoutedEvent("Checked", RoutingStrategy.Bubble, typeof(RoutedEventHandler), 

typeof(CheckableListBoxItem));  
      
        /// <summary>  
        /// 當控件被Check後發生的事件  
        /// </summary>  
        public event RoutedEventHandler Checked  
        {  
            add  
            {  
                AddHandler(CheckedEvent, value);  
            }  
            remove  
            {  
                RemoveHandler(CheckedEvent, value);  
            }  
        }  
      
        void RaiseCheckedEvent()  
        {  
            RoutedEventArgs arg = new RoutedEventArgs(CheckableListBoxItem.CheckedEvent);  
            RaiseEvent(arg);  
        }  
      
        public static readonly RoutedEvent UnCheckedEvent = EventManager.RegisterRoutedEvent("UnChecked", 

RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(CheckableListBoxItem));  
      
        /// <summary>  
        /// 當控件未被Check後發生  
        /// </summary>  
        public event RoutedEventHandler UnChecked  
        {  
            add { AddHandler(UnCheckedEvent, value); }  
            remove { RemoveHandler(UnCheckedEvent, value); }  
        }  
              
        void RaiseUnCheckedEvent()  
        {  
            RaiseEvent(new RoutedEventArgs(UnCheckedEvent));  
        }  
        #endregion  
    }  
      
}

定義模板的XAML的核心部分如下:

<ControlTemplate x:Key="toggleButtonTmp" 

TargetType="{x:Type ToggleButton}">  
    <Border Background="{TemplateBinding Background}" BorderThickness="{TemplateBinding BorderThickness}" 

BorderBrush="{TemplateBinding BorderBrush}">  
        <Path x:Name="pc" Opacity="0" Margin="{TemplateBinding Padding}" Stretch="Uniform" 

Stroke="{TemplateBinding Foreground}" StrokeThickness="2.68">  
            <Path.Data>  
                <PathGeometry>  
                    <PathFigure StartPoint="0,13">  
                        <PolyLineSegment Points="13,20 20,0"  />  
                    </PathFigure>  
                </PathGeometry>  
            </Path.Data>  
        </Path>  
    </Border>  
    <ControlTemplate.Triggers>  
        <Trigger Property="IsChecked" Value="True">  
            <Setter TargetName="pc" Property="Opacity" Value="1.0"  />  
        </Trigger>  
    </ControlTemplate.Triggers>  
</ControlTemplate>  
      
<Style TargetType="{x:Type local:CheckableListBoxItem}">  
    <Setter Property="Padding" Value="14,2,0,2"  />  
    <Setter Property="Background" Value="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"   />  
    <Setter Property="Template">  
        <Setter.Value>  
            <ControlTemplate TargetType="{x:Type local:CheckableListBoxItem}">  
                <Border x:Name="bd" Background="{TemplateBinding Background}" 

BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}">  
                    <Grid Margin="2.1">  
                        <Grid.ColumnDefinitions>  
                            <ColumnDefinition Width="auto"  />  
                            <ColumnDefinition Width="*"  />  
                        </Grid.ColumnDefinitions>  
                        <ToggleButton x:Name="tog" Grid.Column="0" Margin="1" Width="18" Height="18" 

IsChecked="{Binding IsChecked, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}" 

Template="{StaticResource toggleButtonTmp}" Background="{StaticResource togglebtn_bg}"  />  
                        <ContentPresenter Grid.Column="1" Margin="{TemplateBinding Padding}" 

Content="{TemplateBinding Content}" ContentTemplate="{TemplateBinding ContentTemplate}" 

ContentTemplateSelector="{TemplateBinding ContentTemplateSelector}" HorizontalAlignment="{Binding 

HorizontalContentAlignment, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type 

ItemsControl}}}" VerticalAlignment="{Binding VerticalContentAlignment, RelativeSource={RelativeSource 

FindAncestor, AncestorType={x:Type ItemsControl}}}"  />  
                    </Grid>  
                </Border>  
                <ControlTemplate.Triggers>  
                    <Trigger Property="ListBoxItem.IsSelected" Value="True">  
                        <Setter Property="Background" TargetName="bd"  Value="{StaticResource 

selectedItemBrush}"   />  
                        <Setter Property="Control.Foreground" Value="{StaticResource 

selectedForeBrush}"  />  
                        <Setter TargetName="tog" Property="Control.Foreground" Value="{StaticResource 

selectedTgbtnFore}"  />  
                    </Trigger>  
                    <MultiTrigger >  
                        <MultiTrigger.Conditions>  
                            <Condition Property="UIElement.IsMouseOver" Value="True"  />  
                            <Condition Property="ListBoxItem.IsSelected" Value="False"  />  
                        </MultiTrigger.Conditions>   
                        <Setter TargetName="bd" Property="Background" Value="{StaticResource 

hoverBrush}"  />  
                    </MultiTrigger>  
                </ControlTemplate.Triggers>  
            </ControlTemplate>  
        </Setter.Value>  
    </Setter>  
</Style>

接下來就是測試一下控件。

<Grid>  
    <Grid.ColumnDefinitions>  
        <ColumnDefinition  />  
        <ColumnDefinition  />  
    </Grid.ColumnDefinitions>  
    <local:CheckListBox x:Name="lb" Grid.Column="0" SelectionMode="Multiple" 

ItemTemplate="{StaticResource stuTmp}"  />  
    <Grid Grid.Column="1">  
        <Grid.RowDefinitions>  
            <RowDefinition Height="auto"  />  
            <RowDefinition  />  
            <RowDefinition Height="auto"  />  
            <RowDefinition  />  
        </Grid.RowDefinitions>  
        <TextBlock Grid.Row="0" Text="被Checked的項:" FontSize="17" Margin="5,3,0,2"  />  
        <ListBox Grid.Row="1" ItemsSource="{Binding Path=CheckedItems,ElementName=lb}" Margin="5" 

ItemTemplate="{StaticResource stuTmp}"  />  
        <TextBlock Grid.Row="2" Text="被Selected的項:" FontSize="17" Margin="5,0,0,2"  />  
        <ListBox Grid.Row="3" Margin="5" ItemsSource="{Binding Path=SelectedItems,ElementName=lb}" 

ItemTemplate="{StaticResource stuTmp}"  />  
    </Grid>  
</Grid>
lb.ItemsSource = new Student[]  
{  
    new Student{ Name="狗", Age=30 },  
    new Student{ Name="兔", Age=31 },  
    new Student{ Name="蛇", Age=18 },  
    new Student{ Name="雞", Age=22 },  
    new Student{ Name="貓", Age=24 },  
    new Student{ Name="青蛙", Age=28 },  
    new Student{ Name="猴", Age=19 }  
};

代碼不完全,但主要的我都放出來了,隨後我把所有代碼都上傳到【資源】中,相當優惠,0積分下載。

下圖是最終的結果。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved