1+ namespace DataGridExtensions
2+ {
3+ using System ;
4+ using System . Collections . Specialized ;
5+ using System . ComponentModel ;
6+ using System . Windows ;
7+ using System . Windows . Controls ;
8+ using System . Windows . Controls . Primitives ;
9+
10+ using Microsoft . Xaml . Behaviors ;
11+
12+ using TomsToolbox . Wpf ;
13+
14+ /// <summary>
15+ /// A behavior for a list box to handle the interaction between the list box and a "select all" checkbox.
16+ /// </summary>
17+ /// <seealso cref="Behavior{T}" />
18+ internal class ListBoxSelectAllBehavior : Behavior < ListBox >
19+ {
20+ private readonly DispatcherThrottle _collectionChangedThrottle ;
21+ private bool _isListBoxUpdating ;
22+
23+ /// <summary>
24+ /// Initializes a new instance of the <see cref="ListBoxSelectAllBehavior"/> class.
25+ /// </summary>
26+ public ListBoxSelectAllBehavior ( )
27+ {
28+ _collectionChangedThrottle = new DispatcherThrottle ( ListBox_CollectionChanged ) ;
29+ }
30+
31+ /// <summary>
32+ /// Gets or sets a flag indicating if all files are selected. Bind this property to the <see cref="ToggleButton.IsChecked"/> property of a three state check box.
33+ /// </summary>
34+ public bool ? AreAllFilesSelected
35+ {
36+ get => ( bool ? ) GetValue ( AreAllFilesSelectedProperty ) ;
37+ set => SetValue ( AreAllFilesSelectedProperty , value ) ;
38+ }
39+ /// <summary>
40+ /// Identifies the <see cref="AreAllFilesSelected"/> dependency property
41+ /// </summary>
42+ public static readonly DependencyProperty AreAllFilesSelectedProperty =
43+ DependencyProperty . Register ( "AreAllFilesSelected" , typeof ( bool ? ) , typeof ( ListBoxSelectAllBehavior ) , new FrameworkPropertyMetadata ( true , FrameworkPropertyMetadataOptions . BindsTwoWayByDefault , ( sender , e ) => ( ( ListBoxSelectAllBehavior ) sender ) ? . AreAllFilesSelected_Changed ( ( bool ? ) e . NewValue ) ) ) ;
44+
45+ /// <summary>
46+ /// Called after the behavior is attached to an AssociatedObject.
47+ /// </summary>
48+ /// <remarks>
49+ /// Override this to hook up functionality to the AssociatedObject.
50+ /// </remarks>
51+ protected override void OnAttached ( )
52+ {
53+ base . OnAttached ( ) ;
54+
55+ var listBox = AssociatedObject ;
56+ if ( ( listBox == null ) || DesignerProperties . GetIsInDesignMode ( listBox ) )
57+ return ;
58+
59+ listBox . SelectAll ( ) ;
60+
61+ listBox . SelectionChanged += ListBox_SelectionChanged ;
62+ ( ( INotifyCollectionChanged ) listBox . Items ) . CollectionChanged += ( _ , __ ) => _collectionChangedThrottle . Tick ( ) ;
63+ }
64+
65+ private void ListBox_SelectionChanged ( object ? sender , EventArgs ? e )
66+ {
67+ var listBox = AssociatedObject ;
68+ if ( listBox == null )
69+ return ;
70+
71+ try
72+ {
73+ _isListBoxUpdating = true ;
74+
75+ if ( listBox . Items . Count == listBox . SelectedItems . Count )
76+ {
77+ AreAllFilesSelected = true ;
78+ }
79+ else if ( listBox . SelectedItems . Count == 0 )
80+ {
81+ AreAllFilesSelected = false ;
82+ }
83+ else
84+ {
85+ AreAllFilesSelected = null ;
86+ }
87+ }
88+ finally
89+ {
90+ _isListBoxUpdating = false ;
91+ }
92+ }
93+
94+ private void ListBox_CollectionChanged ( )
95+ {
96+ var listBox = AssociatedObject ;
97+
98+ if ( AreAllFilesSelected . GetValueOrDefault ( ) )
99+ {
100+ listBox ? . SelectAll ( ) ;
101+ }
102+ }
103+
104+ private void AreAllFilesSelected_Changed ( bool ? newValue )
105+ {
106+ var listBox = AssociatedObject ;
107+ if ( listBox == null )
108+ return ;
109+
110+ if ( _isListBoxUpdating )
111+ return ;
112+
113+ if ( newValue == null )
114+ {
115+ Dispatcher ? . BeginInvoke ( ( ) => AreAllFilesSelected = false ) ;
116+ return ;
117+ }
118+
119+ if ( newValue . GetValueOrDefault ( ) )
120+ {
121+ listBox . SelectAll ( ) ;
122+ }
123+ else
124+ {
125+ listBox . SelectedIndex = - 1 ;
126+ }
127+ }
128+ }
129+ }
0 commit comments