@@ -10,57 +10,49 @@ namespace CommunityToolkit.WinUI;
1010/// </summary>
1111public static partial class ListViewExtensions
1212{
13- private static Dictionary < IObservableVector < object > , ListViewBase > _itemsForList = new Dictionary < IObservableVector < object > , ListViewBase > ( ) ;
13+ private static readonly Dictionary < IObservableVector < object > , ListViewBase > _trackedListViews = [ ] ;
1414
1515 /// <summary>
1616 /// Attached <see cref="DependencyProperty"/> for binding a <see cref="Brush"/> as an alternate background color to a <see cref="ListViewBase"/>
1717 /// </summary>
18- public static readonly DependencyProperty AlternateColorProperty = DependencyProperty . RegisterAttached ( "AlternateColor" , typeof ( Brush ) , typeof ( ListViewExtensions ) , new PropertyMetadata ( null , OnAlternateColorPropertyChanged ) ) ;
18+ public static readonly DependencyProperty AlternateColorProperty =
19+ DependencyProperty . RegisterAttached ( "AlternateColor" , typeof ( Brush ) , typeof ( ListViewExtensions ) ,
20+ new PropertyMetadata ( null , OnAlternateColorPropertyChanged ) ) ;
1921
2022 /// <summary>
2123 /// Attached <see cref="DependencyProperty"/> for binding a <see cref="DataTemplate"/> as an alternate template to a <see cref="ListViewBase"/>
2224 /// </summary>
23- public static readonly DependencyProperty AlternateItemTemplateProperty = DependencyProperty . RegisterAttached ( "AlternateItemTemplate" , typeof ( DataTemplate ) , typeof ( ListViewExtensions ) , new PropertyMetadata ( null , OnAlternateItemTemplatePropertyChanged ) ) ;
25+ public static readonly DependencyProperty AlternateItemTemplateProperty =
26+ DependencyProperty . RegisterAttached ( "AlternateItemTemplate" , typeof ( DataTemplate ) , typeof ( ListViewExtensions ) ,
27+ new PropertyMetadata ( null , OnAlternateItemTemplatePropertyChanged ) ) ;
2428
2529 /// <summary>
2630 /// Gets the alternate <see cref="Brush"/> associated with the specified <see cref="ListViewBase"/>
2731 /// </summary>
2832 /// <param name="obj">The <see cref="ListViewBase"/> to get the associated <see cref="Brush"/> from</param>
2933 /// <returns>The <see cref="Brush"/> associated with the <see cref="ListViewBase"/></returns>
30- public static Brush GetAlternateColor ( ListViewBase obj )
31- {
32- return ( Brush ) obj . GetValue ( AlternateColorProperty ) ;
33- }
34+ public static Brush GetAlternateColor ( ListViewBase obj ) => ( Brush ) obj . GetValue ( AlternateColorProperty ) ;
3435
3536 /// <summary>
3637 /// Sets the alternate <see cref="Brush"/> associated with the specified <see cref="DependencyObject"/>
3738 /// </summary>
3839 /// <param name="obj">The <see cref="ListViewBase"/> to associate the <see cref="Brush"/> with</param>
3940 /// <param name="value">The <see cref="Brush"/> for binding to the <see cref="ListViewBase"/></param>
40- public static void SetAlternateColor ( ListViewBase obj , Brush value )
41- {
42- obj . SetValue ( AlternateColorProperty , value ) ;
43- }
41+ public static void SetAlternateColor ( ListViewBase obj , Brush value ) => obj . SetValue ( AlternateColorProperty , value ) ;
4442
4543 /// <summary>
4644 /// Gets the <see cref="DataTemplate"/> associated with the specified <see cref="ListViewBase"/>
4745 /// </summary>
4846 /// <param name="obj">The <see cref="ListViewBase"/> to get the associated <see cref="DataTemplate"/> from</param>
4947 /// <returns>The <see cref="DataTemplate"/> associated with the <see cref="ListViewBase"/></returns>
50- public static DataTemplate GetAlternateItemTemplate ( ListViewBase obj )
51- {
52- return ( DataTemplate ) obj . GetValue ( AlternateItemTemplateProperty ) ;
53- }
48+ public static DataTemplate GetAlternateItemTemplate ( ListViewBase obj ) => ( DataTemplate ) obj . GetValue ( AlternateItemTemplateProperty ) ;
5449
5550 /// <summary>
5651 /// Sets the <see cref="DataTemplate"/> associated with the specified <see cref="ListViewBase"/>
5752 /// </summary>
5853 /// <param name="obj">The <see cref="ListViewBase"/> to associate the <see cref="DataTemplate"/> with</param>
5954 /// <param name="value">The <see cref="DataTemplate"/> for binding to the <see cref="ListViewBase"/></param>
60- public static void SetAlternateItemTemplate ( ListViewBase obj , DataTemplate value )
61- {
62- obj . SetValue ( AlternateItemTemplateProperty , value ) ;
63- }
55+ public static void SetAlternateItemTemplate ( ListViewBase obj , DataTemplate value ) => obj . SetValue ( AlternateItemTemplateProperty , value ) ;
6456
6557 private static void OnAlternateColorPropertyChanged ( DependencyObject sender , DependencyPropertyChangedEventArgs args )
6658 {
@@ -70,23 +62,29 @@ private static void OnAlternateColorPropertyChanged(DependencyObject sender, Dep
7062 // Cleanup existing subscriptions
7163 listViewBase . ContainerContentChanging -= ColorContainerContentChanging ;
7264 listViewBase . Items . VectorChanged -= ColorItemsVectorChanged ;
73- listViewBase . Unloaded -= OnListViewBaseUnloaded ;
65+ listViewBase . Unloaded -= OnListViewBaseUnloaded_AltRow ;
7466
75- _itemsForList [ listViewBase . Items ] = listViewBase ;
67+ _trackedListViews [ listViewBase . Items ] = listViewBase ;
7668
7769 // Resubscribe to events as necessary
7870 if ( GetAlternateColor ( listViewBase ) is not null )
7971 {
8072 listViewBase . ContainerContentChanging += ColorContainerContentChanging ;
8173 listViewBase . Items . VectorChanged += ColorItemsVectorChanged ;
82- listViewBase . Unloaded += OnListViewBaseUnloaded ;
74+ listViewBase . Unloaded += OnListViewBaseUnloaded_AltRow ;
8375 }
8476 }
8577
8678 private static void ColorContainerContentChanging ( ListViewBase sender , ContainerContentChangingEventArgs args )
8779 {
88- var itemContainer = args . ItemContainer as Control ;
89- SetItemContainerBackground ( sender , itemContainer , args . ItemIndex ) ;
80+ // Get the row's item container, or contents as a fallback
81+ Control ? control = args . ItemContainer ?? args . Item as Control ;
82+
83+ // Update the row background if the item was found
84+ if ( control is not null )
85+ {
86+ SetRowBackground ( sender , control , args . ItemIndex ) ;
87+ }
9088 }
9189
9290 private static void OnAlternateItemTemplatePropertyChanged ( DependencyObject sender , DependencyPropertyChangedEventArgs args )
@@ -96,74 +94,55 @@ private static void OnAlternateItemTemplatePropertyChanged(DependencyObject send
9694
9795 // Cleanup existing subscriptions
9896 listViewBase . ContainerContentChanging -= ItemTemplateContainerContentChanging ;
99- listViewBase . Unloaded -= OnListViewBaseUnloaded ;
97+ listViewBase . Unloaded -= OnListViewBaseUnloaded_AltRow ;
10098
10199 // Resubscribe to events as necessary
102100 if ( GetAlternateItemTemplate ( listViewBase ) != null )
103101 {
104102 listViewBase . ContainerContentChanging += ItemTemplateContainerContentChanging ;
105- listViewBase . Unloaded += OnListViewBaseUnloaded ;
103+ listViewBase . Unloaded += OnListViewBaseUnloaded_AltRow ;
106104 }
107105 }
108106
109107 private static void ItemTemplateContainerContentChanging ( ListViewBase sender , ContainerContentChangingEventArgs args )
110108 {
111- if ( args . ItemIndex % 2 == 0 )
112- {
113- args . ItemContainer . ContentTemplate = GetAlternateItemTemplate ( sender ) ;
114- }
115- else
116- {
117- args . ItemContainer . ContentTemplate = sender . ItemTemplate ;
118- }
119- }
120-
121- private static void OnListViewBaseUnloaded ( object sender , RoutedEventArgs e )
122- {
123- if ( sender is ListViewBase listViewBase )
124- {
125- _itemsForList . Remove ( listViewBase . Items ) ;
126-
127- listViewBase . ContainerContentChanging -= ItemContainerStretchDirectionChanging ;
128- listViewBase . ContainerContentChanging -= ItemTemplateContainerContentChanging ;
129- listViewBase . ContainerContentChanging -= ColorContainerContentChanging ;
130- listViewBase . Items . VectorChanged -= ColorItemsVectorChanged ;
131- listViewBase . Unloaded -= OnListViewBaseUnloaded ;
132- }
109+ var template = args . ItemIndex % 2 == 0 ? GetAlternateItemTemplate ( sender ) : sender . ItemTemplate ;
110+ args . ItemContainer . ContentTemplate = template ;
133111 }
134112
135113 private static void ColorItemsVectorChanged ( IObservableVector < object > sender , IVectorChangedEventArgs args )
136114 {
137- // If the index is at the end we can ignore
115+ // If the index is at the end, no other items were affected
116+ // and there's no action to take
138117 if ( args . Index == ( sender . Count - 1 ) )
139- {
140118 return ;
141- }
142119
143- // Only need to handle Inserted and Removed because we'll handle everything else in the
144- // ColorContainerContentChanging method
145- if ( ( args . CollectionChange == CollectionChange . ItemInserted ) || ( args . CollectionChange == CollectionChange . ItemRemoved ) )
120+ // This function is for updating indirectly affected items
121+ // Therefore we only need to handle items inserted and removed where every
122+ // item beneath would potentially change if they are even or odd.
123+ if ( args . CollectionChange is not ( CollectionChange . ItemInserted or CollectionChange . ItemRemoved ) )
124+ return ;
125+
126+ // Attempt to get the list view for the affected items
127+ _trackedListViews . TryGetValue ( sender , out ListViewBase ? listViewBase ) ;
128+ if ( listViewBase is null )
129+ return ;
130+
131+ int index = ( int ) args . Index ;
132+ for ( int i = index ; i < sender . Count ; i ++ )
146133 {
147- _itemsForList . TryGetValue ( sender , out ListViewBase ? listViewBase ) ;
148- if ( listViewBase == null )
149- return ;
134+ // Get item container or element at index
135+ var itemContainer = listViewBase . ContainerFromIndex ( i ) as Control ;
136+ itemContainer ??= listViewBase . Items [ i ] as Control ;
150137
151- int index = ( int ) args . Index ;
152- for ( int i = index ; i < sender . Count ; i ++ )
138+ if ( itemContainer is not null )
153139 {
154- // Get item container or element at index
155- var itemContainer = listViewBase . ContainerFromIndex ( i ) as Control ;
156- itemContainer ??= listViewBase . Items [ i ] as Control ;
157-
158- if ( itemContainer is not null )
159- {
160- SetItemContainerBackground ( listViewBase , itemContainer , i ) ;
161- }
140+ SetRowBackground ( listViewBase , itemContainer , i ) ;
162141 }
163142 }
164143 }
165144
166- private static void SetItemContainerBackground ( ListViewBase sender , Control itemContainer , int itemIndex )
145+ private static void SetRowBackground ( ListViewBase sender , Control itemContainer , int itemIndex )
167146 {
168147 var brush = itemIndex % 2 == 0 ? GetAlternateColor ( sender ) : null ;
169148 var rootBorder = itemContainer . FindDescendant < Border > ( ) ;
@@ -174,4 +153,19 @@ private static void SetItemContainerBackground(ListViewBase sender, Control item
174153 rootBorder . Background = brush ;
175154 }
176155 }
156+
157+ private static void OnListViewBaseUnloaded_AltRow ( object sender , RoutedEventArgs e )
158+ {
159+ if ( sender is not ListViewBase listViewBase )
160+ return ;
161+
162+ // Untrack the list view
163+ _trackedListViews . Remove ( listViewBase . Items ) ;
164+
165+ // Unsubscribe from events
166+ listViewBase . ContainerContentChanging -= ItemTemplateContainerContentChanging ;
167+ listViewBase . ContainerContentChanging -= ColorContainerContentChanging ;
168+ listViewBase . Items . VectorChanged -= ColorItemsVectorChanged ;
169+ listViewBase . Unloaded -= OnListViewBaseUnloaded_AltRow ;
170+ }
177171}
0 commit comments