forked from dotnet/project-system
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathReferencePropPage.vb
2311 lines (1984 loc) · 114 KB
/
ReferencePropPage.vb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
' Licensed to the .NET Foundation under one or more agreements. The .NET Foundation licenses this file to you under the MIT license. See the LICENSE.md file in the project root for more information.
Imports System.ComponentModel
Imports System.ComponentModel.Design
Imports System.Drawing
Imports System.Globalization
Imports System.Runtime.InteropServices
Imports System.Text.RegularExpressions
Imports System.Threading
Imports System.Windows.Forms
Imports Microsoft.CodeAnalysis
Imports Microsoft.CodeAnalysis.VisualBasic
Imports Microsoft.VisualStudio.ComponentModelHost
Imports Microsoft.VisualStudio.Editors.Common
Imports Microsoft.VisualStudio.Editors.Interop
Imports Microsoft.VisualStudio.LanguageServices
Imports Microsoft.VisualStudio.Utilities
Imports Microsoft.VisualStudio.Shell.Interop
Imports Microsoft.VisualStudio.WCFReference.Interop
Namespace Microsoft.VisualStudio.Editors.PropertyPages
Partial Friend Class ReferencePropPage
Inherits PropPageUserControlBase
Implements VSLangProj._dispReferencesEvents
Implements VSLangProj._dispImportsEvents
Implements ISelectionContainer
Implements IVsWCFReferenceEvents
Private Const REFCOLUMN_NAME As Integer = 0
Private Const REFCOLUMN_TYPE As Integer = 1
Private Const REFCOLUMN_VERSION As Integer = 2
Private Const REFCOLUMN_COPYLOCAL As Integer = 3
Private Const REFCOLUMN_PATH As Integer = 4
Private Const REFCOLUMN_MAX As Integer = 4
Friend WithEvents AddUserImportButton As Button
Friend WithEvents UpdateUserImportButton As Button
Friend WithEvents UserImportTextBox As TextBox
Private _referencesEventsCookie As NativeMethods.ConnectionPointCookie
Private _importsEventsCookie As NativeMethods.ConnectionPointCookie
Private _updatingReferences As Boolean
Private _updatingImportList As Boolean
Private _designerHost As IDesignerHost
Private _trackSelection As ITrackSelection
Private _holdSelectionChange As Integer
Private _delayUpdatingItems As Queue
Private _columnWidthUpdated As Boolean
Private _ignoreImportEvent As Boolean
Friend WithEvents addRemoveButtonsTableLayoutPanel As TableLayoutPanel
Friend WithEvents referenceButtonsTableLayoutPanel As TableLayoutPanel
Friend WithEvents ReferencePageSplitContainer As SplitContainer
Friend WithEvents addUserImportTableLayoutPanel As TableLayoutPanel
Private _needRefreshImportList As Boolean
Private _importListSelectedItem As String
Private _hidingImportListSelectedItem As Boolean
' helper object to sort the reference list
Private ReadOnly _referenceSorter As ListViewComparer
Private _referenceGroupManager As IVsWCFReferenceManager
Public Sub New()
MyBase.New()
'This call is required by the Windows Form Designer.
InitializeComponent()
' Scale buttons
addSplitButton.Size = DpiAwareness.LogicalToDeviceSize(Handle, addSplitButton.Size)
RemoveReference.Size = DpiAwareness.LogicalToDeviceSize(Handle, RemoveReference.Size)
UpdateReferences.Size = DpiAwareness.LogicalToDeviceSize(Handle, UpdateReferences.Size)
'Add any initialization after the InitializeComponent() call
AddChangeHandlers()
PageRequiresScaling = False
'support sorting
_referenceSorter = New ListViewComparer()
ReferenceList.ListViewItemSorter = _referenceSorter
_referenceSorter.Sorting = SortOrder.Ascending
ReferenceList.Sorting = SortOrder.Ascending
End Sub
''' <summary>
''' Removes references to anything that was passed in to SetObjects
''' </summary>
Protected Overrides Sub CleanupCOMReferences()
UnadviseReferencesEvents()
UnadviseWebReferencesEvents()
UnadviseServiceReferencesEvents()
UnadviseImportsEvents()
MyBase.CleanupCOMReferences()
End Sub
Protected Overrides Sub EnableAllControls(enabled As Boolean)
MyBase.EnableAllControls(enabled)
ReferenceList.Enabled = enabled
addSplitButton.Enabled = enabled
RemoveReference.Enabled = enabled
UpdateReferences.Enabled = enabled
UnusedReferences.Enabled = enabled
GetPropertyControlData("ImportList").EnableControls(enabled)
End Sub
Protected Overrides ReadOnly Property ControlData As PropertyControlData()
Get
If m_ControlData Is Nothing Then
m_ControlData = New PropertyControlData() {
New PropertyControlData(1, "ImportList", ImportList, AddressOf ImportListSet, AddressOf ImportListGet, ControlDataFlags.UserPersisted)
}
End If
Return m_ControlData
End Get
End Property
''' <summary>
''' The designer host of this page
''' NOTE: we currently get the designer host from the propertyPageDesignerView, it is a workaround. The right solution should be the parent page pass in the right serviceProvider when it creates/initializes this page
''' </summary>
Private ReadOnly Property DesignerHost As IDesignerHost
Get
If _designerHost Is Nothing Then
Dim designerView As PropPageDesigner.PropPageDesignerView = FindPropPageDesignerView()
Debug.Assert(designerView IsNot Nothing, "why we can not find the designerView")
If designerView IsNot Nothing Then
_designerHost = designerView.DesignerHost
Debug.Assert(_designerHost IsNot Nothing, "why we can not find DesignerHost")
End If
End If
Return _designerHost
End Get
End Property
''' <summary>
''' Property to return the selected-item of the ImportList which is smart about whether or
''' not we are hiding the selection currently to work around the by-design CheckedListBox
''' behavior of visually looking like it has focus when it really doesn't.
''' </summary>
Private ReadOnly Property ImportListSelectedItem As String
Get
Debug.Assert(ImportList.SelectedItems.Count <= 1, "the ImportList is not set up to support multiple selection")
If ImportList.SelectedItems.Count = 1 Then
Return DirectCast(ImportList.SelectedItem, String)
ElseIf _importListSelectedItem IsNot Nothing Then
Return _importListSelectedItem
End If
Return String.Empty
End Get
End Property
''' <summary>
''' ITrackSelection -- we are using this service to push objects to the propertyPage.
''' We should get this service from DesignerHost, but not other service provider. Each designer has its own ITrackSelection
''' </summary>
Private ReadOnly Property TrackSelection As ITrackSelection
Get
If _trackSelection Is Nothing Then
Dim host As IDesignerHost = DesignerHost
If host IsNot Nothing Then
_trackSelection = CType(host.GetService(GetType(ITrackSelection)), ITrackSelection)
Debug.Assert(_trackSelection IsNot Nothing, "Why we can not find ITrackSelection Service")
End If
End If
Return _trackSelection
End Get
End Property
Public Overrides Function ReadUserDefinedProperty(PropertyName As String, ByRef Value As Object) As Boolean
If PropertyName = "ImportList" Then
Value = GetCurrentImports()
Return True
End If
Return False
End Function
Public Overrides Function WriteUserDefinedProperty(PropertyName As String, Value As Object) As Boolean
If PropertyName = "ImportList" Then
Debug.Assert(TypeOf Value Is String(), "Invalid value type")
SaveImportedNamespaces(DirectCast(Value, String()))
Return True
End If
Return False
End Function
Public Overrides Function GetUserDefinedPropertyDescriptor(PropertyName As String) As PropertyDescriptor
If PropertyName = "ImportList" Then
Return New UserPropertyDescriptor(PropertyName, GetType(String()))
End If
Debug.Fail("Unexpected user-defined property descriptor name")
Return Nothing
End Function
''' <summary>
''' Called when the control layout code wants to know the Preferred size of this page
''' </summary>
''' <param name="proposedSize"></param>
''' <remarks>We need implement this, because split panel doesn't support AutoSize well</remarks>
Public Overrides Function GetPreferredSize(proposedSize As Size) As Size
Dim preferredSize As Size = MyBase.GetPreferredSize(proposedSize)
Dim referenceAreaPreferredSize As Size = Size.Empty
Dim importsAreaPreferredSize As Size = Size.Empty
If ReferencePageTableLayoutPanel IsNot Nothing Then
referenceAreaPreferredSize = ReferencePageTableLayoutPanel.GetPreferredSize(New Size(proposedSize.Width, ReferencePageTableLayoutPanel.Height))
End If
If addUserImportTableLayoutPanel IsNot Nothing Then
importsAreaPreferredSize = addUserImportTableLayoutPanel.GetPreferredSize(New Size(proposedSize.Width, importsAreaPreferredSize.Height))
End If
' NOTE: 6 is 2 times of the margin we used. The exactly number is not important, because it actually does not make any difference on the page.
Return New Size(Math.Max(preferredSize.Width, Math.Max(referenceAreaPreferredSize.Width, importsAreaPreferredSize.Width) + 6),
Math.Max(preferredSize.Height, referenceAreaPreferredSize.Height + importsAreaPreferredSize.Height + 6))
End Function
Protected Overrides Sub WndProc(ByRef m As Message)
Try
Dim processedDelayRefreshMessage As Boolean = False
Select Case m.Msg
Case WmUserConstants.WM_REFPAGE_REFERENCES_REFRESH
ProcessDelayUpdateItems()
processedDelayRefreshMessage = True
Case WmUserConstants.WM_REFPAGE_IMPORTCHANGED
SetDirty(ImportList)
processedDelayRefreshMessage = True
Case WmUserConstants.WM_REFPAGE_IMPORTS_REFRESH
Try
PopulateImportsList(True)
Finally
_needRefreshImportList = False
End Try
processedDelayRefreshMessage = True
Case WmUserConstants.WM_REFPAGE_SERVICEREFERENCES_REFRESH
RefreshServiceReferences()
processedDelayRefreshMessage = True
End Select
If processedDelayRefreshMessage Then
Internal.Performance.CodeMarkers.Instance.CodeMarker(Internal.Performance.RoslynCodeMarkerEvent.PerfMSVSEditorsReferencePagePostponedUIRefreshDone)
End If
Catch ex As COMException
' The message pump in the background compiler could process our pending message, and when the compiler is running, we would get E_PENDING failure
' we want to post the message back to try it again. To prevent spinning the main thread, we ask a background thread to post the message back after a short period of time
If ex.ErrorCode = NativeMethods.E_PENDING Then
Dim delayMessage As New System.Threading.Timer(AddressOf DelayPostingMessage, m.Msg, 200, Timeout.Infinite)
Return
End If
Throw
End Try
MyBase.WndProc(m)
End Sub
''' <summary>
''' We cannot process the UI refreshing message when compiler is running. However the compiler continually pumps messages.
''' It is a workaround to use background thread to wait for the compiler to finish.
''' Note: it rarely happens. (It happens we have a post message when a third party start the compiler and wait for something.)
''' </summary>
Private Sub DelayPostingMessage(messageId As Object)
If Not IsDisposed Then
NativeMethods.PostMessage(Handle, CInt(messageId), 0, 0)
End If
End Sub
''' <summary>
''' Called when the page is activated or deactivated
''' </summary>
''' <param name="activated"></param>
Protected Overrides Sub OnPageActivated(activated As Boolean)
MyBase.OnPageActivated(activated)
If IsActivated Then
PostRefreshImportListMessage()
End If
End Sub
''' <summary>
''' ;ReferenceToListViewItem
''' Creates a four column listview item from the information of a project reference.
''' These columns are: Reference Name, Type (COM/.NET/UNKNOWN), Version, Copy Local (Yes/No), Path
''' </summary>
''' <param name="ref">Reference to take extract information from</param>
''' <param name="refObject">Internal Reference Object, which we push to the property grid</param>
''' <returns>ListViewItem object containing information from reference</returns>
''' <remarks>Helper for RefreshReferenceList() and UnusedReferencePropPage</remarks>
Friend Shared Function ReferenceToListViewItem(ref As VSLangProj.Reference, refObject As Object) As ListViewItem
Debug.Assert(ref IsNot Nothing)
Dim lvi As ListViewItem
Dim CopyLocalText As String
If ref.Type = VSLangProj.prjReferenceType.prjReferenceTypeActiveX AndAlso ref.Description <> "" Then
'For COM references with a nice description, use this
'(like "Microsoft Office 10.0 Object Library" instead of "Office")
lvi = New ListViewItem(ref.Description)
Else
lvi = New ListViewItem(ref.Name)
End If
lvi.Tag = refObject
lvi.Checked = ref.CopyLocal
CopyLocalText = ref.CopyLocal.ToString(CultureInfo.CurrentUICulture)
If ref.Type = VSLangProj.prjReferenceType.prjReferenceTypeActiveX Then
lvi.SubItems.Add("COM")
ElseIf ref.Type = VSLangProj.prjReferenceType.prjReferenceTypeAssembly Then
lvi.SubItems.Add(".NET")
Else
lvi.SubItems.Add("UNKNOWN") 'Type
End If
lvi.SubItems.Add(ref.Version.ToString()) 'Version
lvi.SubItems.Add(CopyLocalText) 'CopyLocal column
' We should put an error message there if we can not resolve the reference...
Dim path As String = ref.Path
If String.IsNullOrEmpty(path) Then
path = My.Resources.Microsoft_VisualStudio_Editors_Designer.PropPage_ReferenceNotFound
End If
lvi.SubItems.Add(path)
Return lvi
End Function
''' <summary>
''' WebReferenceToListViewItem
''' Creates a four column listview item from the information of a web reference.
''' These columns are: Reference Name, Type (COM/.NET/UNKNOWN), Version, Copy Local (Yes/No), Path
''' </summary>
''' <param name="webref">WebReference project item</param>
''' <param name="refObject">Internal Reference Object</param>
''' <returns>ListViewItem object containing information from reference</returns>
''' <remarks>Helper for RefreshReferenceList()</remarks>
Private Shared Function WebReferenceToListViewItem(webRef As EnvDTE.ProjectItem, refObject As Object) As ListViewItem
Debug.Assert(webRef IsNot Nothing)
Dim lvi As ListViewItem
lvi = New ListViewItem(webRef.Name) With {
.Tag = refObject
}
lvi.SubItems.Add("WEB") 'Type
lvi.SubItems.Add("") ' Version
lvi.SubItems.Add("") ' Copy Local
lvi.SubItems.Add(CStr(webRef.Properties.Item("WebReference").Value)) 'Path
Return lvi
End Function
''' <summary>
''' ServiceReferenceToListViewItem
''' Creates a four column listview item from the information of a web reference.
''' These columns are: Reference Name, Type (COM/.NET/UNKNOWN), Version, Copy Local (Yes/No), Path
''' </summary>
''' <param name="serviceReference">service reference component</param>
''' <returns>ListViewItem object containing information from reference</returns>
''' <remarks>Helper for RefreshReferenceList()</remarks>
Private Shared Function ServiceReferenceToListViewItem(serviceReference As ServiceReferenceComponent) As ListViewItem
Debug.Assert(serviceReference IsNot Nothing)
Dim lvi As ListViewItem
lvi = New ListViewItem(serviceReference.[Namespace]) With {
.Tag = serviceReference
}
lvi.SubItems.Add("SERVICE") 'Type
lvi.SubItems.Add("") ' Version
lvi.SubItems.Add("") ' Copy Local
Dim referencePath As String
Try
referencePath = serviceReference.ServiceReferenceURL
Catch ex As Exception
' show the error message, if the reference is broken
referencePath = ex.Message
End Try
lvi.SubItems.Add(referencePath) 'Path
Return lvi
End Function
''' <summary>
''' Refreshes the reference listviews (both regular and web references), based on the list of references ReferenceListData.
''' </summary>
''' <param name="ReferenceListData">reference object lists</param>
Private Sub RefreshReferenceList(ReferenceListData As ArrayList)
ReferenceList.BeginUpdate()
Try
ReferenceList.View = View.Details
ReferenceList.Items.Clear()
'For Each ref As VSLangProj.Reference In theVSProject.References
For refIndex As Integer = 0 To ReferenceListData.Count - 1
Dim refObject As Object = ReferenceListData(refIndex)
If TypeOf refObject Is ReferenceComponent Then
Debug.Assert(Not IsImplicitlyAddedReference(CType(refObject, ReferenceComponent).CodeReference), "Implicitly added references should have been filtered out and never displayed in our list")
ReferenceList.Items.Add(ReferenceToListViewItem(CType(refObject, ReferenceComponent).CodeReference, refObject))
ElseIf TypeOf refObject Is WebReferenceComponent Then
ReferenceList.Items.Add(WebReferenceToListViewItem(CType(refObject, WebReferenceComponent).WebReference, refObject))
ElseIf TypeOf refObject Is ServiceReferenceComponent Then
ReferenceList.Items.Add(ServiceReferenceToListViewItem(CType(refObject, ServiceReferenceComponent)))
End If
Next
ReferenceList.Sort()
Finally
ReferenceList.EndUpdate()
End Try
If Not _columnWidthUpdated Then
SetReferenceListColumnWidths(Me, ReferenceList, 0)
_columnWidthUpdated = True
End If
ReferenceList.Refresh()
EnableReferenceGroup()
End Sub
''' <summary>
''' Populates the Reference object of all references (regular and web) currently in the project, and also
''' calls RefreshReferenceList() to update the listviews with those objects
''' </summary>
Private Sub PopulateReferenceList()
Dim theVSProject As VSLangProj.VSProject
Dim ReferenceCount As Integer
Dim ref As VSLangProj.Reference
theVSProject = CType(DTEProject.Object, VSLangProj.VSProject)
ReferenceCount = theVSProject.References.Count
HoldSelectionChange(True)
Try
Dim ReferenceListData As New ArrayList(ReferenceCount)
For refIndex As Integer = 0 To ReferenceCount - 1
ref = theVSProject.References.Item(refIndex + 1) '1-based
'Don't worry about implicitly-added references (these can't be removed, and don't
' show up in the solution explorer, so we don't want to show them in the property
' pages, either - for VB, this is currently mscorlib and ms.vb.dll)
If Not IsImplicitlyAddedReference(ref) Then
ReferenceListData.Add(New ReferenceComponent(ref))
End If
Next refIndex
If theVSProject.WebReferencesFolder IsNot Nothing Then
For Each webRef As EnvDTE.ProjectItem In theVSProject.WebReferencesFolder.ProjectItems
' we need check whether the project item is a web reference.
' user could add random items under this folder
If IsWebReferenceItem(webRef) Then
ReferenceListData.Add(New WebReferenceComponent(Me, webRef))
End If
Next
End If
If _referenceGroupManager Is Nothing Then
Dim referenceManagerFactory As IVsWCFReferenceManagerFactory = CType(ServiceProvider.GetService(GetType(SVsWCFReferenceManagerFactory)), IVsWCFReferenceManagerFactory)
If referenceManagerFactory IsNot Nothing Then
Dim vsHierarchy As IVsHierarchy = ShellUtil.VsHierarchyFromDTEProject(ServiceProvider, DTEProject)
If vsHierarchy IsNot Nothing AndAlso IsServiceReferenceValidInProject(vsHierarchy) AndAlso referenceManagerFactory.IsReferenceManagerSupported(vsHierarchy) <> 0 Then
_referenceGroupManager = referenceManagerFactory.GetReferenceManager(vsHierarchy)
End If
End If
End If
If _referenceGroupManager IsNot Nothing Then
Dim collection As IVsWCFReferenceGroupCollection = _referenceGroupManager.GetReferenceGroupCollection()
For i As Integer = 0 To collection.Count() - 1
Dim referenceGroup As IVsWCFReferenceGroup = collection.Item(i)
ReferenceListData.Add(New ServiceReferenceComponent(collection, referenceGroup))
Next
End If
RefreshReferenceList(ReferenceListData)
_delayUpdatingItems = Nothing
Finally
HoldSelectionChange(False)
End Try
PushSelection()
End Sub
''' <summary>
''' check whether a project item is really a web reference
''' </summary>
''' <param name="webRef"></param>
Private Shared Function IsWebReferenceItem(webRef As EnvDTE.ProjectItem) As Boolean
Dim webRefProperty As EnvDTE.Property = Nothing
Dim properties As EnvDTE.Properties = webRef.Properties
If properties IsNot Nothing Then
Try
webRefProperty = properties.Item("WebReferenceInterface")
Catch ex As ArgumentException
' Ignore those items which is actually not web reference (but random items added by user into the directory.)
End Try
End If
Return webRefProperty IsNot Nothing
End Function
Public Function GetReferencedNamespaceList() As IList(Of String)
Dim threadedWaitDialogFactory = DirectCast(ServiceProvider.GetService(GetType(SVsThreadedWaitDialogFactory)), IVsThreadedWaitDialogFactory)
Dim threadedWaitDialog2 As IVsThreadedWaitDialog2 = Nothing
ErrorHandler.ThrowOnFailure(threadedWaitDialogFactory.CreateInstance(threadedWaitDialog2))
Dim threadedWaitDialog3 = DirectCast(threadedWaitDialog2, IVsThreadedWaitDialog3)
Dim cancellationTokenSource As New CancellationTokenSource
Dim cancellationCallback As New CancellationCallback(cancellationTokenSource)
threadedWaitDialog3.StartWaitDialogWithCallback(
My.Resources.Microsoft_VisualStudio_Editors_Designer.PropPage_ImportedNamespacesTitle,
My.Resources.Microsoft_VisualStudio_Editors_Designer.PropPage_ComputingReferencedNamespacesMessage,
szProgressText:=Nothing,
varStatusBmpAnim:=Nothing,
szStatusBarText:=Nothing,
fIsCancelable:=True,
iDelayToShowDialog:=1,
fShowProgress:=True,
iTotalSteps:=0,
iCurrentStep:=0,
pCallback:=cancellationCallback)
Try
Dim componentModel = DirectCast(ServiceProvider.GetService(GetType(SComponentModel)), IComponentModel)
Dim visualStudioWorkspace = componentModel.GetService(Of VisualStudioWorkspace)
Dim solution = visualStudioWorkspace.CurrentSolution
For Each project In solution.Projects
' We need to find the project that matches by project file path
If project.FilePath IsNot Nothing AndAlso String.Compare(project.FilePath, DTEProject.FullName, ignoreCase:=True) = 0 Then
Dim compilationTask = project.GetCompilationAsync(cancellationTokenSource.Token)
compilationTask.Wait(cancellationTokenSource.Token)
Dim compilation = compilationTask.Result
Dim namespaceNames As New List(Of String)
Dim namespacesToProcess As New Stack(Of INamespaceSymbol)
namespacesToProcess.Push(compilation.GlobalNamespace)
Do While namespacesToProcess.Count > 0
cancellationTokenSource.Token.ThrowIfCancellationRequested()
Dim namespaceToProcess = namespacesToProcess.Pop()
For Each childNamespace In namespaceToProcess.GetNamespaceMembers()
If NamespaceIsReferenceableFromCompilation(childNamespace, compilation) Then
namespaceNames.Add(childNamespace.ToDisplayString())
End If
namespacesToProcess.Push(childNamespace)
Next
Loop
namespaceNames.Sort(CaseInsensitiveComparison.Comparer)
Return namespaceNames
End If
Next
' Return empty list if an error occurred
Return Array.Empty(Of String)
Catch ex As OperationCanceledException
' Return empty list if we canceled
Return Array.Empty(Of String)
Finally
Dim canceled As Integer = 0
threadedWaitDialog3.EndWaitDialog(canceled)
End Try
End Function
Private Class CancellationCallback
Implements IVsThreadedWaitDialogCallback
Private ReadOnly _cancellationTokenSource As CancellationTokenSource
Public Sub New(cancellationTokenSource As CancellationTokenSource)
_cancellationTokenSource = cancellationTokenSource
End Sub
Public Sub OnCanceled() Implements IVsThreadedWaitDialogCallback.OnCanceled
_cancellationTokenSource.Cancel()
End Sub
End Class
Private Shared Function NamespaceIsReferenceableFromCompilation([namespace] As INamespaceSymbol, compilation As Compilation) As Boolean
For Each typeMember In [namespace].GetTypeMembers()
If typeMember.CanBeReferencedByName Then
If typeMember.DeclaredAccessibility = CodeAnalysis.Accessibility.Public Then
Return True
End If
If SymbolEqualityComparer.Default.Equals(typeMember.ContainingAssembly, compilation.Assembly) OrElse typeMember.ContainingAssembly.GivesAccessTo(compilation.Assembly) Then
Return True
End If
End If
Next
Return False
End Function
Private Sub PopulateImportsList(InitSelections As Boolean, Optional RemoveInvalidEntries As Boolean = False)
Dim Namespaces As IList(Of String)
Dim UserImports As String()
If ServiceProvider Is Nothing Then
'We may be tearing down...
Return
End If
' get namespace list earlier to prevent reentrance in this function to cause duplicated items in the list
Namespaces = GetReferencedNamespaceList()
UserImports = GetCurrentImports()
' Gotta make a copy of the currently selected items so I can re-select 'em after
' I have repopulated the list...
Dim currentlySelectedItems As New Specialized.StringCollection
For Each SelectedItem As String In ImportList.SelectedItems
currentlySelectedItems.Add(SelectedItem)
Next
Dim TopIndex As Integer = ImportList.TopIndex
'CurrentList is a dictionary whose keys are all the items which are
' currently in the imports listbox or are in the referenced namespaces
' of the project or are imports added by the user.
'The value of the entry is True if it is a reference namespace or user
' import.
Dim CurrentListMap As New Dictionary(Of String, Boolean)
'Initialize CurrentListMap to include keys from everything currently
' in the listbox. We'll next mark as true only those that the compiler
' and project actually know about.
For Each cItem As String In ImportList.Items
CurrentListMap.Add(cItem, False)
Next
'Create a combined list of referenced namespaces and user-defined imports
Dim NamespacesAndUserImports As New List(Of String)
NamespacesAndUserImports.AddRange(Namespaces)
NamespacesAndUserImports.AddRange(UserImports)
'For each item of NamespacesAndUserImports, make sure the item is in the
' imports listbox, and also set its entry in CurrentListMap to True so
' we know it's a current namespace or user import
For Each name As String In NamespacesAndUserImports
If name.Length > 0 Then
If Not CurrentListMap.ContainsKey(name) Then
'Not already in the listbox - add it
ImportList.Items.Add(name)
CurrentListMap.Add(name, True)
Else
CurrentListMap.Item(name) = True
End If
End If
Next name
If RemoveInvalidEntries Then
For Each item As KeyValuePair(Of String, Boolean) In CurrentListMap
If item.Value = False Then
'The item is not in the referenced namespaces and it's not in the
' user-defined imports list (i.e., the namespace no longer exists, or
' it's a user-import that the user has previously unchecked)
ImportList.Items.Remove(item.Key)
End If
Next
End If
If InitSelections Then
CheckCurrentImports()
End If
For Each item As String In currentlySelectedItems
Dim itemIndex As Integer = ImportList.Items.IndexOf(item)
If itemIndex <> -1 Then
ImportList.SetSelected(itemIndex, True)
End If
Next
If TopIndex < ImportList.Items.Count Then
ImportList.TopIndex = TopIndex
End If
EnableImportGroup()
End Sub
Private Sub AddNamespaceToImportList(ns As String)
If ImportList.Items.IndexOf(ns) = -1 Then
ImportList.Items.Add(ns)
End If
End Sub
Private Sub SelectNamespaceInImportList(_namespace As String, MoveToTop As Boolean)
Dim index As Integer
index = ImportList.Items.IndexOf(_namespace)
If index = -1 AndAlso Not MoveToTop Then
'We skip this step if MoveToTop is true so we avoid adding then moving
'This should only be able to occur if a namespace
' is not in the references
AddNamespaceToImportList(_namespace)
index = ImportList.Items.IndexOf(_namespace)
End If
Try
_updatingImportList = True
If MoveToTop Then
If index <> -1 Then
ImportList.Items.RemoveAt(index)
End If
ImportList.Items.Insert(0, _namespace)
ImportList.SetItemChecked(0, True)
Else
ImportList.SetItemChecked(index, True)
End If
Finally
_updatingImportList = False
End Try
End Sub
''' <summary>
''' Customizable processing done before the class has populated controls in the ControlData array
''' </summary>
''' <remarks>
''' Override this to implement custom processing.
''' IMPORTANT NOTE: this method can be called multiple times on the same page. In particular,
''' it is called on every SetObjects call, which means that when the user changes the
''' selected configuration, it is called again.
''' </remarks>
Protected Overrides Sub PreInitPage()
MyBase.PreInitPage()
PopulateReferenceList()
PopulateImportsList(False)
AdviseReferencesEvents(CType(DTEProject.Object, VSLangProj.VSProject))
AdviseWebReferencesEvents()
AdviseServiceReferencesEvents()
AdviseImportsEvents(CType(DTEProject.Object, VSLangProj.VSProject))
End Sub
Private Function GetCurrentImports() As String()
Dim threadedWaitDialogFactory = DirectCast(ServiceProvider.GetService(GetType(SVsThreadedWaitDialogFactory)), IVsThreadedWaitDialogFactory)
Dim threadedWaitDialog2 As IVsThreadedWaitDialog2 = Nothing
ErrorHandler.ThrowOnFailure(threadedWaitDialogFactory.CreateInstance(threadedWaitDialog2))
Dim threadedWaitDialog3 = DirectCast(threadedWaitDialog2, IVsThreadedWaitDialog3)
Dim cancellationTokenSource As New CancellationTokenSource
Dim cancellationCallback As New CancellationCallback(cancellationTokenSource)
threadedWaitDialog3.StartWaitDialogWithCallback(
My.Resources.Microsoft_VisualStudio_Editors_Designer.PropPage_CurrentImportsTitle,
My.Resources.Microsoft_VisualStudio_Editors_Designer.PropPage_ComputingCurrentImportsMessage,
szProgressText:=Nothing,
varStatusBmpAnim:=Nothing,
szStatusBarText:=Nothing,
fIsCancelable:=True,
iDelayToShowDialog:=1,
fShowProgress:=True,
iTotalSteps:=0,
iCurrentStep:=0,
pCallback:=cancellationCallback)
Try
Dim vsImports As VSLangProj.Imports = CType(DTEProject.Object, VSLangProj.VSProject).Imports
Dim result As New List(Of String)(vsImports.Count)
result.AddRange(vsImports.Cast(Of String)())
result.Sort(CaseInsensitiveComparison.Comparer)
Return result.ToArray()
Catch ex As OperationCanceledException
' Return empty list if we canceled
Return Array.Empty(Of String)()
Finally
Dim canceled As Integer = 0
threadedWaitDialog3.EndWaitDialog(canceled)
End Try
End Function
''' <summary>
''' Customizable processing done after base class has populated controls in the ControlData array
''' </summary>
''' <remarks>
''' Override this to implement custom processing.
''' IMPORTANT NOTE: this method can be called multiple times on the same page. In particular,
''' it is called on every SetObjects call, which means that when the user changes the
''' selected configuration, it is called again.
''' </remarks>
Protected Overrides Sub PostInitPage()
MyBase.PostInitPage()
EnableReferenceGroup()
EnableImportGroup()
' make the import-panel act as if it lost focus so that the selected-row color
' of the Imports CheckedListBox does not look like it is focused
'
ImportPanel_Leave(Me, EventArgs.Empty)
End Sub
''' <summary>
''' Take a snapshot of the user defined imports
''' </summary>
''' <returns>A dictionary with import name/is namespace pairs</returns>
Private Function GetUserDefinedImportsSnapshot() As IDictionary(Of String, Boolean)
' First, we get a collection of referenced namespaces that is fast to
' search...
Dim ReferencedNamespaces As New Hashtable
For Each ReferencedNamespace As String In GetReferencedNamespaceList()
If ReferencedNamespace <> "" Then
ReferencedNamespaces.Add(ReferencedNamespace, Nothing)
End If
Next
' We save all currently imported namespaces
' Each import is stored in the hashtable with the
' value set to "True" if it is a namespace known to the compiler
Dim UserDefinedImports As New Dictionary(Of String, Boolean)
For Each UserImport As String In GetCurrentImports()
UserDefinedImports.Add(UserImport, ReferencedNamespaces.Contains(UserImport))
Next
Return UserDefinedImports
End Function
''' <summary>
''' Remove any user imported namespaces that were known to the compilerat the time the ImportsSnapshot
''' was taken
''' </summary>
''' <param name="ImportsSnapshot">
''' A snapshot of the project imports taken sometime before...
''' </param>
Private Function TrimUserImports(ImportsSnapshot As IDictionary(Of String, Boolean)) As String()
' Let's give the compiler time to update the namespace list - it looks like we may
' have a race-condition here, but I can't find out why.... and o
Thread.Sleep(10)
' First, we get a collection of referenced namespaces that is fast to
' search...
Dim ReferencedNamespaces As New Hashtable
For Each ReferencedNamespace As String In GetReferencedNamespaceList()
If ReferencedNamespace <> "" Then
ReferencedNamespaces.Add(ReferencedNamespace, Nothing)
End If
Next
Dim ResultList As New List(Of String)
Dim snapshot As IEnumerable(Of KeyValuePair(Of String, Boolean)) = ImportsSnapshot
For Each PreviousImportEntry As KeyValuePair(Of String, Boolean) In snapshot
If PreviousImportEntry.Value Then
' This was a namespace known to the compiler before whatever references were removed...
' Only add it to the result if it is still known!
If ReferencedNamespaces.Contains(PreviousImportEntry.Key) Then
ResultList.Add(PreviousImportEntry.Key)
End If
Else
ResultList.Add(PreviousImportEntry.Key)
End If
Next
Return ResultList.ToArray()
End Function
Private Sub RemoveReference_Click(sender As Object, e As EventArgs) Handles RemoveReference.Click
RemoveSelectedReference()
End Sub
Private Sub RemoveSelectedReference()
Dim ItemIndices As ListView.SelectedIndexCollection = ReferenceList.SelectedIndices
Dim ItemIndex As Integer
Dim ref As ReferenceComponent
Dim refComponent As IReferenceComponent
Dim ReferenceRemoved As Boolean = False 'True if one or more references was actually removed
If ItemIndices.Count = 0 Then
Return
End If
Using New WaitCursor
Dim ImportsSnapshot As IDictionary(Of String, Boolean) = Nothing
Using New ProjectBatchEdit(ProjectHierarchy)
Try
Dim errorString As String = Nothing
Dim refName As String = String.Empty
_updatingReferences = True
ReferenceList.BeginUpdate()
For i As Integer = ItemIndices.Count - 1 To 0 Step -1
Dim err As String = Nothing
ItemIndex = ItemIndices(i)
If ImportsSnapshot Is Nothing Then
' Since we are going to remove a reference, and we haven't taken a snapshot of
' the user imports before, we better do it now!
ImportsSnapshot = GetUserDefinedImportsSnapshot()
End If
'Remove from project references
EnterProjectCheckoutSection()
Try
refComponent = TryCast(ReferenceList.Items(ItemIndex).Tag, IReferenceComponent)
If refComponent IsNot Nothing Then
ref = TryCast(refComponent, ReferenceComponent)
If ref IsNot Nothing Then
If IsImplicitlyAddedReference(ref.CodeReference) Then
Debug.Fail("Implicitly added references should have been filtered out and never displayed in our list")
Continue For
End If
End If
refName = refComponent.GetName()
refComponent.Remove()
ReferenceRemoved = True
Else
Debug.Fail("Unknown reference item")
End If
'Remove from local storage
ReferenceList.Items.RemoveAt(ItemIndex)
Catch ex As Exception When ReportWithoutCrash(ex, NameOf(RemoveSelectedReference), NameOf(ReferencePropPage))
If ProjectReloadedDuringCheckout Then
' If the Project could be reloaded, we should return ASAP, because the designer has been disposed
Return
End If
If IsCheckoutCanceledException(ex) Then
'User already saw a message, no need to show an error message. Also, don't
' want to continue trying to remove references.
Exit For
Else
' some reference can not be removed (like mscorlib)
err = My.Resources.Microsoft_VisualStudio_Editors_Designer.GetString(My.Resources.Microsoft_VisualStudio_Editors_Designer.PPG_Reference_CanNotRemoveReference, refName, ex.Message)
End If
Finally
LeaveProjectCheckoutSection()
End Try
If err IsNot Nothing Then
If errorString Is Nothing Then
errorString = err
Else
errorString += err
End If
End If
Next
If errorString IsNot Nothing Then
ShowErrorMessage(errorString)
End If
Finally
' If the Project is reloaded, don't do anything as the page is disposed. VSWhidbey: 595444
If Not ProjectReloadedDuringCheckout Then
ReferenceList.EndUpdate()
' Update buttons...
EnableReferenceGroup()
EnableImportGroup()
_updatingReferences = False
End If
End Try
End Using
If ReferenceRemoved Then
' Now, we better remove any user imports that is no longer
' known to the compiler...
If ImportsSnapshot IsNot Nothing Then
SaveImportedNamespaces(TrimUserImports(ImportsSnapshot))
End If
'RemoveInvalidEntries=True here because so that we can remove imports
' that correspond to the removed references, instead of just unchecking
' them. This will also clean up any other invalid unchecked imports in
' the list, which might be a minor surprise to the user, but shouldn't be
' too bad, and is the safest fix at this point in the schedule.
PopulateImportsList(InitSelections:=True, RemoveInvalidEntries:=True)
SetDirty(ImportList)
End If
End Using
End Sub
Private Sub addContextMenuStrip_Opening(sender As Object, e As CancelEventArgs) Handles addContextMenuStrip.Opening
Dim vsHierarchy As IVsHierarchy = ShellUtil.VsHierarchyFromDTEProject(ServiceProvider, DTEProject)
If vsHierarchy IsNot Nothing Then