@@ -18,6 +18,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Behaviors
18
18
/// The focus will be set following the <see cref="Targets"/> order. The first control being ready
19
19
/// and accepting the focus will receive it.
20
20
/// 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.
21
22
/// </summary>
22
23
[ ContentProperty ( Name = nameof ( Targets ) ) ]
23
24
public sealed class FocusBehavior : BehaviorBase < UIElement >
@@ -67,10 +68,27 @@ public TimeSpan FocusEngagementTimeout
67
68
}
68
69
69
70
/// <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
+ }
71
80
72
81
/// <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
+ }
74
92
75
93
private static void OnTargetsPropertyChanged ( DependencyObject d , DependencyPropertyChangedEventArgs e )
76
94
{
@@ -92,24 +110,39 @@ private void ApplyFocus()
92
110
}
93
111
94
112
var focusedControlIndex = - 1 ;
113
+ var hasListViewBaseControl = false ;
95
114
for ( var i = 0 ; i < Targets . Count ; i ++ )
96
115
{
97
116
var control = Targets [ i ] . Control ;
117
+ if ( control is null )
118
+ {
119
+ continue ;
120
+ }
121
+
98
122
if ( control . IsLoaded )
99
123
{
100
124
if ( control . Focus ( FocusState . Programmatic ) )
101
125
{
102
126
focusedControlIndex = i ;
103
127
break ;
104
128
}
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
+ }
105
137
}
106
138
else
107
139
{
140
+ control . Loaded -= OnControlLoaded ;
108
141
control . Loaded += OnControlLoaded ;
109
142
}
110
143
}
111
144
112
- if ( focusedControlIndex == 0 || Targets . All ( t => t . Control ? . IsLoaded == true ) )
145
+ if ( focusedControlIndex == 0 || ( ! hasListViewBaseControl && Targets . All ( t => t . Control ? . IsLoaded == true ) ) )
113
146
{
114
147
// The first control has received the focus or all the control are loaded and none can take the focus: we stop.
115
148
Stop ( ) ;
@@ -137,6 +170,14 @@ private void OnEngagementTimerTick(object sender, object e)
137
170
138
171
private void OnControlLoaded ( object sender , RoutedEventArgs e ) => ApplyFocus ( ) ;
139
172
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
+
140
181
private void Stop ( FocusTargetList targets = null )
141
182
{
142
183
if ( _timer != null )
@@ -153,6 +194,11 @@ private void Stop(FocusTargetList targets = null)
153
194
}
154
195
155
196
target . Control . Loaded -= OnControlLoaded ;
197
+
198
+ if ( target . Control is ListViewBase listViewBase )
199
+ {
200
+ listViewBase . ContainerContentChanging -= OnContainerContentChanging ;
201
+ }
156
202
}
157
203
}
158
204
}
@@ -167,7 +213,7 @@ public sealed class FocusTargetList : List<FocusTarget>
167
213
/// <summary>
168
214
/// A target for the <see cref="FocusBehavior"/>.
169
215
/// </summary>
170
- public sealed partial class FocusTarget : DependencyObject
216
+ public sealed class FocusTarget : DependencyObject
171
217
{
172
218
/// <summary>
173
219
/// The DP to store the <see cref="Control"/> property value.
@@ -176,7 +222,13 @@ public sealed partial class FocusTarget : DependencyObject
176
222
nameof ( Control ) ,
177
223
typeof ( Control ) ,
178
224
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 ;
180
232
181
233
/// <summary>
182
234
/// Gets or sets the control that will receive the focus.
@@ -186,6 +238,12 @@ public Control Control
186
238
get => ( Control ) GetValue ( ControlProperty ) ;
187
239
set => SetValue ( ControlProperty , value ) ;
188
240
}
241
+
242
+ private static void OnControlChanged ( DependencyObject d , DependencyPropertyChangedEventArgs e )
243
+ {
244
+ var target = ( FocusTarget ) d ;
245
+ target . ControlChanged ? . Invoke ( target , EventArgs . Empty ) ;
246
+ }
189
247
}
190
248
#pragma warning restore SA1402 // File may only contain a single type
191
249
}
0 commit comments