自定义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>

 

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

效果预览:

posted @ 2012-10-08 18:03  ╰→劉じ尛鶴  阅读(1044)  评论(1编辑  收藏  举报