zoukankan      html  css  js  c++  java
  • 自定义ListBox,实现单多选切换(复选框)

    今天花了一天的时间收集了一些关于ListBox多选功能的文章研究了一下,并实现了自己的ListBox多选效果。

    下面先分享一下我收集的几篇ListBox多选功能的文章:

    (1)卤面网:a68215812 的文章:玩转控件之ListBox 多选功能,实现批量操作

    (2)卤面网:chenxx08 的文章:[原创开发教程] 卤面网&亿动智道教程大赛+【原创特效列表控件系列之多选】

    (3)博客园:Terry_龙 的文章:【WP7进阶】——分享一个可供切换状态的ListBox组件

    接着说下自己所做的Listbox多选控件,我现在把它命名为SinOrMulListBox:

    (1)声明一个类SinOrMulListBox,让它继承自ListBox。

        同时定义一个依赖属性IsMultipleSelect便于我们在单选和多选之间切换。

        由于ListBox的默认容器是ListBoxItem,而我们要在容器中添加一个复选框,所以为了方便我们定义一个自己的默认容器暂命名为SinOrMulListBoxItem。要在我们的SinOrMulListBox使用自己的容器SinOrMulListBoxItem则需要重写父类ListBox里的两个函数:GetContainerForItemOverride()IsItemItsOwnContainerOverride(object item)

    主要代码:

    Generic.xaml       默认样式如下:

      1 <ResourceDictionary
      2     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"       
      3     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      4     xmlns:Locat="clr-namespace:SinOrMulListBox">
      5 
      6     <!--在SinOrMulListBoxItem容器中我们在原有的ListBoxItem样式中添加了一个复选框,并添加一个状态分组MultiSelectionStates让SinOrMulListBox在单多选中切换,为了切换效果平滑过渡,我们添加了一个过渡动画-->
      7     <Style TargetType="Locat:SinOrMulListBoxItem">
      8         <Setter Property="Background" Value="Transparent"/>
      9         <Setter Property="BorderThickness" Value="0"/>
     10         <Setter Property="BorderBrush" Value="Transparent"/>
     11         <Setter Property="Padding" Value="0"/>
     12         <Setter Property="HorizontalContentAlignment" Value="Left"/>
     13         <Setter Property="VerticalContentAlignment" Value="Top"/>
     14         <Setter Property="Template">
     15             <Setter.Value>
     16                 <ControlTemplate TargetType="Locat:SinOrMulListBoxItem">
     17                     <Border x:Name="LayoutRoot" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" HorizontalAlignment="{TemplateBinding HorizontalAlignment}" VerticalAlignment="{TemplateBinding VerticalAlignment}">
     18                         <VisualStateManager.VisualStateGroups>
     19                             <VisualStateGroup x:Name="CommonStates">
     20                                 <VisualState x:Name="Normal"/>
     21                                 <VisualState x:Name="MouseOver"/>
     22                                 <VisualState x:Name="Disabled">
     23                                     <Storyboard>
     24                                         <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Background" Storyboard.TargetName="LayoutRoot">
     25                                             <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource TransparentBrush}"/>
     26                                         </ObjectAnimationUsingKeyFrames>
     27                                         <DoubleAnimation Duration="0" To=".5" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="ContentContainer"/>
     28                                     </Storyboard>
     29                                 </VisualState>
     30                             </VisualStateGroup>
     31                             <VisualStateGroup x:Name="SelectionStates">
     32                                 <VisualState x:Name="Unselected"/>
     33                                 <VisualState x:Name="Selected">
     34                                     <Storyboard>
     35                                         <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Foreground" Storyboard.TargetName="ContentContainer">
     36                                             <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource PhoneAccentBrush}"/>
     37                                         </ObjectAnimationUsingKeyFrames>
     38                                     </Storyboard>
     39                                 </VisualState>
     40                             </VisualStateGroup>
     41                             <VisualStateGroup x:Name="MultiSelectionStates">
     42                                 <VisualState x:Name="EnableSelection">
     43                                     <Storyboard>
     44                                         <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="SelecterCheckBox">
     45                                             <EasingDoubleKeyFrame KeyTime="0" Value="0"/>
     46                                             <EasingDoubleKeyFrame KeyTime="0:0:0.5" Value="1"/>
     47                                         </DoubleAnimationUsingKeyFrames>
     48                                         <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(CompositeTransform.ScaleX)" Storyboard.TargetName="SelecterCheckBox">
     49                                             <EasingDoubleKeyFrame KeyTime="0" Value="0"/>
     50                                             <EasingDoubleKeyFrame KeyTime="0:0:0.5" Value="1">
     51                                                 <EasingDoubleKeyFrame.EasingFunction>
     52                                                     <CircleEase EasingMode="EaseOut"/>
     53                                                 </EasingDoubleKeyFrame.EasingFunction>
     54                                             </EasingDoubleKeyFrame>
     55                                         </DoubleAnimationUsingKeyFrames>
     56                                         <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(CompositeTransform.TranslateX)" Storyboard.TargetName="ContentContainer">
     57                                             <EasingDoubleKeyFrame KeyTime="0" Value="-62"/>
     58                                             <EasingDoubleKeyFrame KeyTime="0:0:0.5" Value="0">
     59                                                 <EasingDoubleKeyFrame.EasingFunction>
     60                                                     <CircleEase EasingMode="EaseOut"/>
     61                                                 </EasingDoubleKeyFrame.EasingFunction>
     62                                             </EasingDoubleKeyFrame>
     63                                         </DoubleAnimationUsingKeyFrames>
     64                                     </Storyboard>
     65                                 </VisualState>
     66                                 <VisualState x:Name="DisableSelection">
     67                                     <Storyboard>
     68                                         <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="SelecterCheckBox">
     69                                             <EasingDoubleKeyFrame KeyTime="0" Value="1"/>
     70                                             <EasingDoubleKeyFrame KeyTime="0:0:0.5" Value="0"/>
     71                                         </DoubleAnimationUsingKeyFrames>
     72                                         <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(CompositeTransform.TranslateX)" Storyboard.TargetName="ContentContainer">
     73                                             <EasingDoubleKeyFrame KeyTime="0" Value="0"/>
     74                                             <EasingDoubleKeyFrame KeyTime="0:0:0.5" Value="-62">
     75                                                 <EasingDoubleKeyFrame.EasingFunction>
     76                                                     <CircleEase EasingMode="EaseOut"/>
     77                                                 </EasingDoubleKeyFrame.EasingFunction>
     78                                             </EasingDoubleKeyFrame>
     79                                         </DoubleAnimationUsingKeyFrames>
     80                                         <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(CompositeTransform.ScaleX)" Storyboard.TargetName="SelecterCheckBox">
     81                                             <EasingDoubleKeyFrame KeyTime="0" Value="1"/>
     82                                             <EasingDoubleKeyFrame KeyTime="0:0:0.5" Value="0">
     83                                                 <EasingDoubleKeyFrame.EasingFunction>
     84                                                     <CircleEase EasingMode="EaseOut"/>
     85                                                 </EasingDoubleKeyFrame.EasingFunction>
     86                                             </EasingDoubleKeyFrame>
     87                                         </DoubleAnimationUsingKeyFrames>
     88                                     </Storyboard>
     89                                 </VisualState>
     90                             </VisualStateGroup>
     91                         </VisualStateManager.VisualStateGroups>
     92                         <Grid>
     93                             <Grid.ColumnDefinitions>
     94                                 <ColumnDefinition Width="Auto"/>
     95                                 <ColumnDefinition Width="*"/>
     96                             </Grid.ColumnDefinitions>
     97 
     98                             <CheckBox x:Name="SelecterCheckBox" Width="62" VerticalAlignment="Top" Margin="5,-12,0,0" IsChecked="{Binding IsSelected, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}" Opacity="0">
     99                                 <CheckBox.RenderTransform>
    100                                     <CompositeTransform ScaleX="0"/>
    101                                 </CheckBox.RenderTransform>
    102                             </CheckBox>
    103                             <ContentControl Grid.Column="1" x:Name="ContentContainer" ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" Foreground="{TemplateBinding Foreground}" HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}">
    104                                 <ContentControl.RenderTransform>
    105                                     <CompositeTransform TranslateX="-62"/>
    106                                 </ContentControl.RenderTransform>
    107                             </ContentControl>
    108                         </Grid>
    109                     </Border>
    110                 </ControlTemplate>
    111             </Setter.Value>
    112         </Setter>
    113     </Style>
    114 
    115     <!--SinOrMulListBox的样式其实和ListBox是一样的,我们主要修改是继承ListBoxItem的容器控件SinOrMulListBoxItem的样式-->
    116     <Style TargetType="Locat:SinOrMulListBox">
    117         <Setter Property="Background" Value="Transparent"/>
    118         <Setter Property="Foreground" Value="{StaticResource PhoneForegroundBrush}"/>
    119         <Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Disabled"/>
    120         <Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto"/>
    121         <Setter Property="BorderThickness" Value="0"/>
    122         <Setter Property="BorderBrush" Value="Transparent"/>
    123         <Setter Property="Padding" Value="0"/>
    124         <Setter Property="Template">
    125             <Setter.Value>
    126                 <ControlTemplate TargetType="Locat:SinOrMulListBox">
    127                     <ScrollViewer x:Name="ScrollViewer" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Foreground="{TemplateBinding Foreground}" Padding="{TemplateBinding Padding}">
    128                         <ItemsPresenter/>
    129                     </ScrollViewer>
    130                 </ControlTemplate>
    131             </Setter.Value>
    132         </Setter>
    133     </Style>
    134 
    135 </ResourceDictionary>

      其中SinOrMulListBox的样式其实和ListBox是一样的,我们主要修改是继承ListBoxItem的容器控件SinOrMulListBoxItem的样式;而在SinOrMulListBoxItem容器中我们在原有的ListBoxItem样式中添加了一个复选框,并添加一个状态分组MultiSelectionStates让SinOrMulListBox在单多选中切换,为了切换效果平滑过渡,我们添加了一个过渡动画

    SinOrMulListBox.cs      主要代码如下:

     1 using tool;
     2 using System;
     3 using System.Windows;
     4 using System.Windows.Controls;
     5 
     6 namespace SinOrMulListBox
     7 {
     8     [StyleTypedProperty(Property = "ItemContainerStyle", StyleTargetType = typeof(SinOrMulListBoxItem))]
     9     public class SinOrMulListBox : ListBox
    10     {
    11         public SinOrMulListBox()
    12         {
    13             DefaultStyleKey = typeof(SinOrMulListBox);
    14         }
    15 
    16         #region Rewrite the superclass method
    17 
    18         protected override DependencyObject GetContainerForItemOverride()
    19         {
    20             var item = new SinOrMulListBoxItem();
    21             if (ItemContainerStyle != null)
    22                 item.Style = ItemContainerStyle;
    23             return item;
    24         }
    25         protected override bool IsItemItsOwnContainerOverride(object item)
    26         {
    27             bool nBool = item is SinOrMulListBoxItem;
    28 
    29             return nBool;
    30         }
    31 
    32         public override void OnApplyTemplate()
    33         {
    34             base.OnApplyTemplate();
    35             IsMultipleSelect = SelectionMode != SelectionMode.Single;
    36         }
    37 
    38         #endregion
    39 
    40         #region Custom fuction
    41 
    42         /// <summary>
    43         /// Don't all selected .
    44         /// </summary>
    45         public void UnSelectAll()
    46         {
    47             var items = this.Descendants<SinOrMulListBoxItem>();
    48             SelectedItem = null;
    49             items.ForEachEx(t => t.IsSelected = false);//t.CanCheck
    50         }
    51 
    52         #endregion
    53 
    54         #region Custom DependencyObject
    55         /// <summary>
    56         /// Whether can choose more options?
    57         /// </summary>
    58         public bool IsMultipleSelect
    59         {
    60             get { return (bool)GetValue(IsMultipleSelectProperty); }
    61             set { SetValue(IsMultipleSelectProperty, value); }
    62         }
    63 
    64         public static readonly DependencyProperty IsMultipleSelectProperty =
    65             DependencyProperty.Register("IsMultipleSelect", typeof(bool), typeof(SinOrMulListBox), new PropertyMetadata(false, OnIsMultipleSelectPropertyChanged));
    66 
    67         private static void OnIsMultipleSelectPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
    68         {
    69             var listBox = sender as SinOrMulListBox;
    70             //Debug.Assert(listBox != null);
    71             listBox.OnIsMultipleSelectChanged();
    72         }
    73 
    74         private void OnIsMultipleSelectChanged()
    75         {
    76             var items = this.Descendants<SinOrMulListBoxItem>();
    77             if (IsMultipleSelect)
    78             {
    79                 SelectionMode = SelectionMode.Multiple;
    80                 items.ForEachEx(t => t.CanCheck = true);
    81             }
    82             else
    83             {
    84                 SelectionMode = SelectionMode.Single;
    85                 SelectedItem = null;
    86                 items.ForEachEx(t => t.CanCheck = false);
    87             }
    88         }
    89         #endregion
    90     }
    91 }

       这里面我额外添加了一个方法UnSelectAll(),用于对应继承ListBox的方法SelectAll(),实现全部不选择。

       在OnIsMultipleSelectChanged()中items.ForEachEx(t => t.CanCheck = false)来启动SinOrMulListBoxItem中复选框的显示与不显示动画

     (2)声明一个类SinOrMulListBoxItem,让它继承自ListBoxItem。

        同时定义一个依赖属性CanCheck便于我们在单选和多选之间切换,并启动过渡动画。

        CheckBox的IsChecked绑定ListBoxItem的IsSelected属性,通过ListBox.SelectItems取出选中集合

    SinOrMulListBoxItem.cs      主要代码如下:

    View Code
     1 using tool;
     2 using System;
     3 using System.Linq;
     4 using System.Windows;
     5 using System.Windows.Media;
     6 using System.Windows.Controls;
     7 
     8 namespace SinOrMulListBox
     9 {
    10     
    11     public class SinOrMulListBoxItem : ListBoxItem
    12     {
    13         public SinOrMulListBoxItem()
    14         {
    15             DefaultStyleKey = typeof(SinOrMulListBoxItem);
    16         }
    17 
    18         public override void OnApplyTemplate()
    19         {
    20             base.OnApplyTemplate();
    21         }
    22 
    23         #region Custom DependencyProperty
    24         /// <summary>
    25         /// Is used check box ?
    26         /// </summary>
    27         internal bool CanCheck
    28         {
    29             get { return (bool)GetValue(CanCheckProperty); }
    30             set { SetValue(CanCheckProperty, value); }
    31         }
    32 
    33         internal static readonly DependencyProperty CanCheckProperty =
    34             DependencyProperty.Register("CanCheck", typeof(bool), typeof(SinOrMulListBoxItem), new PropertyMetadata(false, OnCanCheckPropertyChanged));
    35 
    36         private static void OnCanCheckPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
    37         {
    38             var item = sender as SinOrMulListBoxItem;
    39             //Debug.Assert(item != null);
    40             item.OnCanCheckChanged();
    41         }
    42 
    43         private void OnCanCheckChanged()
    44         {
    45             VisualStateManager.GoToState(this, CanCheck ? "EnableSelection" : "DisableSelection", true);
    46         }
    47         #endregion 
    48     }
    49 
    50     public class StaticFunction
    51     {
    52         /// <summary>
    53         /// Find father control
    54         /// </summary>
    55         /// <typeparam name="T"></typeparam>
    56         /// <param name="obj"></param>
    57         /// <returns></returns>
    58         public static T FindParentOfType<T>(DependencyObject obj) where T : FrameworkElement
    59         {
    60             DependencyObject parent = VisualTreeHelper.GetParent(obj);
    61             while (parent != null)
    62             {
    63                 if (parent is T)
    64                 {
    65                     return (T)parent;
    66                 }
    67                 parent = VisualTreeHelper.GetParent(parent);
    68             }
    69             return null;
    70         }
    71     }
    72 }

      注意:到这其实应该说所有所有功能都OK了,可运行后发现个问题~~~~(>_<)~~~~   在显示区内的SinOrMulListBoxItem没有问题,但在显示区外的SinOrMulListBoxItem有的显示复选框有的不显示复选框,而功能什么运行使用又都正常,很郁闷。根据这些现象我猜测问题出现在ListBox本身的虚拟化机制上,也就是说,当SinOrMulListBoxItem在显示区时候才会new而不在显示区的SinOrMulListBoxItem是不会new出来的。因此我们应该让在显示区外的SinOrMulListBoxItem被new的时候知道自己是否应该显示复选框,为此我们添加了一些代码解决这一问题。下面是解决后的完整代码:

     1 using tool;
     2 using System;
     3 using System.Linq;
     4 using System.Windows;
     5 using System.Windows.Media;
     6 using System.Windows.Controls;
     7 
     8 namespace SinOrMulListBox
     9 {
    10     
    11     public class SinOrMulListBoxItem : ListBoxItem
    12     {
    13         public SinOrMulListBoxItem()
    14         {
    15             DefaultStyleKey = typeof(SinOrMulListBoxItem);
    16 
    17             Loaded += SinOrMulListBoxItem_Loaded;
    18         }
    19 
    20         public override void OnApplyTemplate()
    21         {
    22             base.OnApplyTemplate();
    23         }
    24 
    25         #region Custom DependencyProperty
    26         /// <summary>
    27         /// Is used check box ?
    28         /// </summary>
    29         internal bool CanCheck
    30         {
    31             get { return (bool)GetValue(CanCheckProperty); }
    32             set { SetValue(CanCheckProperty, value); }
    33         }
    34 
    35         internal static readonly DependencyProperty CanCheckProperty =
    36             DependencyProperty.Register("CanCheck", typeof(bool), typeof(SinOrMulListBoxItem), new PropertyMetadata(false, OnCanCheckPropertyChanged));
    37 
    38         private static void OnCanCheckPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
    39         {
    40             var item = sender as SinOrMulListBoxItem;
    41             //Debug.Assert(item != null);
    42             item.OnCanCheckChanged();
    43         }
    44 
    45         private void OnCanCheckChanged()
    46         {
    47             VisualStateManager.GoToState(this, CanCheck ? "EnableSelection" : "DisableSelection", true);
    48         }
    49         #endregion 
    50 
    51         private SinOrMulListBox _listBox;
    52         private SinOrMulListBox ListBox
    53         {
    54             get { return _listBox ?? (_listBox = this.Ancestors<SinOrMulListBox>().FirstOrDefault()); }
    55         }
    56 
    57         void SinOrMulListBoxItem_Loaded(object sender, RoutedEventArgs e)
    58         {
    59             CanCheck = ListBox.IsMultipleSelect;
    60         }
    61     }
    62 
    63     public class StaticFunction
    64     {
    65         /// <summary>
    66         /// Find father control
    67         /// </summary>
    68         /// <typeparam name="T"></typeparam>
    69         /// <param name="obj"></param>
    70         /// <returns></returns>
    71         public static T FindParentOfType<T>(DependencyObject obj) where T : FrameworkElement
    72         {
    73             DependencyObject parent = VisualTreeHelper.GetParent(obj);
    74             while (parent != null)
    75             {
    76                 if (parent is T)
    77                 {
    78                     return (T)parent;
    79                 }
    80                 parent = VisualTreeHelper.GetParent(parent);
    81             }
    82             return null;
    83         }
    84     }
    85 }

      在这里再附上子父控件的查找类,这个是从[原创开发教程] 卤面网&亿动智道教程大赛+【原创特效列表控件系列之多选】中拷贝的。
    TreeExtensions.cs     所有代码:

    View Code
      1 using System;
      2 using System.Linq;
      3 using System.Windows;
      4 using System.Windows.Data;
      5 using System.Windows.Media;
      6 using System.Collections.Generic;
      7 
      8 namespace tool
      9 {
     10     public static class TreeExtensions
     11     {
     12         /// <summary>
     13         /// 返回可视树中所有子代元素集合(不包括本身)
     14         /// </summary>
     15         public static IEnumerable<DependencyObject> Descendants(this DependencyObject item)
     16         {
     17             foreach (var child in item.ChildrenEx())
     18             {
     19                 yield return child;
     20 
     21                 foreach (var grandChild in child.Descendants())
     22                 {
     23                     yield return grandChild;
     24                 }
     25             }
     26         }
     27 
     28         /// <summary>
     29         /// 返回可视树中所有子代元素集合(包括本身)
     30         /// </summary>
     31         public static IEnumerable<DependencyObject> DescendantsAndSelf(this DependencyObject item)
     32         {
     33             yield return item;
     34 
     35             foreach (var child in item.Descendants())
     36             {
     37                 yield return child;
     38             }
     39         }
     40 
     41         /// <summary>
     42         /// 返回可视树中所有父代元素集合(不包括本身)
     43         /// </summary>
     44         public static IEnumerable<DependencyObject> Ancestors(this DependencyObject item)
     45         {
     46             var parent = item.ParentEx();
     47             while (parent != null)
     48             {
     49                 yield return parent;
     50                 parent = parent.ParentEx();
     51             }
     52         }
     53 
     54         /// <summary>
     55         /// 返回可视树中所有父代元素集合(包括本身)
     56         /// </summary>
     57         public static IEnumerable<DependencyObject> AncestorsAndSelf(this DependencyObject item)
     58         {
     59             yield return item;
     60 
     61             foreach (var ancestor in item.Ancestors())
     62             {
     63                 yield return ancestor;
     64             }
     65         }
     66 
     67         /// <summary>
     68         /// 返回可视树中下一代所有的子元素(不包括自身)
     69         /// </summary>
     70         public static IEnumerable<DependencyObject> Elements(this DependencyObject item)
     71         {
     72             return item.ChildrenEx();
     73         }
     74 
     75         /// <summary>
     76         /// 返回可视树中与该元素位于同一级别且文档顺序位于该元素前面的所有元素
     77         /// </summary>
     78         public static IEnumerable<DependencyObject> ElementsBeforeSelf(this DependencyObject item)
     79         {
     80             var parent = item.ParentEx();
     81             if (parent == null)
     82                 yield break;
     83             foreach (var child in item.Elements().TakeWhile(child => !child.Equals(item)))
     84             {
     85                 yield return child;
     86             }
     87         }
     88 
     89         /// <summary>
     90         /// 返回可视树中与该元素位于同一级别且文档顺序位于该元素后面的所有元素
     91         /// </summary>
     92         public static IEnumerable<DependencyObject> ElementsAfterSelf(this DependencyObject item)
     93         {
     94             var parent = item.ParentEx();
     95             if (parent == null)
     96                 yield break;
     97             var afterSelf = false;
     98             foreach (var child in parent.Elements())
     99             {
    100                 if (afterSelf)
    101                     yield return child;
    102                 if (child.Equals(item))
    103                     afterSelf = true;
    104             }
    105         }
    106 
    107         /// <summary>
    108         /// 返回可视树中下一代所有的子元素(包括本身)
    109         /// </summary>
    110         public static IEnumerable<DependencyObject> ElementsAndSelf(this DependencyObject item)
    111         {
    112             yield return item;
    113 
    114             foreach (var child in item.Elements())
    115             {
    116                 yield return child;
    117             }
    118         }
    119 
    120         /// <summary>
    121         /// 返回可视树中所有子代中类型符合要求的元素集合(不包括自身)
    122         /// </summary>
    123         public static IEnumerable<T> Descendants<T>(this DependencyObject item)
    124         {
    125             return item.Descendants().Where(i => i is T).Cast<T>();
    126         }
    127 
    128         /// <summary>
    129         /// 返回可视树中与该元素位于同一级别且文档顺序位于该元素前面的符合类型要求的所有元素
    130         /// </summary>
    131         public static IEnumerable<T> ElementsBeforeSelf<T>(this DependencyObject item)
    132         {
    133             return item.ElementsBeforeSelf().Where(i => i is T).Cast<T>();
    134         }
    135 
    136         /// <summary>
    137         /// 返回可视树中与该元素位于同一级别且文档顺序位于该元素后面的符合类型要求的所有元素
    138         /// </summary>
    139         public static IEnumerable<T> ElementsAfterSelf<T>(this DependencyObject item)
    140         {
    141             return item.ElementsAfterSelf().Where(i => i is T).Cast<T>();
    142         }
    143 
    144         /// <summary>
    145         /// 返回可视树中所有子代中类型符合要求的元素集合(包括自身)
    146         /// </summary>
    147         public static IEnumerable<T> DescendantsAndSelf<T>(this DependencyObject item)
    148         {
    149             return item.DescendantsAndSelf().Where(i => i is T).Cast<T>();
    150         }
    151 
    152         /// <summary>
    153         /// 返回可视树中所有父代中类型符合要求的元素集合(不包括自身)
    154         /// </summary>
    155         public static IEnumerable<T> Ancestors<T>(this DependencyObject item)
    156         {
    157             return item.Ancestors().Where(i => i is T).Cast<T>();
    158         }
    159 
    160         /// <summary>
    161         /// 返回可视树中所有父代中类型符合要求的元素集合(包括自身)
    162         /// which match the given type.
    163         /// </summary>
    164         public static IEnumerable<T> AncestorsAndSelf<T>(this DependencyObject item)
    165         {
    166             return item.AncestorsAndSelf().Where(i => i is T).Cast<T>();
    167         }
    168 
    169         /// <summary>
    170         /// 返回可视树中下一代符合类型要求的所有子元素(不包括自身)
    171         /// </summary>
    172         public static IEnumerable<T> Elements<T>(this DependencyObject item)
    173         {
    174             return item.Elements().Where(i => i is T).Cast<T>();
    175         }
    176 
    177         /// <summary>
    178         /// 返回可视树中下一代符合类型要求的所有子元素(包括自身)
    179         /// </summary>
    180         public static IEnumerable<T> ElementsAndSelf<T>(this DependencyObject item)
    181         {
    182             return item.ElementsAndSelf().Where(i => i is T).Cast<T>();
    183         }
    184 
    185     }
    186 
    187     public static class EnumerableTreeExtensions
    188     {
    189         /// <summary>
    190         /// 对元素集合应用相同的函数,并返回该函数的结果集合
    191         /// </summary>
    192         private static IEnumerable<DependencyObject> DrillDown(this IEnumerable<DependencyObject> items,
    193             Func<DependencyObject, IEnumerable<DependencyObject>> function)
    194         {
    195             return items.SelectMany(function);
    196         }
    197 
    198         /// <summary>
    199         /// 对元素集合应用相同的函数,并返回该函数结果符合类型要求的集合
    200         /// </summary>
    201         public static IEnumerable<T> DrillDown<T>(this IEnumerable<DependencyObject> items,
    202             Func<DependencyObject, IEnumerable<DependencyObject>> function)
    203             where T : DependencyObject
    204         {
    205             return items.SelectMany(item => function(item).OfType<T>());
    206         }
    207 
    208         /// <summary>
    209         /// 返回集合中所有的子代元素集合(不包括自身)
    210         /// </summary>
    211         public static IEnumerable<DependencyObject> Descendants(this IEnumerable<DependencyObject> items)
    212         {
    213             return items.DrillDown(i => i.Descendants());
    214         }
    215 
    216         /// <summary>
    217         /// 返回集合中所有的子代元素集合(包括自身)
    218         /// </summary>
    219         public static IEnumerable<DependencyObject> DescendantsAndSelf(this IEnumerable<DependencyObject> items)
    220         {
    221             return items.DrillDown(i => i.DescendantsAndSelf());
    222         }
    223 
    224         /// <summary>
    225         /// 返回集合中所有的父代元素集合(不包括自身)
    226         /// </summary>
    227         public static IEnumerable<DependencyObject> Ancestors(this IEnumerable<DependencyObject> items)
    228         {
    229             return items.DrillDown(i => i.Ancestors());
    230         }
    231 
    232         /// <summary>
    233         /// 返回集合中所有的父代元素集合(包括自身)
    234         /// </summary>
    235         public static IEnumerable<DependencyObject> AncestorsAndSelf(this IEnumerable<DependencyObject> items)
    236         {
    237             return items.DrillDown(i => i.AncestorsAndSelf());
    238         }
    239 
    240         /// <summary>
    241         /// Returns a collection of child elements.
    242         /// </summary>
    243         public static IEnumerable<DependencyObject> Elements(this IEnumerable<DependencyObject> items)
    244         {
    245             return items.DrillDown(i => i.Elements());
    246         }
    247 
    248         /// <summary>
    249         /// Returns a collection containing this element and all child elements.
    250         /// </summary>
    251         public static IEnumerable<DependencyObject> ElementsAndSelf(this IEnumerable<DependencyObject> items)
    252         {
    253             return items.DrillDown(i => i.ElementsAndSelf());
    254         }
    255 
    256         /// <summary>
    257         /// Returns a collection of descendant elements which match the given type.
    258         /// </summary>
    259         public static IEnumerable<T> Descendants<T>(this IEnumerable<DependencyObject> items)
    260             where T : DependencyObject
    261         {
    262             return items.DrillDown<T>(i => i.Descendants());
    263         }
    264 
    265         /// <summary>
    266         /// Returns a collection containing this element and all descendant elements.
    267         /// which match the given type.
    268         /// </summary>
    269         public static IEnumerable<T> DescendantsAndSelf<T>(this IEnumerable<DependencyObject> items)
    270             where T : DependencyObject
    271         {
    272             return items.DrillDown<T>(i => i.DescendantsAndSelf());
    273         }
    274 
    275         /// <summary>
    276         /// Returns a collection of ancestor elements which match the given type.
    277         /// </summary>
    278         public static IEnumerable<T> Ancestors<T>(this IEnumerable<DependencyObject> items)
    279             where T : DependencyObject
    280         {
    281             return items.DrillDown<T>(i => i.Ancestors());
    282         }
    283 
    284         /// <summary>
    285         /// Returns a collection containing this element and all ancestor elements.
    286         /// which match the given type.
    287         /// </summary>
    288         public static IEnumerable<T> AncestorsAndSelf<T>(this IEnumerable<DependencyObject> items)
    289             where T : DependencyObject
    290         {
    291             return items.DrillDown<T>(i => i.AncestorsAndSelf());
    292         }
    293 
    294         /// <summary>
    295         /// Returns a collection of child elements which match the given type.
    296         /// </summary>
    297         public static IEnumerable<T> Elements<T>(this IEnumerable<DependencyObject> items)
    298             where T : DependencyObject
    299         {
    300             return items.DrillDown<T>(i => i.Elements());
    301         }
    302 
    303         /// <summary>
    304         /// Returns a collection containing this element and all child elements.
    305         /// which match the given type.
    306         /// </summary>
    307         public static IEnumerable<T> ElementsAndSelf<T>(this IEnumerable<DependencyObject> items)
    308             where T : DependencyObject
    309         {
    310             return items.DrillDown<T>(i => i.ElementsAndSelf());
    311         }
    312     }
    313 
    314     public static class CommonExtension
    315     {
    316         #region DependencyObject Extension
    317 
    318         #region 监听依赖属性变化
    319         private static readonly Dictionary<DependencyObject, List<PropertyCallbackInfo>> _callbackDic = new Dictionary<DependencyObject, List<PropertyCallbackInfo>>();
    320         private static readonly Dictionary<DependencyProperty, string> _propertyNames = new Dictionary<DependencyProperty, string>();
    321 
    322         /// <summary>
    323         /// 添加依赖属性改变时的回调函数
    324         /// </summary>
    325         /// <param name="obj"></param>
    326         /// <param name="propertyName"></param>
    327         /// <param name="callback"></param>
    328         public static void AddPropertyChangedCallback(this DependencyObject obj, string propertyName, PropertyChangedCallback callback)
    329         {
    330             try
    331             {
    332                 var attachName = "ListenAttached" + propertyName;
    333                 if (!_callbackDic.ContainsKey(obj))
    334                 {
    335                     _callbackDic.Add(obj, new List<PropertyCallbackInfo>());
    336                 }
    337                 var infoList = _callbackDic[obj];
    338                 var info = infoList.FirstOrDefault(t => t.PropertyName == attachName);
    339                 if (info == null)
    340                 {
    341                     info = new PropertyCallbackInfo { PropertyName = attachName };
    342                     var binding = new Binding(propertyName) { Source = obj };
    343                     var pro = DependencyProperty.RegisterAttached(attachName, typeof(object), obj.GetType(), new PropertyMetadata(OnPropertyChanged));
    344                     BindingOperations.SetBinding(obj, pro, binding);
    345                     _propertyNames.Add(pro, attachName);
    346                     infoList.Add(info);
    347                 }
    348                 info.Callbacks.Add(callback);
    349             }
    350             catch (Exception e)
    351             {
    352                 //Debug.WriteLine("执行CommonExtension中的AddPropertyChangedCallback函数出错:" + e.Message);
    353             }
    354         }
    355 
    356         /// <summary>
    357         /// 移除依赖属性改变时的回调函数
    358         /// </summary>
    359         /// <param name="obj"></param>
    360         /// <param name="propertyName"></param>
    361         /// <param name="callback"></param>
    362         public static void RemovePropertyChangedCallback(this DependencyObject obj, string propertyName, PropertyChangedCallback callback)
    363         {
    364             List<PropertyCallbackInfo> infoList;
    365             if (!_callbackDic.TryGetValue(obj, out infoList))
    366                 return;
    367             var attachName = "ListenAttached" + propertyName;
    368             var info = infoList.FirstOrDefault(t => t.PropertyName == attachName);
    369             if (info == null)
    370                 return;
    371             info.Callbacks.Remove(callback);
    372         }
    373 
    374         private static void OnPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
    375         {
    376             string name;
    377             if (!_propertyNames.TryGetValue(e.Property, out name))
    378                 return;
    379             List<PropertyCallbackInfo> infoList;
    380             if (!_callbackDic.TryGetValue(sender, out infoList))
    381                 return;
    382             var info = infoList.FirstOrDefault(t => t.PropertyName == name);
    383             if (info == null)
    384                 return;
    385             info.Callbacks.ForEach(t => t(sender, e));
    386         }
    387 
    388         private class PropertyCallbackInfo
    389         {
    390             public string PropertyName { get; set; }
    391             private readonly List<PropertyChangedCallback> _callbacks = new List<PropertyChangedCallback>();
    392             public List<PropertyChangedCallback> Callbacks
    393             {
    394                 get { return _callbacks; }
    395             }
    396         }
    397         #endregion
    398 
    399         /// <summary>
    400         /// 返回可视树中该元素的所有子元素
    401         /// </summary>
    402         /// <param name="item"></param>
    403         /// <returns></returns>
    404         public static IEnumerable<DependencyObject> ChildrenEx(this DependencyObject item)
    405         {
    406             var childrenCount = VisualTreeHelper.GetChildrenCount(item);
    407             for (var i = 0; i < childrenCount; i++)
    408             {
    409                 yield return VisualTreeHelper.GetChild(item, i);
    410             }
    411         }
    412 
    413         /// <summary>
    414         /// 返回可视树中该元素的父元素
    415         /// </summary>
    416         /// <param name="item"></param>
    417         /// <returns></returns>
    418         public static DependencyObject ParentEx(this DependencyObject item)
    419         {
    420             return VisualTreeHelper.GetParent(item);
    421         }
    422         #endregion
    423 
    424         #region IEnumerable Extension
    425         /// <summary>
    426         /// 枚举中的每个对象执行相同的动作
    427         /// </summary>
    428         /// <typeparam name="T"></typeparam>
    429         /// <param name="items"></param>
    430         /// <param name="action"></param>
    431         public static void ForEachEx<T>(this IEnumerable<T> items, Action<T> action)
    432         {
    433             foreach (var item in items)
    434                 action(item);
    435         }
    436 
    437         /// <summary>
    438         /// 枚举中的每个对象执行相同的动作
    439         /// </summary>
    440         /// <typeparam name="T">枚举类型</typeparam>
    441         /// <typeparam name="S">需要执行动作的类型</typeparam>
    442         /// <param name="items"></param>
    443         /// <param name="action"></param>
    444         public static void ForEachEx<S, T>(this IEnumerable<T> items, Action<S> action)
    445             where S : class
    446         {
    447             foreach (var item in items.OfType<S>())
    448             {
    449                 action(item);
    450             }
    451         }
    452 
    453         #endregion
    454     }
    455 }

    (3)下面是我们的测试代码。这个比较简单直接上代码

    MainPage.xaml         主要代码

    View Code
     1 <phone:PhoneApplicationPage xmlns:my="clr-namespace:SinOrMulListBox;assembly=SinOrMulListBox"  
     2     x:Class="TestLstBoxDemo.MainPage"
     3     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
     4     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
     5     xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
     6     xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
     7     xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
     8     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
     9     mc:Ignorable="d" d:DesignWidth="480" d:DesignHeight="696"
    10     FontFamily="{StaticResource PhoneFontFamilyNormal}"
    11     FontSize="{StaticResource PhoneFontSizeNormal}"
    12     Foreground="{StaticResource PhoneForegroundBrush}"
    13     SupportedOrientations="Portrait" Orientation="Portrait"
    14     shell:SystemTray.IsVisible="True" d:DataContext="{d:DesignData Design/ViewModelSampleData.xaml}">
    15 
    16     <!--LayoutRoot is the root grid where all page content is placed-->
    17     <Grid x:Name="LayoutRoot" Background="Transparent">
    18         <Grid.RowDefinitions>
    19             <RowDefinition Height="Auto"/>
    20             <RowDefinition Height="*"/>
    21         </Grid.RowDefinitions>
    22 
    23         <!--TitlePanel contains the name of the application and page title-->
    24         <StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
    25             <TextBlock x:Name="ApplicationTitle" Text="MY APPLICATION" Style="{StaticResource PhoneTextNormalStyle}"/>
    26         </StackPanel>
    27 
    28         <!--ContentPanel - place additional content here-->
    29         <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
    30             <my:SinOrMulListBox Name="listBoxWithBoxes" Margin="0,0,0,0" IsMultipleSelect="True" ItemsSource="{Binding SimpleModels}">
    31                 <my:SinOrMulListBox.ItemTemplate>
    32                     <DataTemplate>
    33                         <StackPanel Orientation="Horizontal" Margin="0,0,0,20">
    34                             <Rectangle Height="100" Width="100" Fill="#FFE5001b" Margin="0,0,9,0"/>
    35                             <StackPanel>
    36                                 <TextBlock Text="{Binding Name}" TextWrapping="Wrap" Style="{StaticResource PhoneTextLargeStyle}"/>
    37                                 <TextBlock Text="{Binding Description}" TextWrapping="Wrap" Margin="12,-6,12,0" Style="{StaticResource PhoneTextSubtleStyle}"/>
    38                             </StackPanel>
    39                         </StackPanel>
    40                     </DataTemplate>
    41                 </my:SinOrMulListBox.ItemTemplate>
    42             </my:SinOrMulListBox>
    43         </Grid>
    44     </Grid>
    45  
    46     <!--Sample code showing usage of ApplicationBar-->
    47     <phone:PhoneApplicationPage.ApplicationBar>
    48         <shell:ApplicationBar IsVisible="True" IsMenuEnabled="True">
    49             <shell:ApplicationBarIconButton IconUri="/Images/appbar_button1.png" Text="Button 1" Click="ApplicationBarIconButton_Click"/>
    50             <shell:ApplicationBarIconButton IconUri="/Images/appbar_button1.png" Text="Button 1" Click="ApplicationBarIconButton_Click_1"/>
    51             <shell:ApplicationBarIconButton IconUri="/Images/appbar_button1.png" Text="Button 1" Click="ApplicationBarIconButton_Click_2"/>
    52 
    53             <shell:ApplicationBar.MenuItems>
    54                 <shell:ApplicationBarMenuItem Text="开启单选" Click="ApplicationBarMenuItem_Click"/>
    55                 <shell:ApplicationBarMenuItem Text="开启多选" Click="ApplicationBarMenuItem_Click_1"/>
    56             </shell:ApplicationBar.MenuItems>
    57         </shell:ApplicationBar>
    58     </phone:PhoneApplicationPage.ApplicationBar>
    59 
    60 </phone:PhoneApplicationPage>

    MainPage.xaml.cs         主要代码

    View Code
     1 using System;
     2 using System.Collections.Generic;
     3 using System.Linq;
     4 using System.Net;
     5 using System.Windows;
     6 using System.Windows.Controls;
     7 using System.Windows.Documents;
     8 using System.Windows.Input;
     9 using System.Windows.Media;
    10 using System.Windows.Media.Animation;
    11 using System.Windows.Shapes;
    12 using Microsoft.Phone.Controls;
    13 
    14 namespace TestLstBoxDemo
    15 {
    16     public partial class MainPage : PhoneApplicationPage
    17     {
    18         // Constructor
    19         public MainPage()
    20         {
    21             InitializeComponent();
    22 
    23             this.Loaded += new RoutedEventHandler(MainPage_Loaded);
    24         }
    25 
    26         void MainPage_Loaded(object sender, RoutedEventArgs e)
    27         {
    28             DataContext = App.ViewModel;
    29         }
    30 
    31         protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
    32         {
    33             if (!App.ViewModel.IsDataLoaded)
    34             {
    35                 App.ViewModel.LoadData();
    36             }
    37             base.OnNavigatedTo(e);
    38         }
    39 
    40         //查看选中所有选项
    41         private void ApplicationBarIconButton_Click(object sender, EventArgs e)
    42         {
    43             List<SimpleModel> SelectedItems = new List<SimpleModel>();
    44             foreach (var item in listBoxWithBoxes.SelectedItems)
    45             {
    46                 SimpleModel nModel = item as SimpleModel;
    47                 if (nModel != null)
    48                 {
    49                     SelectedItems.Add(nModel);
    50                 }
    51             }
    52         }
    53         //全选
    54         private void ApplicationBarIconButton_Click_1(object sender, EventArgs e)
    55         {
    56             listBoxWithBoxes.SelectAll();
    57         }
    58         //全部不选
    59         private void ApplicationBarIconButton_Click_2(object sender, EventArgs e)
    60         {
    61             listBoxWithBoxes.UnSelectAll();
    62         }
    63         //开启单选
    64         private void ApplicationBarMenuItem_Click(object sender, EventArgs e)
    65         {
    66             listBoxWithBoxes.IsMultipleSelect = false;
    67         }
    68         //开启多选
    69         private void ApplicationBarMenuItem_Click_1(object sender, EventArgs e)
    70         {
    71             listBoxWithBoxes.IsMultipleSelect = true;
    72         }
    73     }
    74 }

    ViewModel    主要代码

    SimpleModel.cs
     1 using System;
     2 using System.Net;
     3 using System.ComponentModel;
     4 
     5 namespace TestLstBoxDemo
     6 {
     7     public class SimpleModel : INotifyPropertyChanged
     8     {
     9         protected string itsName;
    10         protected string itsDescription;
    11 
    12         public event PropertyChangedEventHandler PropertyChanged;
    13 
    14         public string Name
    15         {
    16             get { return this.itsName; }
    17             set { this.itsName = value; NotifyPropertyChanged("Name"); }
    18         }
    19 
    20 
    21         public string Description
    22         {
    23             get { return this.itsDescription; }
    24             set { this.itsDescription = value; NotifyPropertyChanged("Description"); }
    25         }
    26 
    27 
    28 
    29         protected void NotifyPropertyChanged(string thePropertyName)
    30         {
    31             if (this.PropertyChanged != null)
    32             {
    33                 this.PropertyChanged(this, new PropertyChangedEventArgs(thePropertyName));
    34             }
    35         }
    36 
    37     }
    38 }
    ListModel.cs
     1 using System;
     2 using System.ComponentModel;
     3 using System.Collections.ObjectModel;
     4 
     5 namespace TestLstBoxDemo
     6 {
     7     public class ListModel : INotifyPropertyChanged
     8     {
     9         public event PropertyChangedEventHandler PropertyChanged;
    10 
    11         public ObservableCollection<SimpleModel> SimpleModels { get; private set; }
    12 
    13 
    14 
    15         public bool IsDataLoaded { get; private set; }
    16 
    17         public ListModel()
    18         {
    19             this.SimpleModels = new ObservableCollection<SimpleModel>();
    20         }
    21 
    22 
    23         /// <summary>
    24         /// 加载数据
    25         /// </summary>
    26         public void LoadData()
    27         {
    28             for (int i = 1; i < 30; i++)
    29             {
    30                 this.SimpleModels.Add(new SimpleModel() { Name = "" + i + "", Description = "这是第" + i + "项数据" });
    31             }
    32             this.IsDataLoaded = true;
    33         }
    34 
    35 
    36         protected void NotifyPropertyChanged(string thePropertyName)
    37         {
    38             if (this.PropertyChanged != null)
    39             {
    40                 this.PropertyChanged(this, new PropertyChangedEventArgs(thePropertyName));
    41             }
    42         }
    43 
    44     }
    45 }

    这里使用了Terry_龙 的文章:【WP7进阶】——分享一个可供切换状态的ListBox组件中提到的小技巧,代码如下

    ViewModelSampleData.xaml
     1 <viewModels:ListModel 
     2     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"       
     3     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
     4     xmlns:viewModels="clr-namespace:TestLstBoxDemo">
     5 
     6     <viewModels:ListModel.SimpleModels>
     7         <viewModels:SimpleModel Name="测试第一项" Description="这是测试的第一个节点" />
     8         <viewModels:SimpleModel Name="测试第二项" Description="这是测试的第二个节点" />
     9     </viewModels:ListModel.SimpleModels>
    10 
    11 </viewModels:ListModel>

    完整代码如下:猛点击这里

    效果预览:

  • 相关阅读:
    HTML5学习记录
    CSS学习记录
    HTML扩展(thead,tbody,tfoot标签的使用)
    测删除功能
    jmeter-连接数据库
    jmeter-正则表达式提取器
    jmeter常用函数
    java基础(二)
    git基本使用
    波特的钻石模型
  • 原文地址:https://www.cnblogs.com/qq278360339/p/2715628.html
Copyright © 2011-2022 走看看