@@ -26,14 +26,24 @@ public class TeamExplorerServiceHolder : ITeamExplorerServiceHolder
26
26
bool activeRepoNotified = false ;
27
27
28
28
IServiceProvider serviceProvider ;
29
- IGitExt gitService ;
30
- UIContext gitUIContext ;
29
+ IVSGitExt gitService ;
30
+ IVSUIContext gitUIContext ;
31
+ IVSUIContextFactory uiContextFactory ;
31
32
32
33
// ActiveRepositories PropertyChanged event comes in on a non-main thread
33
34
readonly SynchronizationContext syncContext ;
34
35
35
- public TeamExplorerServiceHolder ( )
36
+ /// <summary>
37
+ /// This class relies on IVSUIContextFactory to get the UIContext object that provides information
38
+ /// when VS switches repositories. Unfortunately, for some reason MEF fails to create the instance
39
+ /// when imported from the constructor, so it's imported manually when first accessed via the
40
+ /// ServiceProvider instance (when mocking, make sure that the ServiceProvider includes this factory)
41
+ /// </summary>
42
+ /// <param name="gitService"></param>
43
+ [ ImportingConstructor ]
44
+ public TeamExplorerServiceHolder ( IVSGitExt gitService )
36
45
{
46
+ this . GitService = gitService ;
37
47
syncContext = SynchronizationContext . Current ;
38
48
}
39
49
@@ -49,7 +59,7 @@ public IServiceProvider ServiceProvider
49
59
serviceProvider = value ;
50
60
if ( serviceProvider == null )
51
61
return ;
52
- GitUIContext = GitUIContext ?? UIContext . FromUIContextGuid ( new Guid ( Guids . GitSccProviderId ) ) ;
62
+ GitUIContext = GitUIContext ?? UIContextFactory . GetUIContext ( new Guid ( Guids . GitSccProviderId ) ) ;
53
63
UIContextChanged ( GitUIContext ? . IsActive ?? false , false ) ;
54
64
}
55
65
}
@@ -77,7 +87,7 @@ public void Subscribe(object who, Action<ILocalRepositoryModel> handler)
77
87
78
88
bool notificationsExist ;
79
89
ILocalRepositoryModel repo ;
80
- lock ( activeRepoHandlers )
90
+ lock ( activeRepoHandlers )
81
91
{
82
92
repo = ActiveRepo ;
83
93
notificationsExist = activeRepoNotified ;
@@ -125,7 +135,7 @@ public void ClearServiceProvider(IServiceProvider provider)
125
135
126
136
public void Refresh ( )
127
137
{
128
- GitUIContext = GitUIContext ?? UIContext . FromUIContextGuid ( new Guid ( Guids . GitSccProviderId ) ) ;
138
+ GitUIContext = GitUIContext ?? UIContextFactory . GetUIContext ( new Guid ( Guids . GitSccProviderId ) ) ;
129
139
UIContextChanged ( GitUIContext ? . IsActive ?? false , true ) ;
130
140
}
131
141
@@ -139,14 +149,19 @@ void NotifyActiveRepo()
139
149
}
140
150
}
141
151
142
- void UIContextChanged ( object sender , UIContextChangedEventArgs e )
152
+ void UIContextChanged ( object sender , IVSUIContextChangedEventArgs e )
143
153
{
144
154
Guard . ArgumentNotNull ( e , nameof ( e ) ) ;
145
155
146
156
ActiveRepo = null ;
147
157
UIContextChanged ( e . Activated , false ) ;
148
158
}
149
159
160
+ /// <summary>
161
+ /// This is called on a background thread. Do not do synchronous GetService calls here.
162
+ /// </summary>
163
+ /// <param name="active"></param>
164
+ /// <param name="refresh"></param>
150
165
async void UIContextChanged ( bool active , bool refresh )
151
166
{
152
167
Debug . Assert ( ServiceProvider != null , "UIContextChanged called before service provider is set" ) ;
@@ -155,42 +170,34 @@ async void UIContextChanged(bool active, bool refresh)
155
170
156
171
if ( active )
157
172
{
158
- GitService = GitService ?? ServiceProvider . GetServiceSafe < IGitExt > ( ) ;
159
173
if ( ActiveRepo == null || refresh )
174
+ {
160
175
ActiveRepo = await System . Threading . Tasks . Task . Run ( ( ) =>
161
176
{
162
- var repos = GitService ? . ActiveRepositories ;
177
+ var repos = GitService . ActiveRepositories ;
163
178
// Looks like this might return null after a while, for some unknown reason
164
179
// if it does, let's refresh the GitService instance in case something got wonky
165
180
// and try again. See issue #23
166
181
if ( repos == null )
167
182
{
168
183
log . Error ( "Error 2001: ActiveRepositories is null. GitService: '{GitService}'" , GitService ) ;
169
- GitService = ServiceProvider ? . GetServiceSafe < IGitExt > ( ) ;
170
- repos = GitService ? . ActiveRepositories ;
184
+ GitService . Refresh ( ServiceProvider ) ;
185
+ repos = GitService . ActiveRepositories ;
171
186
if ( repos == null )
172
187
log . Error ( "Error 2002: ActiveRepositories is null. GitService: '{GitService}'" , GitService ) ;
173
188
}
174
- return repos ? . FirstOrDefault ( ) ? . ToModel ( ) ;
189
+ return repos ? . FirstOrDefault ( ) ;
175
190
} ) ;
191
+ }
176
192
}
177
193
else
178
194
ActiveRepo = null ;
179
195
}
180
196
181
- void CheckAndUpdate ( object sender , System . ComponentModel . PropertyChangedEventArgs e )
197
+ void UpdateActiveRepo ( )
182
198
{
183
- Guard . ArgumentNotNull ( e , nameof ( e ) ) ;
184
-
185
- if ( e . PropertyName != "ActiveRepositories" )
186
- return ;
187
-
188
- var service = GitService ;
189
- if ( service == null )
190
- return ;
191
-
192
- var repo = service . ActiveRepositories . FirstOrDefault ( ) ? . ToModel ( ) ;
193
- if ( repo != ActiveRepo )
199
+ var repo = gitService . ActiveRepositories . FirstOrDefault ( ) ;
200
+ if ( ! Equals ( repo , ActiveRepo ) )
194
201
// so annoying that this is on the wrong thread
195
202
syncContext . Post ( r => ActiveRepo = r as ILocalRepositoryModel , repo ) ;
196
203
}
@@ -221,7 +228,7 @@ ITeamExplorerPage PageService
221
228
get { return ServiceProvider . GetServiceSafe < ITeamExplorerPage > ( ) ; }
222
229
}
223
230
224
- UIContext GitUIContext
231
+ IVSUIContext GitUIContext
225
232
{
226
233
get { return gitUIContext ; }
227
234
set
@@ -236,18 +243,86 @@ UIContext GitUIContext
236
243
}
237
244
}
238
245
239
- IGitExt GitService
246
+ IVSGitExt GitService
240
247
{
241
248
get { return gitService ; }
242
249
set
243
250
{
244
251
if ( gitService == value )
245
252
return ;
246
253
if ( gitService != null )
247
- gitService . PropertyChanged -= CheckAndUpdate ;
254
+ gitService . ActiveRepositoriesChanged -= UpdateActiveRepo ;
248
255
gitService = value ;
249
256
if ( gitService != null )
250
- gitService . PropertyChanged += CheckAndUpdate ;
257
+ gitService . ActiveRepositoriesChanged += UpdateActiveRepo ;
258
+ }
259
+ }
260
+
261
+ IVSUIContextFactory UIContextFactory
262
+ {
263
+ get
264
+ {
265
+ if ( uiContextFactory == null )
266
+ {
267
+ uiContextFactory = ServiceProvider . GetServiceSafe < IVSUIContextFactory > ( ) ;
268
+ }
269
+ return uiContextFactory ;
270
+ }
271
+ }
272
+ }
273
+
274
+ [ Export ( typeof ( IVSUIContextFactory ) ) ]
275
+ [ PartCreationPolicy ( CreationPolicy . Shared ) ]
276
+ class VSUIContextFactory : IVSUIContextFactory
277
+ {
278
+ public IVSUIContext GetUIContext ( Guid contextGuid )
279
+ {
280
+ return new VSUIContext ( UIContext . FromUIContextGuid ( contextGuid ) ) ;
281
+ }
282
+ }
283
+
284
+ class VSUIContextChangedEventArgs : IVSUIContextChangedEventArgs
285
+ {
286
+ public bool Activated { get ; }
287
+
288
+ public VSUIContextChangedEventArgs ( bool activated )
289
+ {
290
+ Activated = activated ;
291
+ }
292
+ }
293
+
294
+ class VSUIContext : IVSUIContext
295
+ {
296
+ readonly UIContext context ;
297
+ readonly Dictionary < EventHandler < IVSUIContextChangedEventArgs > , EventHandler < UIContextChangedEventArgs > > handlers =
298
+ new Dictionary < EventHandler < IVSUIContextChangedEventArgs > , EventHandler < UIContextChangedEventArgs > > ( ) ;
299
+ public VSUIContext ( UIContext context )
300
+ {
301
+ this . context = context ;
302
+ }
303
+
304
+ public bool IsActive { get { return context . IsActive ; } }
305
+
306
+ public event EventHandler < IVSUIContextChangedEventArgs > UIContextChanged
307
+ {
308
+ add
309
+ {
310
+ EventHandler < UIContextChangedEventArgs > handler = null ;
311
+ if ( ! handlers . TryGetValue ( value , out handler ) )
312
+ {
313
+ handler = ( s , e ) => value . Invoke ( s , new VSUIContextChangedEventArgs ( e . Activated ) ) ;
314
+ handlers . Add ( value , handler ) ;
315
+ }
316
+ context . UIContextChanged += handler ;
317
+ }
318
+ remove
319
+ {
320
+ EventHandler < UIContextChangedEventArgs > handler = null ;
321
+ if ( handlers . TryGetValue ( value , out handler ) )
322
+ {
323
+ handlers . Remove ( value ) ;
324
+ context . UIContextChanged -= handler ;
325
+ }
251
326
}
252
327
}
253
328
}
0 commit comments