@@ -18,6 +18,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Behaviors
1818 /// The focus will be set following the <see cref="Targets"/> order. The first control being ready
1919 /// and accepting the focus will receive it.
2020 /// The focus can be set to another control with a higher priority if it loads before <see cref="FocusEngagementTimeout"/>.
21+ /// The focus can be set to another control if some controls will be loaded/unloaded later.
2122 /// </summary>
2223 [ ContentProperty ( Name = nameof ( Targets ) ) ]
2324 public sealed class FocusBehavior : BehaviorBase < UIElement >
@@ -67,10 +68,27 @@ public TimeSpan FocusEngagementTimeout
6768 }
6869
6970 /// <inheritdoc/>
70- protected override void OnAssociatedObjectLoaded ( ) => ApplyFocus ( ) ;
71+ protected override void OnAssociatedObjectLoaded ( )
72+ {
73+ foreach ( var target in Targets )
74+ {
75+ target . ControlChanged += OnTargetControlChanged ;
76+ }
77+
78+ ApplyFocus ( ) ;
79+ }
7180
7281 /// <inheritdoc/>
73- protected override void OnDetaching ( ) => Stop ( ) ;
82+ protected override bool Uninitialize ( )
83+ {
84+ foreach ( var target in Targets )
85+ {
86+ target . ControlChanged -= OnTargetControlChanged ;
87+ }
88+
89+ Stop ( ) ;
90+ return true ;
91+ }
7492
7593 private static void OnTargetsPropertyChanged ( DependencyObject d , DependencyPropertyChangedEventArgs e )
7694 {
@@ -92,24 +110,39 @@ private void ApplyFocus()
92110 }
93111
94112 var focusedControlIndex = - 1 ;
113+ var hasListViewBaseControl = false ;
95114 for ( var i = 0 ; i < Targets . Count ; i ++ )
96115 {
97116 var control = Targets [ i ] . Control ;
117+ if ( control is null )
118+ {
119+ continue ;
120+ }
121+
98122 if ( control . IsLoaded )
99123 {
100124 if ( control . Focus ( FocusState . Programmatic ) )
101125 {
102126 focusedControlIndex = i ;
103127 break ;
104128 }
129+
130+ if ( control is ListViewBase listViewBase )
131+ {
132+ // The list may not have any item yet, we wait until the first item is rendered.
133+ listViewBase . ContainerContentChanging -= OnContainerContentChanging ;
134+ listViewBase . ContainerContentChanging += OnContainerContentChanging ;
135+ hasListViewBaseControl = true ;
136+ }
105137 }
106138 else
107139 {
140+ control . Loaded -= OnControlLoaded ;
108141 control . Loaded += OnControlLoaded ;
109142 }
110143 }
111144
112- if ( focusedControlIndex == 0 || Targets . All ( t => t . Control ? . IsLoaded == true ) )
145+ if ( focusedControlIndex == 0 || ( ! hasListViewBaseControl && Targets . All ( t => t . Control ? . IsLoaded == true ) ) )
113146 {
114147 // The first control has received the focus or all the control are loaded and none can take the focus: we stop.
115148 Stop ( ) ;
@@ -137,6 +170,14 @@ private void OnEngagementTimerTick(object sender, object e)
137170
138171 private void OnControlLoaded ( object sender , RoutedEventArgs e ) => ApplyFocus ( ) ;
139172
173+ private void OnTargetControlChanged ( object sender , EventArgs e ) => ApplyFocus ( ) ;
174+
175+ private void OnContainerContentChanging ( ListViewBase sender , ContainerContentChangingEventArgs args )
176+ {
177+ sender . ContainerContentChanging -= OnContainerContentChanging ;
178+ ApplyFocus ( ) ;
179+ }
180+
140181 private void Stop ( FocusTargetList targets = null )
141182 {
142183 if ( _timer != null )
@@ -153,6 +194,11 @@ private void Stop(FocusTargetList targets = null)
153194 }
154195
155196 target . Control . Loaded -= OnControlLoaded ;
197+
198+ if ( target . Control is ListViewBase listViewBase )
199+ {
200+ listViewBase . ContainerContentChanging -= OnContainerContentChanging ;
201+ }
156202 }
157203 }
158204 }
@@ -167,7 +213,7 @@ public sealed class FocusTargetList : List<FocusTarget>
167213 /// <summary>
168214 /// A target for the <see cref="FocusBehavior"/>.
169215 /// </summary>
170- public sealed partial class FocusTarget : DependencyObject
216+ public sealed class FocusTarget : DependencyObject
171217 {
172218 /// <summary>
173219 /// The DP to store the <see cref="Control"/> property value.
@@ -176,7 +222,13 @@ public sealed partial class FocusTarget : DependencyObject
176222 nameof ( Control ) ,
177223 typeof ( Control ) ,
178224 typeof ( FocusTarget ) ,
179- new PropertyMetadata ( null ) ) ;
225+ new PropertyMetadata ( null , OnControlChanged ) ) ;
226+
227+ /// <summary>
228+ /// Raised when <see cref="Control"/> property changed.
229+ /// It can change if we use x:Load on the control.
230+ /// </summary>
231+ public event EventHandler ControlChanged ;
180232
181233 /// <summary>
182234 /// Gets or sets the control that will receive the focus.
@@ -186,6 +238,12 @@ public Control Control
186238 get => ( Control ) GetValue ( ControlProperty ) ;
187239 set => SetValue ( ControlProperty , value ) ;
188240 }
241+
242+ private static void OnControlChanged ( DependencyObject d , DependencyPropertyChangedEventArgs e )
243+ {
244+ var target = ( FocusTarget ) d ;
245+ target . ControlChanged ? . Invoke ( target , EventArgs . Empty ) ;
246+ }
189247 }
190248#pragma warning restore SA1402 // File may only contain a single type
191249}
0 commit comments