• 制作一个CheckListBox控件


    虽然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积分下载。

    下图是最终的结果。

  • 相关阅读:
    Android登入界面
    安卓第4周作业
    第13周作业
    5.28上机作业
    5.22作业
    数据返回值
    登录
    安卓
    安卓第四周
    安卓第四周
  • 原文地址:https://www.cnblogs.com/javawebsoa/p/3047823.html
Copyright © 2020-2023  润新知