Skip to content
This repository was archived by the owner on Jun 21, 2023. It is now read-only.

Commit 84df182

Browse files
committed
Abstract out more things so TeamExplorerServiceHolder doesn't rely on any specific TE types
1 parent 9998cdb commit 84df182

File tree

2 files changed

+91
-41
lines changed

2 files changed

+91
-41
lines changed
Lines changed: 3 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
using Microsoft.VisualStudio.Shell;
22
using System;
3-
using System.ComponentModel.Composition;
43

54
namespace GitHub.Services
65
{
@@ -9,43 +8,14 @@ public interface IVSUIContextFactory
98
IVSUIContext GetUIContext(Guid contextGuid);
109
}
1110

12-
[Export(typeof(IVSUIContextFactory))]
13-
[PartCreationPolicy(CreationPolicy.Shared)]
14-
public class VSUIContextFactory : IVSUIContextFactory
11+
public interface IVSUIContextChangedEventArgs
1512
{
16-
public IVSUIContext GetUIContext(Guid contextGuid)
17-
{
18-
return new VSUIContext(UIContext.FromUIContextGuid(contextGuid));
19-
}
13+
bool Activated { get; }
2014
}
2115

2216
public interface IVSUIContext
2317
{
2418
bool IsActive { get; }
25-
event EventHandler<UIContextChangedEventArgs> UIContextChanged;
26-
}
27-
28-
public class VSUIContext : IVSUIContext
29-
{
30-
readonly UIContext context;
31-
32-
public VSUIContext(UIContext context)
33-
{
34-
this.context = context;
35-
}
36-
37-
public bool IsActive { get { return context.IsActive; } }
38-
39-
public event EventHandler<UIContextChangedEventArgs> UIContextChanged
40-
{
41-
add
42-
{
43-
context.UIContextChanged += value;
44-
}
45-
remove
46-
{
47-
context.UIContextChanged -= value;
48-
}
49-
}
19+
event EventHandler<IVSUIContextChangedEventArgs> UIContextChanged;
5020
}
5121
}

src/GitHub.TeamFoundation.14/Base/TeamExplorerServiceHolder.cs

Lines changed: 88 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,21 +21,28 @@ namespace GitHub.VisualStudio.Base
2121
public class TeamExplorerServiceHolder : ITeamExplorerServiceHolder
2222
{
2323
static readonly ILogger log = LogManager.ForContext<TeamExplorerServiceHolder>();
24-
readonly IVSUIContextFactory uiContextFactory;
2524
readonly Dictionary<object, Action<ILocalRepositoryModel>> activeRepoHandlers = new Dictionary<object, Action<ILocalRepositoryModel>>();
2625
ILocalRepositoryModel activeRepo;
2726
bool activeRepoNotified = false;
2827

2928
IServiceProvider serviceProvider;
3029
IVSGitExt gitService;
3130
IVSUIContext gitUIContext;
31+
IVSUIContextFactory uiContextFactory;
3232

3333
// ActiveRepositories PropertyChanged event comes in on a non-main thread
3434
readonly SynchronizationContext syncContext;
3535

36-
public TeamExplorerServiceHolder(IVSUIContextFactory uiContextFactory, IVSGitExt gitService)
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)
3745
{
38-
this.uiContextFactory = uiContextFactory;
3946
this.GitService = gitService;
4047
syncContext = SynchronizationContext.Current;
4148
}
@@ -52,7 +59,7 @@ public IServiceProvider ServiceProvider
5259
serviceProvider = value;
5360
if (serviceProvider == null)
5461
return;
55-
GitUIContext = GitUIContext ?? uiContextFactory.GetUIContext(new Guid(Guids.GitSccProviderId));
62+
GitUIContext = GitUIContext ?? UIContextFactory.GetUIContext(new Guid(Guids.GitSccProviderId));
5663
UIContextChanged(GitUIContext?.IsActive ?? false, false);
5764
}
5865
}
@@ -80,7 +87,7 @@ public void Subscribe(object who, Action<ILocalRepositoryModel> handler)
8087

8188
bool notificationsExist;
8289
ILocalRepositoryModel repo;
83-
lock(activeRepoHandlers)
90+
lock (activeRepoHandlers)
8491
{
8592
repo = ActiveRepo;
8693
notificationsExist = activeRepoNotified;
@@ -128,7 +135,7 @@ public void ClearServiceProvider(IServiceProvider provider)
128135

129136
public void Refresh()
130137
{
131-
GitUIContext = GitUIContext ?? uiContextFactory.GetUIContext(new Guid(Guids.GitSccProviderId));
138+
GitUIContext = GitUIContext ?? UIContextFactory.GetUIContext(new Guid(Guids.GitSccProviderId));
132139
UIContextChanged(GitUIContext?.IsActive ?? false, true);
133140
}
134141

@@ -142,14 +149,19 @@ void NotifyActiveRepo()
142149
}
143150
}
144151

145-
void UIContextChanged(object sender, UIContextChangedEventArgs e)
152+
void UIContextChanged(object sender, IVSUIContextChangedEventArgs e)
146153
{
147154
Guard.ArgumentNotNull(e, nameof(e));
148155

149156
ActiveRepo = null;
150157
UIContextChanged(e.Activated, false);
151158
}
152159

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>
153165
async void UIContextChanged(bool active, bool refresh)
154166
{
155167
Debug.Assert(ServiceProvider != null, "UIContextChanged called before service provider is set");
@@ -170,7 +182,7 @@ async void UIContextChanged(bool active, bool refresh)
170182
{
171183
log.Error("Error 2001: ActiveRepositories is null. GitService: '{GitService}'", GitService);
172184
GitService.Refresh(ServiceProvider);
173-
repos = GitService?.ActiveRepositories;
185+
repos = GitService.ActiveRepositories;
174186
if (repos == null)
175187
log.Error("Error 2002: ActiveRepositories is null. GitService: '{GitService}'", GitService);
176188
}
@@ -245,6 +257,74 @@ IVSGitExt GitService
245257
gitService.ActiveRepositoriesChanged += UpdateActiveRepo;
246258
}
247259
}
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+
}
326+
}
327+
}
248328
}
249329

250330
static class IGitRepositoryInfoExtensions

0 commit comments

Comments
 (0)