The Amadeus.NET Blog

Development, Projects, Plans and Ideas on .NET, Visual Studio, Windows XP & Vista
Heroes Happen Here

hero_small copyHallo,

Although it might seem as if I was totally lost, the truth is that the last 1.5 years were more than productive and possibly the most exciting concerning research. I have been traveling around Europe, meeting people and sharing ideas, while at the same time trying to catch up with all the changes around us lately. Windows Vista, Visual Studio 2008, and Windows Server 2008 are the new games we all now have to spend days and nights to get used with and as platforms seem to change now in a frequency no much different than new car models appear, we sure need to become Heroes, to stay tuned!

While changes don't seem to be so dramatic at first sight, digging a bit dipper, you'll find out they really are. LINQ, XAML and many more for developers and ITs absolute pleasure which is Virtualization in Windows Server 2008 are only some of the exciting new features, while things behind the scenes also change rampantly.

In the future, we shall be discussing many of these new features in many articles. At the same time, many projects I started some time ago, are either finished or close to finish, new have started and of course, all are now adjusted to the new platforms and environment.

 

microsoft_small_2 I've also spent a considerable amount of time designing some new sites and utilizing some interesting new initiatives such as Microsoft's new open source community Codeplex and MSDN Code Gallery that actually substitutes GotDotNet, using a platform much similar to that of Codeplex.

Some of my projects will be published in these sites, while the rest will be available through my new websites:

www.amadeuspage.com
www.amadeuspage.net

Finally, I need to appologize for articles that wait to be finished for so long, but a can reassure you that the future is going to be far more exciting.

Regards,
Perikles C. Stephanidis

The Amadeus .NET Assembly Browser (Part I)

The Amadeus .NET Assembly Browser, is a shell extension for Windows® XP that adds a property page to the Properties window of executable files (.dll and .exe), that represent .NET assemblies. This property page contains an advanced object browser, similar to the one in Visual Studio® yet with many more features, that allows you to investigate the contents of the assembly. This application does not require Microsoft® Visual Studio® to be installed in your system. It does however, require the .NET Framework (v.2.0), just like any .NET application.

The Amadeus .NET Assembly Browser property page, displaying the contents of the Microsoft.VisualBasic.dll assembly in VB.     The Amadeus .NET Assembly Browser property page, displaying the contents of the Microsoft.VisualBasic.dll assembly in C#.

(The Amadeus .NET Assembly Browser property page, displaying the contents of the Microsoft.VisualBasic.dll assembly, in VB and in C#)

In a number of articles that will follow, I will describe some of the details of the technology used in this tool. In the meantime, you can download and try the pre-release version of this tool here. This application is free to use. Please post any comments to the workspace site or here.

Implementing 'Show All Files' in a Visual Studio project.

This article shows how a project implementer in Visual Studio 2005 can handle the "Show All Files" command to show/hide nodes (that may or may not represent files on disk). Before I proceed, it's important to mention that, in my projects, I don't use the MPF classes. I've developed my own classes that wrap the interop interfaces and I've added my own logic to them. Therefore, some of the examples in this article, may need to be modified before they are used in a different project system.

This article supposes that you are familiar with:

-Developing a project system and managing the contents of the project hierarchy.
-Handling commands through an IOleCommandTarget interface. (The IVsUIHierarchy interface, handles commands almost the same way)

Prerequisites:

1) A collection that holds all hierarchy events clients that request to be advised about your hierarchy events through
    Microsoft.VisualStudio.Shell.Interop.IVsHierarchy.AdviseHierarchyEvents().

2) One or more specific classes (or base classes like FileNode of the MPF), that will be used to represent your extra nodes in the project
    hierarchy.

3) A method that iterates through the members of the hierarchy events clients and advises them of any changes.

4) A variable that holds a project specific value that indicates the current view mode (Normal/ViewAll).

The Implementation:

Clients that wish to be advised of project hierarchy events, request to be added to our list of clients through IVsHierarchy.AdviseHierarchyEvents. The very first such client calls this method is the solution (IVsSolution) itself. By ignoring this call, we simply cannot inform the solution of any changes in out project hierarchy structure. The MPF classes handle this method internally and, as far as I know, inheritors don't have access to the private hierarchyEventSinks list, created and used in the HierarchyNode class. Those using the MPF classes will have to override AdviseHierarchyEvents and add extra code after calling the base class or, go change the access of the hierarchyEventSinks field to public (easy since if you are using the MPF classes, you have to include the actual source code of the project model in your project).

Private m_HierarchyEventsClient As New Dictionary(Of UInt32, IVsHierarchyEvents)
...

#Region " AdviseHierarchyEvents "
    Protected Overridable Function AdviseHierarchyEvents(ByVal pEventSink As IVsHierarchyEvents, ByRef pdwCookie As UInteger) As Integer Implements IVsHierarchy.AdviseHierarchyEvents, IVsUIHierarchy.AdviseHierarchyEvents
        If m_IsDisposed Then
            Return VSConstants.E_UNEXPECTED
        End If

        If m_ReleasedCookies.Count = 0 Then
            pdwCookie = CUInt(m_HierarchyEventsClient.Count + 1)
            m_HierarchyEventsClient.Add(pdwCookie, pEventSink)
        Else
            pdwCookie = m_ReleasedCookies(0)
            m_HierarchyEventsClient(pdwCookie) = pEventSink
            m_ReleasedCookies.Remove(pdwCookie)
        End If

        Return VSConstants.S_OK
    End Function
#End Region

(Example of implementing the AdviseHierarchyEvents() method)

 

    Friend Overloads Sub AdviseClients(ByVal propid As Int32)
        If Me.Project.HierarchyEventsClients.Count > 0 Then
            Dim clientID As UInt32

            For Each clientID In Me.Project.HierarchyEventsClients.Keys
                Dim client As IVsHierarchyEvents = Me.Project.HierarchyEventsClients(clientID)

                If client IsNot Nothing Then
                    Try
                        client.OnPropertyChanged(m_ItemID, propid, 0)
                    Catch ex As Exception
                        Me.Project.UnadviseHierarchyEvents(clientID)
                    End Try
                End If
            Next
        End If
    End Sub

    ' This accepts an array that allows us to fire multiple events
    ' using a single call.
    Friend Overloads Sub AdviseClients(ByVal propid() As Int32)
        If (propid IsNot Nothing) AndAlso _
            (propid.Length > 0) Then

            Dim p As Int32

            For Each p In propid
                Me.AdviseClients(p)
            Next
        End If
    End Sub

(Example of a method that iterates through the hierarchy events clients collection and informs them of any changes. m_ItemID is the item id of the item in the project hierarchy that fires the event. In our case this will most possibly be the project node.)

 

Handling the "Show All Files" command:

Next thing we do is handle the VSStd2KCmdID.SHOWALLFILES command through our implementation of IVsUIHierarchy.QueryStatusCommand and IVsUIHierarchy.ExecCommand(). In MPF we will have to override the QueryStatus() and ExecCommand() of the base HierarchyNode.

    Private Overloads Function QueryStatusCommand(ByVal itemid As UInteger, ByRef pguidCmdGroup As System.Guid, ByVal cCmds As UInteger, ByVal prgCmds() As Microsoft.VisualStudio.OLE.Interop.OLECMD, ByVal pCmdText As System.IntPtr) As Integer Implements IVsUIHierarchy.QueryStatusCommand
        If m_IsDisposed Then
            Return VSConstants.E_UNEXPECTED
        End If

        If pguidCmdGroup = VSConstants.VSStd2K Then

            Select Case prgCmds(0).cmdID
...

                Case VSConstants.VSStd2KCmdID.SHOWALLFILES
                    If m_ViewMode = EProjectMode.Normal Then
                        prgCmds(0).cmdf = CUInt(OLECMDF.OLECMDF_SUPPORTED Or OLECMDF.OLECMDF_ENABLED)
                    Else
                        prgCmds(0).cmdf = CUInt(OLECMDF.OLECMDF_SUPPORTED Or OLECMDF.OLECMDF_ENABLED Or OLECMDF.OLECMDF_LATCHED)
                    End If

                    Return VSConstants.S_OK

...

            End Select
...

    
        End If

    End Function

(Example of implementing IVsUIHierarchy.ExecCommand())

 

Private Overloads Function ExecCommand(ByVal itemid As UInteger, ByRef pguidCmdGroup As System.Guid, ByVal nCmdID As UInteger, ByVal nCmdexecopt As UInteger, ByVal pvaIn As System.IntPtr, ByVal pvaOut As System.IntPtr) As Integer Implements IVsUIHierarchy.ExecCommand If m_IsDisposed Then Return VSConstants.E_UNEXPECTED End If ... If pguidCmdGroup = VSConstants.VSStd2K Then Select Case nCmdID Case VSConstants.VSStd2KCmdID.SHOWALLFILES If m_ViewMode = EProjectMode.Normal Then m_ViewMode = EProjectMode.ViewAll





' In MPF we would simple create the new FileNode objects
' here and add the to the project before calling AdviseClients()
Else m_ViewMode = EProjectMode.Normal



' In MPF we would remove any nodes from the project here
' before calling AdviseClients()
End If

m_ProjectFolder.AdviseClients(New __VSHPROPID() _ {__VSHPROPID.VSHPROPID_Expandable, _ __VSHPROPID.VSHPROPID_FirstChild, _ __VSHPROPID.VSHPROPID_ExpandByDefault, _ __VSHPROPID.VSHPROPID_Expanded}) Return VSConstants.S_OK ... End Select ... Else Return OLEInterop.Constants.OLECMDERR_E_UNKNOWNGROUP End If End Function

(Example of implementing IVsUIHierarchy.ExecCommand())

In the last example above, m_ViewMode represents our internal variable that holds a project specific value indicating the current view mode. When the event is fired, the solution (IVsSolution), that is also added in our list of hierarchy events clients, will immediately call IVsHierarchy.GetProperty() to obtain the new structure of the project hierarchy. In MPF, we can simply instantiate all the new nodes that we need to appear, or remove them depending on the current view mode and before calling AdviseClients(). The MPF base classes would then take care of populating the hierarchy.

For implementers of a project that uses the MPF classes, this article has ended here!

 

    Private Overloads Function GetProperty(ByVal itemid As UInteger, ByVal propid As Integer, ByRef pVar As Object) As Integer Implements IVsHierarchy.GetProperty, IVsUIHierarchy.GetProperty
        Select Case propid
            Case __VSHPROPID.VSHPROPID_FirstChild, __VSHPROPID.VSHPROPID_FirstVisibleChild
                Select Case m_ViewMode
                    Case EProjectMode.Normal
                    ' Return the first item id depending on view mode.
...

                    Case EProjectMode.ViewAll
                    ' Return the first item id depending on view mode.
...

                End Select
...

                Return New UIntPtr(VSConstants.VSITEMID_NIL)
...

        End Select
    End Function

(Example of implementing the IVsHierarchy.GetProperty() method on the project node or any file or folder node that has children we want to appear only when Show All Files is selected.)

 

    Private Overloads Function GetProperty(ByVal itemid As UInteger, ByVal propid As Integer, ByRef pVar As Object) As Integer Implements IVsHierarchy.GetProperty, IVsUIHierarchy.GetProperty
        Select Case propid
            Case __VSHPROPID.VSHPROPID_NextSibling, __VSHPROPID.VSHPROPID_NextVisibleSibling
                Select Case m_ViewMode
                    Case EProjectMode.Normal
                    ' Return the next item id depending on view mode.
...

                    Case EProjectMode.ViewAll
                    ' Return the next item id depending on view mode.
...

                End Select
...

                Return New UIntPtr(VSConstants.VSITEMID_NIL)
...

        End Select
    End Function

(Example of implementing the IVsHierarchy.GetProperty() method on the any file or folder node.)

 

This concludes this brief article describing how to implement the "Show All Files" feature in Visual Studio 2005.

Using the Windows Forms designer as your custom Design editor. (Part I)

While using the Visual Studio core editor as your text editor in a language service is a known and straightforward procedure, creating a designer has been totally left in the responsibility of the language service or editor implementer. In this quest, the most wanted designer is the Windows Forms Designer, that can be customized (along with the toolbox), and used as our custom designer.

Our instruments to accomplish this, will be the following classes and interfaces, some provided with the .NET Framework, while others come with the Visual Studio SDK:

.NET Framework:

System.ComponentModel.Design.DesignSurface
System.ComponentModel.Design.IDesignerHost (...and other services)
System.Windows.Forms.Design.DocumentDesigner
System.Drawing.Design.ToolboxItem

...and a number of controls and their corresponding designers, based on the controls we want to add to our designer and the toolbox.

Visual Studio SDK:

Microsoft.VisualStudio.Shell.WindowPane
Microsoft.VisualStudio.Shell.Interop.IVsToolboxUser
...optionally:
Microsoft.VisualStudio.Shell.Interop.IVsDocOutlineProvider
...and whatever else can be implemented by an editor. The majority are already implemented by the WindowPane class.

1) The first step of course, is to create and register the editor factory.

Click to collapseCreating and registering an editor in managed code (VB):

...
 MSVSIP.ProvideEditorFactory(GetType(MyEditorFactory), 106), _
 MSVSIP.ProvideEditorExtensionAttribute(GetType(MyEditorFactory), ".ext1", 32, _
    ProjectGuid:="{00000000-0000-0000-0000-000000000000}", _
    TemplateDir:="..\..\Templates", _
    NameResourceID:=114, _
    DefaultName:="MyEditor"), _
 MSVSIP.ProvideEditorExtensionAttribute(GetType(MyEditorFactory), ".ext2", 32), _
 MSVSIP.ProvideEditorExtensionAttribute(GetType(MyEditorFactory), ".ext3", 32), _
 ProvideOpenWithEntry(GetType(MyEditorFactory), LogicalView.Code, "#115"), _
 ProvideOpenWithEntry(GetType(MyEditorFactory), LogicalView.Designer, "#116"), _
 Guid("00000000-0000-0000-0000-000000000000"), _
 CLSCompliant(False)> _
Public NotInheritable Class MyLanguageServiceOrEditorPackage
...

(Example of the attributes added on a managed VsPackage class)

Protected Overrides Sub Initialize()
...
    ' Register the Editor Factory
    editorFactory = New MyEditorFactory(Me)
    MyBase.RegisterEditorFactory(editorFactory)
...
End Sub

(Registration of the editor factory in the Initialize() method of the VsPackage class)

<ProvideView(LogicalView.Code, Nothing), _
 ProvideView(LogicalView.Debugging, Nothing), _
 ProvideView(LogicalView.Primary, Nothing), _
 ProvideView(LogicalView.Text, Nothing), _
 ProvideView(LogicalView.Designer, "Form")> _
<Guid("00000000-0000-0000-0000-000000000000")> _
<CLSCompliant(False)> _
Public NotInheritable Class MyEditorFactory
    Implements IVsEditorFactory
...

(Optional attributes on the editor factory class)

#Region " MapLogicalView " ' This method is called by the Environment (inside IVsUIShellOpenDocument:: ' OpenStandardEditor and OpenSpecificEditor) to map a LOGICAL view to a ' PHYSICAL view. A LOGICAL view identifies the purpose of the view that is ' desired (e.g. a view appropriate for Debugging [LOGVIEWID_Debugging], or a ' view appropriate for text view manipulation as by navigating to a find ' result [LOGVIEWID_TextView]). A PHYSICAL view identifies an actual type ' of view implementation that an IVsEditorFactory can create. ' ' NOTE: Physical views are identified by a string of your choice with the ' one constraint that the default/primary physical view for an editor ' *MUST* use a NULL string as its physical view name (*pbstrPhysicalView = NULL). ' ' NOTE: It is essential that the implementation of MapLogicalView properly ' validates that the LogicalView desired is actually supported by the editor. ' If an unsupported LogicalView is requested then E_NOTIMPL must be returned. ' ' NOTE: The special Logical Views supported by an Editor Factory must also ' be registered in the local registry hive. LOGVIEWID_Primary is implicitly ' supported by all editor types and does not need to be registered. ' For example, an editor that supports a ViewCode/ViewDesigner scenario ' might register something like the following: ' HKLM\Software\Microsoft\VisualStudio\8.0\Editors\ ' {...guidEditor...}\ ' LogicalViews\ ' {...LOGVIEWID_TextView...} = s '' ' {...LOGVIEWID_Code...} = s '' ' {...LOGVIEWID_Debugging...} = s '' ' {...LOGVIEWID_Designer...} = s 'Form' ' Private Function MapLogicalView(ByRef rguidLogicalView As Guid, _
ByRef pbstrPhysicalView As String) As Integer Implements IVsEditorFactory.MapLogicalView
pbstrPhysicalView = Nothing Select Case rguidLogicalView Case VSConstants.LOGVIEWID_Primary, _ VSConstants.LOGVIEWID_Code, _ VSConstants.LOGVIEWID_Debugging, _ VSConstants.LOGVIEWID_TextView Return VSConstants.S_OK Case VSConstants.LOGVIEWID_Designer pbstrPhysicalView = "Form" Return VSConstants.S_OK Case Else Return VSConstants.E_NOTIMPL End Select End Function #End Region

 

(Standard implementation of the MapLogicalView method)

...And the greatest brick (This also demonstrates how to create and use an instance of the core editor).:

#Region " CreateEditorInstance " Private Function CreateEditorInstance(ByVal grfCreateDoc As System.UInt32, _
        ByVal pszMkDocument As String, ByVal pszPhysicalView As String, _
        ByVal pvHier As IVsHierarchy, ByVal itemid As System.UInt32, _
        ByVal punkDocDataExisting As System.IntPtr, ByRef ppunkDocView As System.IntPtr, _
        ByRef ppunkDocData As System.IntPtr, ByRef pbstrEditorCaption As String, _
        ByRef pguidCmdUI As Guid, _
        ByRef pgrfCDW As Integer) As Integer Implements IVsEditorFactory.CreateEditorInstance ' Initialize to null ppunkDocView = IntPtr.Zero ppunkDocData = IntPtr.Zero pgrfCDW = 0 pbstrEditorCaption = Nothing Dim buffer As IVsTextLines ' Validate inputs If (grfCreateDoc And (VSConstants.CEF_OPENFILE Or VSConstants.CEF_SILENT)) = 0 Then Throw New ArgumentException("Only Open or Silent is valid") End If If Not (pszMkDocument.ToLower.EndsWith(".ext1") OrElse _ pszMkDocument.ToLower.EndsWith(".ext2") OrElse _ pszMkDocument.ToLower.EndsWith(".ext3")) Then Throw New ArgumentException("Only .ext1, .ext2, or .ext3 files " _
"are supported by this factory."
) End If If punkDocDataExisting = IntPtr.Zero Then ' Create a text buffer if one does not exist already. Try ' Instantiate a text buffer of type VsTextBuffer. Dim clsidTextBuffer As Guid = GetType(VsTextBufferClass).GUID Dim iidTextBuffer As Guid = GetType(IVsTextLines).GUID buffer = My.Instance(Of VsTextBufferClass, IVsTextLines)() If buffer IsNot Nothing Then ' Site the text buffer with the service provider we were provided. Dim textBufferSite As IObjectWithSite = TryCast(buffer, IObjectWithSite) If textBufferSite IsNot Nothing Then textBufferSite.SetSite(m_OLESP) End If End If Catch ex2 As Exception Return VSConstants.E_FAIL End Try Else ' Check if the existing DocData implements IVsTextLines. buffer = TryCast(Marshal.GetObjectForIUnknown(punkDocDataExisting), IVsTextLines) If buffer Is Nothing Then Return VSConstants.VS_E_INCOMPATIBLEDOCDATA End If End If If pszMkDocument.ToLower.EndsWith(".ext1") Then ' Assign our language service with the new buffer. buffer.SetLanguageServiceID(GuidList.guidMyLanguage) ElseIf pszMkDocument.ToLower.EndsWith(".ext2") Then buffer.SetLanguageServiceID(GuidList.guidMyLanguage) ElseIf pszMkDocument.ToLower.EndsWith(".ext3") Then buffer.SetLanguageServiceID(GuidList.guidMyLanguage) End If If pszPhysicalView = "Form" Then Dim view As New MyDesignView(buffer, pvHier, itemid, pszMkDocument) If view IsNot Nothing Then ' Now tell the caller about all this new stuff ' that has been created. ppunkDocView = Marshal.GetIUnknownForObject(view) ppunkDocData = Marshal.GetIUnknownForObject(buffer) ' This caption is appended to the filename and ' lets us know our invocation of the core editor ' is up and running. pbstrEditorCaption = " [Design]" Return VSConstants.S_OK Else My.VsUIShell.ShowMessageBox(0, Guid.Empty, _ "My Editor Factory", _ "Design View is currently unavailable." & vbCrLf & _ "Make sure the file is part of a MyProject.", "", 0, _ OLEMSGBUTTON.OLEMSGBUTTON_OK, _ OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST, _ OLEMSGICON.OLEMSGICON_WARNING, 0, 0) Return VSConstants.S_OK End If Else Try ' Instantiate a code window. Dim codeWindow As IVsCodeWindow codeWindow = My.Instance(Of VsCodeWindowClass, IVsCodeWindow)() If codeWindow IsNot Nothing Then ' We do any needed initialization, before ' we call SetBuffer(). Dim codeWindowEx As IVsCodeWindowEx = TryCast(codeWindow, IVsCodeWindowEx) If codeWindowEx IsNot Nothing Then Dim init(0) As INITVIEW codeWindowEx.Initialize( _ CUInt(_codewindowbehaviorflags.CWB_DEFAULT), _ 0, String.Empty, String.Empty, _ CUInt(TextViewInitFlags.VIF_SET_WIDGET_MARGIN), init) End If ' Give the text buffer to the code window. ' We are giving up ownership of the text buffer! codeWindow.SetBuffer(buffer) ' Now tell the caller about all this new stuff ' that has been created. ppunkDocView = Marshal.GetIUnknownForObject(codeWindow) ppunkDocData = Marshal.GetIUnknownForObject(buffer) ' Specify the command UI to use so keypresses are ' automatically dealt with. pguidCmdUI = VSConstants.GUID_TextEditorFactory ' This caption is appended to the filename and ' lets us know our invocation of the core editor ' is up and running. pbstrEditorCaption = " [Code]" Return VSConstants.S_OK End If Catch ex As Exception Return VSConstants.E_FAIL End Try End If Return VSConstants.E_FAIL End Function #End Region

 

 

2) The second step is to implement our designer. In the implementation of the CreateEditorInstance method above, we created and returned to the environment an instance of our designer (MyDesignView). The first thing that the environment asks for, and we provide through the ppunkDocView argument, is a class implementing Microsoft.VisualStudio.Shell.Interop.IVsWindowPane. When we create an instance of the core editor as our code editor, the VsCodeWindowClass that we instantiate and provide, already implements IVsWindowPane. But when developing a designer, we are the only implementers of the document view which we have to design from scratch. The environment uses the IVsWindowPane to tell us, among other things, when to create our window and who to parent our window to. In managed code, we can use many of the classes provided, that can save us work and time. In this case, we shall be using the Microsoft.VisualStudio.Shell.WindowPane class, which properly implements the IVsWindowPane interface, as well as other interfaces like System.IServiceProvider and Microsoft.VisualStudio.OLE.Interop.IOleCommandTarget. As with managed tool windows (the Microsoft.VisualStudio.Shell.ToolWindowPane class inherits WindowPane) we return the actual control (that in our case is going to be windows forms designer), through the Window property.

The windows forms designer is actually a design surface (System.ComponentModel.Design.DesignSurface), that hosts a control (root component). For that control to be able to be used as a root component in a design surface, it must have a designer assigned, that implements System.ComponentModel.Design.IRootDesigner. In a windows forms application a System.Windows.Forms.Form is usually used as root component. We will use a UserControl. Of all the designers in the .NET Framework that inherit IRootDesigner, the best to be used as a document view and provide all the functionality that we need in our designer, is System.Windows.Forms.Design.DocumentDesigner

To summarize, we need the following:

a) A class that inherits System.ComponentModel.Design.DesignSurface. The design surface is responsible for providing the environment with individual designers of every control added in the design surface; including our root component.

b) A control that will be used as our root component. In our case a UserControl. This is where controls picked from the toolbox will be dropped.

c) A designer that implements System.ComponentModel.Design.IRootDesigner. In our case a DocumentDesigner. The designer is responsible for providing all the design-time features of our design view, coordinating any new controls added and optionally updating the underlying text buffer of our document.

d) One or more classes that inherit System.Drawing.Design.ToolboxItem. These represent the toolbox items that will be added in the toolbox when our designer is loaded.

e) One or more controls and components that the toolbox items represent and that the user will be able to drop into our designer. As in a windows forms application, controls will be added as children of our root component, while components will be added in the Component Tray.
The addition of such controls can trigger the creation of code.

f) Designers for each of the control or component we add in the toolbox. The designers will be provided to the environment through our custom design surface and they are responsible for controlling the design-time functionality of the controls or components. The user interaction with the controls in design-time, helps us edit the underlying code (if any) in the text buffer.

 

Click to collapse Creating a Windows Forms Designer:

Public Class MyDesignSurface Inherits DesignSurface Public Sub New(ByVal sp As System.IServiceProvider) MyBase.New(sp, GetType(MyDocView)) Dim mcs As OleMenuCommandService = New OleMenuCommandService(Me) Me.ServiceContainer.RemoveService(GetType(IMenuCommandService)) Me.ServiceContainer.AddService(GetType(IMenuCommandService), mcs) End Sub ...
Protected Overrides Function CreateDesigner( _
ByVal component As System.ComponentModel.IComponent, _
ByVal rootDesigner As Boolean) As System.ComponentModel.Design.IDesigner
Dim designer As IDesigner If TypeOf component Is MyDocView Then designer = New MyDocViewDesigner ElseIf TypeOf component Is MyControl Then designer = New MyControlDesigner Else designer = MyBase.CreateDesigner(component, rootDesigner) End If Return designer End Function

... End Class

 

(Our custom design surface)

<Designer(GetType(MyDocViewDesigner), GetType(IRootDesigner))> _
<CLSCompliant(False)> _
Public Class MyDocView
    Inherits UserControl
...

(Our root component. We may add custom painting, code that arranges any child controls added or whatever else we want here.)

<ToolboxItemFilter("MyEditor.MyDesigner")> _ Public Class MyDocViewDesigner Inherits DocumentDesigner Private m_DocView As MyDocView Private m_DVC As DesignerVerbCollection = Nothing ... Public Overrides Sub Initialize(ByVal component As System.ComponentModel.IComponent) MyBase.Initialize(component) If Not TypeOf component Is MyDocView Then Throw New Exception("This designer requires a MyDocView control.") End If component.Site.Name = "MyDesigner" m_DocView = CType(component, MyDocView) m_DVC = New DesignerVerbCollection( _
New DesignerVerb() {New DesignerVerb("Say Hallo", AddressOf Me.SayHallo)}) Dim compChangeSvc As IComponentChangeService = _
CType(Me.GetService(GetType(IComponentChangeService)), IComponentChangeService) If compChangeSvc IsNot Nothing Then AddHandler compChangeSvc.ComponentRemoving, AddressOf Me.ComponentRemoving AddHandler compChangeSvc.ComponentRemoved, AddressOf Me.ComponentRemoved End If End Sub ... Protected Overrides Function CreateToolCore(ByVal tool As System.Drawing.Design.ToolboxItem, _
ByVal x As Integer, ByVal y As Integer, ByVal width As Integer, ByVal height As Integer, _
ByVal hasLocation As Boolean, ByVal hasSize As Boolean) As System.ComponentModel.IComponent() ' Creation sequence. ' This is the first method called when a component is about to be created. ' The base implementation will first call CreateComponentsCore(host, defaultValues) ' in the MyToolboxItem which in turn will call its CreateComponentsCore(host). ' The base implementation of CreateComponentsCore(host) will create the ' component which will ask from the MyDesignSurface to create its designer. ' MyDesignSurface will create the designer and its initialization will ' take place before this method returns. ' TODO: Designer Updating Procedure. ' We need to check for existing elements and deside if we can add ' the dropped element. We must also calculate the size based on existing ' elements and rearrange all components in the container of the root component. ' Child elements will do the same for their children. ' Finally we update the underlying code (if any). Dim component() As IComponent = MyBase.CreateToolCore(tool, x, y, width, height, _
hasLocation, hasSize) ... Return component End Function ... Public Overrides Function CanParent(ByVal control As System.Windows.Forms.Control) As Boolean ' The root component can only parent the controls we allow. If TypeOf control Is MyControl Then Return True End If End Function ... Protected Overrides Sub OnDragEnter(ByVal de As System.Windows.Forms.DragEventArgs) Dim tbxItem As System.Drawing.Design.ToolboxItem Dim tbxData As OleDataObject = New OleDataObject(de.Data) Dim tbxItemContainer As New ToolboxItemContainer(tbxData) Dim host As IDesignerHost = CType(Me.GetService(GetType(IDesignerHost)), IDesignerHost) Dim tbxSvc As IToolboxService = CType(Me.GetService(GetType(IToolboxService)), _
IToolboxService) If tbxSvc IsNot Nothing Then If tbxSvc.IsSupported(de.Data, host) Then If tbxItemContainer IsNot Nothing Then tbxItem = tbxItemContainer.GetToolboxItem(Nothing) If TypeOf tbxItem Is MyToolboxItem Then Dim myTbxItem As MyToolboxItem = CType(tbxItem, MyToolboxItem) 'We check here if we can parent the selected control. 'If yes then... MyBase.OnDragEnter(de) Return End If End If End If End If de.Effect = DragDropEffects.None End Sub ... Public Overrides ReadOnly Property Verbs() As System.ComponentModel.Design.DesignerVerbCollection Get Return m_DVC End Get End Property ... Private ReadOnly Property Window() As MyDesignView Get
'We have added our window pane as a service.
Return CType(Me.GetService(GetType(MyDesignView)), MyDesignView) End Get End Property ... Private Sub SayHallo(ByVal sender As Object, ByVal e As EventArgs) MsgBox("Hallo!") End Sub Private Sub ComponentRemoving(ByVal sender As Object, _
ByVal e As System.ComponentModel.Design.ComponentEventArgs)
If MsgBox("Are you sure?", MsgBoxStyle.YesNo) = MsgBoxResult.No Then Throw New Exception End If End Sub Private Sub ComponentRemoved(ByVal sender As Object, _
ByVal e As System.ComponentModel.Design.ComponentEventArgs)
'Update code? End Sub ... Protected Overrides Sub Dispose(ByVal disposing As Boolean) Dim compChangeSvc As IComponentChangeService = _
CType(Me.GetService(GetType(IComponentChangeService)), IComponentChangeService)
If compChangeSvc IsNot Nothing Then RemoveHandler compChangeSvc.ComponentRemoving, AddressOf Me.ComponentRemoving RemoveHandler compChangeSvc.ComponentRemoved, AddressOf Me.ComponentRemoved End If MyBase.Dispose(disposing) End Sub ... End Class

 

(Our Document Designer)

This example only shows a few of the design-time features we can provide to the user by overriding the methods of the DocumentDesigner this class inherits. The DocumentDesigner is actually the heart of our designer. Ever since we don't develop just another managed language that uses the .NET Framework to create windows forms applications or class libraries (where creating a windows forms designer would be much easier but we would first have to develop an advanced CodeDOM provider), this is where we must edit the underlying text buffer, based on the actions of the user in the designer. The overall appearence and functionality of our designer is also provided here.

We continue with the creation of a sample control that will be added in the toolbox and its corresponding designer. For each control, we also need to implement a ToolBoxItem that we add to the toolbox when our design view is loaded.

<Designer(GetType(MyControlDesigner), GetType(IDesigner))> _ Public Class MyControl Inherits GroupBox 'Or any other control. 'Supposing this represents an element in a code model. Private m_Element As CodeElement2 ... Public Sub New() MyBase.New() InitializeComponent() End Sub ' Used with existing elements. Friend Sub New(ByVal myElm As CodeElement2) MyClass.New() InitializeComponent() 'If this control represents an existing code element, 'this overloaded constructor allows us to adjust the appearance 'of the control to reflect the underlying code model. End Sub ... ' Code elements created by a MyToolboxItem, can be initialized ' by the designer in CreateToolCore and provided to the control. Friend Sub Initialize(ByVal myElm As CodeElement2) m_Element = myElm ... End Sub ... End Class

 

(Our custom toolbox control)

Public Class MyControlDesigner
    Inherits ParentControlDesigner
...

(Our custom control's designer)

We implement this more or less as our DocumentDesigner. In this example, we use a ParentControlDesigner that allows our control to behave as a parent control, meaning that the user can add other controls as children of this control too. Therefore, this designer too will have similar responsibilities to our DocumentDesigner. Details may be provided in later articles.

Before creating our window pane, we need one or more toolbox items that will be add to the toolbox for our custom controls.

<ToolboxItemFilter("MyEditor.MyDesigner", ToolboxItemFilterType.Require)> _ <Serializable()> _ Public Class MyToolboxItem Inherits Drawing.Design.ToolboxItem Public Sub New() With Me
'We can as well use the same ToolboxItem for
'all our custom controls. In this case we could use
'a parameterized constructor and then intialize the
'toolbox item according to the info passed.
MyBase.Initialize(GetType(MyControl)) .IsTransient = True .Description = "Creates a nice element" .DisplayName = "My Element" End With End Sub ... Public Overrides ReadOnly Property ComponentType() As String Get Return "MyElement" End Get End Property ... End Class

 

(Our toolbox item)

Finally, the last and most important thing before we see our designer for the first time, is to implement our window pane. This is what we will actually provide the environment through the CreateEditorInstance method. Here we do all initialization needed, access any underlying code model, create the design surface, create the toolbox items and populate the toolbox. We also add this class as a service of the design surface. This way all later created controls and designer can easily access the window pane, and coordinate any changes to the underlying code, provide document outline info and accomplish any operation where the top level window pane may be needed.

Lets go, step by step, through the implementation of the window pane...

<CLSCompliant(False)> _ Public Class MyDesignView Inherits WindowPane ... Implements IVsToolboxUser Implements IVsDocOutlineProvider ... ' Text Buffer Private m_Buffer As IVsTextLines ' The project's hierarchy. Private m_Hierarchy As IVsHierarchy ' Item id of our file in the project's hierarchy. Private m_ItemID As UInt32 ' File name. Private m_File As String ' Our code window's window frame. Private m_Frame As IVsWindowFrame ' Caption of the window frame. Private m_Caption As String ' Design surface selection service. Private m_SelService As ISelectionService Private m_SelContainer As MSVSIP.SelectionContainer Private m_TrackSel As ITrackSelection Private m_SelectableList As ArrayList ' OLE service provider. Private m_PaneOLESP As OLEInterop.IServiceProvider ' Design surface Private m_Surface As MyDesignSurface ' Root component. Private m_MyView As MyDocView ' View Code command Private m_ViewCodeCommand As OleMenuCommand ' Design surface control. Private WithEvents m_Ctrl As Control ' Outline window control. Private WithEvents m_Outline As MyOutline ... Private Sub New(ByVal buffer As IVsTextLines, ByVal hier As IVsHierarchy, _
        ByVal itemID As UInt32, ByVal file As String)
MyBase.New(Nothing) m_Buffer = buffer m_Hierarchy = hier m_ItemID = itemID m_File = file ... End Sub ...

 

(Our design view class)

As with tool windows, the first method the environment will call on our WindowPane, will be Initialize(). This is where we have to initialize our design surface.

Protected Overrides Sub Initialize() MyBase.Initialize() Try m_PaneOLESP = CType(Me.GetService(GetType(OLEInterop.IServiceProvider)), _
                          OLEInterop.IServiceProvider) m_Frame = CType(Me.GetService(GetType(IVsWindowFrame)), IVsWindowFrame) ' Create our custom design surface. m_Surface = New MyDesignSurface(Me) If m_Surface.View IsNot Nothing Then ' Obtain and initialize the frame's control. m_Ctrl = CType(m_Surface.View, Control) With m_Ctrl .AllowDrop = True .BackColor = Drawing.Color.White End With ' Obtain the surface's root component. We provided the type of our
' root component, when calling the base constructor of our design surface.
' By now, the design surface has already instanciated an instanve of this class.
Dim host As IDesignerHost = CType(m_Surface.GetService(GetType(IDesignerHost)), _
                                            IDesignerHost) m_MyView = CType(host.RootComponent, MyDocView) ' Set the font. Dim UIService As IUIService = CType(Me.GetService(GetType(IUIService)), IUIService) If UIService IsNot Nothing Then m_MyView.Font = CType(UIService.Styles("DialogFont"), Font) End If ' Add ourselves as a service for this design surface, ' so that controls and designers can access the window. m_Surface.SurfaceServiceContainer.AddService(GetType(MyDesignView), Me) ' We can build or access any existing CodeModel here. ... Me.InitializeToolBox() Me.InitializeCommands() Me.StartMonitoring() Me.UpdateProperties(m_MyView) End If Catch ex As Exception Throw End Try End Sub ... Protected Overrides Sub OnCreate() MyBase.OnCreate() ' We can call IVsTextManager.RegisterIndependentView(Me, m_Buffer) here. ... End Sub ...

(Implementation of the Initialize() method)

Private Sub InitializeToolBox() ' Get the toolbox service Dim toolboxService As IToolboxService = CType( _
                                                m_Surface.GetService(GetType(IToolboxService)), _
                                                IToolboxService) Dim toolbox As IVsToolbox = CType(GetService(GetType(SVsToolbox)), IVsToolbox) ' Create the data object that will store the data for the menu item. Dim toolboxData As OleDataObject ' Create the array of TBXITEMINFO structures to describe the items ' we are adding to the toolbox. Dim itemInfo(0) As TBXITEMINFO ' Our custom ToolboxItem Dim tbxItem As MyToolboxItem tbxItem = New MyToolboxItem() toolboxData = New OleDataObject( _ CType(toolboxService.SerializeToolboxItem(tbxItem), System.Windows.Forms.IDataObject)) itemInfo(0).bstrText = "MyElement" itemInfo(0).clrTransparent = CUInt(ColorTranslator.ToWin32(Color.Magenta)) itemInfo(0).hBmp = ControlPaint.CreateHBitmap16Bit( _
                           My.Resources.Control_GroupBox, Color.Magenta) itemInfo(0).dwFlags = _ CUInt(__TBXITEMINFOFLAGS.TBXIF_DONTPERSIST Or _ __TBXITEMINFOFLAGS.TBXIF_DELETEBITMAP Or _ __TBXITEMINFOFLAGS.TBXIF_CANTREMOVE) ErrorHandler.ThrowOnFailure( _ toolbox.AddItem(CType(toolboxData, OLEInterop.IDataObject), _ itemInfo, "My Elemenents")) ' Repeat the same for any more toolbox items we want to add. ... ' Make sure only the pointer item is initially selected. toolbox.DataUsed() End Sub

 

(Implementation of the internal, InitializeToolBox() method)

 

This actually concludes the first part of this article. Later we shall take a look into the implementation of InitalizeCommands() (that adds commands to be handled by our editor like VSStd97CmdID.ViewCode), StartMonitoring() (that starts tracking the selection context) and UpdateProperties() (that updates the properties window). We will see how the window pane controls the toolbox through IVsToolboxUser and get into the implementation of additional interfaces our window pane can implement (like IVsDocOutlineProvider). 

Using standard design-time features provided by the .NET Framework, we will extend our document designer, to provide advanced design-time functionality.

How to dynamically Import/Export settings in Visual Studio 2005.

Since .vssettings files were first introduced in Visual Studio 2005, I've seen many questions about how the Import/Export settings mechanism can be controlled through code. This was something that bothered me too since VS2005 Beta 1 but it didn't take long till I figure out the solution. I have now created two methods that I use in all VSPackages that I create, that do the job.

The Import/Export mechanism in Visual Studio 2005 can be controlled with the use of a number of interfaces, provided with the Visual Studio SDK. These interfaces are not documented and, for some of them, the usual "This interface supports the VSIP infrastructure and is not intended to be used directly from your code." phrase, is the only info in the documentation.

The interfaces used (in managed code) all come from the Microsoft.VisualStudio.Shell.Interop.8.0.dll assembly and these are:

Microsoft.VisualStudio.Shell.Interop.IVsProfileSettingsTree
Microsoft.VisualStudio.Shell.Interop.IVsSettingsErrorInformation
Microsoft.VisualStudio.Shell.Interop.IVsProfileSettingsFileCollection
Microsoft.VisualStudio.Shell.Interop.IVsProfileSettingsFileInfo

Before proceeding, we must:

i) Be sure that our VSPackage implements at least one of the methods needed to provide VS with the settings we want to import or export. This is either a Microsoft.VisualStudio.Shell.DialogPage class implementing a number of properties, or an implementation of Microsoft.VisualStudio.Shell.IProfileManager. For information on how to create a profile manager by implementing Microsoft.VisualStudio.Shell.IProfileManager in managed code, see How to: Export Settings Using the Managed Package Framework in the Visual Studio SDK documentation. The easiest way however is to add additional properties we wish to save, to an existing Microsoft.VisualStudio.Shell.DialogPage and add a System.ComponentModel.BrowsableAttribute to them, set to False, if we don't want them to appear to the user through the Options window. In any case, a Microsoft.VisualStudio.Shell.ProvideProfileAttribute must be added to our VSPackage class (the class implementing Microsoft.VisualStudio.Shell.Interop.IVsPackage). We must also be sure that the SupportsProfiles property of any Microsoft.VisualStudio.Shell.ProvideOptionPageAttribute attributes added to our VSPackage, is set to True.

<MSVSIP.ProvideOptionPageAttribute(GetType(OptionsPageGeneral), "Amadeus IDEWindowsState", "General", 105, 106, True, SupportsProfiles:=True)> _
<MSVSIP.ProvideProfileAttribute(GetType(OptionsPageGeneral), "Amadeus IDEWindowsState", "IDEWindowsState General Settings", 105, 108, True, GroupName:="Amadeus", GroupResourceID:=104, DescriptionResourceID:=103)> _

ii) Know the path of our settings in the settings file. This is almost the same as the value we use to access our settings through automation. To determine the exact path, we use the info we initially provided to the Microsoft.VisualStudio.Shell.ProvideProfileAttribute. This path is:
[GroupName]\categoryName_objectName
In the example above, the path would be "Amadeus\Amadeus IDEWindowsState_IDEWindowsState General Settings". If we have provided a GroupName (which is a group in the tree of options in the Options window, our options pages are grouped under) as in the example above, we can only provide the GroupName as path, in which case all the settings of our VSPackage will be exported.

I will now provide the code of two methods (in Visual Basic) that can be used to import/export settings through code.

The first method is:

 Function ExportSettings(Optional ByVal externalFile As String = "", Optional ByVal settingsPath As String = "") As Boolean
 externalFile: The name of the file where our settings will be saved. Not a path and without the extension. If not defined, settingsPath is ignored and all VS settings will be exported in the default file (usually currentsettings.vssetings).
 settingsPath: The path to our settings in the settings file. (see above)

   1:  #Region " ExportSettings "
   2:      Friend Function ExportSettings(Optional ByVal externalFile As String = "", Optional ByVal settingsPath As String = "") As Boolean
   3:          Try
   4:              ' The profile manager that provides programmatic control of settings.
   5:              Dim prf As IVsProfileDataManager = CType(m_Package.GetVSService(GetType(SVsProfileDataManager)), _
   6:                                                      IVsProfileDataManager)
   7:   
   8:              ' All the settings in a .vssettings file.
   9:              Dim sets As IVsProfileSettingsTree = Nothing
  10:              ' The specific settings we wish to export.
  11:              Dim wl As IVsProfileSettingsTree = Nothing
  12:              ' Interface used to retrieve progress status.
  13:              Dim ret As IVsSettingsErrorInformation = Nothing
  14:              ' Variable that holds the extension of .vssettings files.
  15:              Dim ext As String = ""
  16:              ' The path to the default directory that contains the files.
  17:              Dim path As String = ""
  18:              ' The name of the file were settings will be exported.
  19:              Dim file As String = ""
  20:   
  21:              If prf IsNot Nothing Then
  22:                  m_Package.ShowFeedback("Exporting Settings...")
  23:   
  24:                  With prf
  25:                      ' Get the path to the default directory that contains the settings files.
  26:                      ' This is the one defined by the user in Tools->Options->Environment->
  27:                      ' General->Import and Export Settings.
  28:                      .GetDefaultSettingsLocation(path)
  29:                      ' Get the standard extension of the settings file.
  30:                      .GetSettingsFileExtension(ext)
  31:                      ' Get all the settings that can be exported at this moment.
  32:                      ' This will call all properties in any DialogPage classes of the
  33:                      ' VSPackage.
  34:                      .GetSettingsForExport(sets)
  35:   
  36:                      ' If the caller has provided a filename, we use this.
  37:                      ' Otherwise we use currentsettings which is the default file
  38:                      ' VS uses to store settings.
  39:                      If externalFile <> "" Then
  40:                          If Not PackageUtilities.ContainsInvalidFileNameChars(externalFile) Then
  41:                              file = externalFile
  42:                          Else
  43:                              Throw New ArgumentException("Invalid file name specified.", _
  44:                                                          "externalFile")
  45:                          End If
  46:   
  47:                          ' If the caller only wishes to export specific settings
  48:                          ' (like the settings of this VSPackage), we need to disable
  49:                          ' exporting of any other settings and enable only the settings
  50:                          ' we want to export. Otherwise, all VS settings will be exported.
  51:                          If settingsPath <> "" Then
  52:                              ' First disable all settings.
  53:                              sets.SetEnabled(0, 1)
  54:                              ' Find the specified child tree of settings.
  55:                              ' (The settings file has XML structure.)
  56:                              sets.FindChildTree(settingsPath, wl)
  57:                              If wl IsNot Nothing Then
  58:                                  ' If the child tree is found, enable it.
  59:                                  wl.SetEnabled(1, 1)
  60:                              Else
  61:                                  ' The specified settings path, does not exist in the
  62:                                  ' collection of settings retrieved from all VSPackages.
  63:                                  Throw New ArgumentException("Settings path does not exist.", _
  64:                                                              "settingsPath")
  65:                              End If
  66:                          End If
  67:                      Else
  68:                          ' The caller has not specified a specific filename.
  69:                          ' We will export all settings to the default file. We will
  70:                          ' not use this file for specific settings. VS uses this file
  71:                          ' to store all settings.
  72:                          file = "currentsettings"
  73:                      End If
  74:   
  75:                      ' Export settings. Success/Fail info can be retrieved
  76:                      ' from the IVsSettingsErrorInformation interface we provide
  77:                      ' in the ppsettingsErrorInformation argument. But the call
  78:                      ' will also return an error code we can use to determine errors.
  79:                      Dim retCode As Int32 = .ExportSettings((path & file & ext), sets, ret)
  80:                      ' Now we can check success.
  81:                      Dim ex As Exception = Marshal.GetExceptionForHR(retCode)
  82:   
  83:                      ' You can respond to possible failure, anyway you want.
  84:                      If ex IsNot Nothing Then
  85:                          My.TraceInfo.Write(CStr(ex.Message))
  86:                          m_Package.ShowFeedback("Exporting Settings..." & ex.Message)
  87:                          Return False
  88:                      Else
  89:                          m_Package.ShowFeedback("Exporting Settings...Complete.")
  90:                          Return True
  91:                      End If
  92:                  End With
  93:              Else
  94:                  ' The profile manager was not obtained.
  95:                  Return False
  96:              End If
  97:          Catch exM As Exception
  98:              ' Any other exceptions?
  99:              m_Package.ShowFeedback("Exporting Settings..." & exM.Message)
 100:              My.TraceInfo.Write(CStr(exM.Message))
 101:              Return False
 102:          End Try
 103:      End Function
 104:  #End Region

The second method is:

 Function ImportSettings(Optional ByVal externalFile As String = "", Optional ByVal settingsPath As String = "") As Boolean
 externalFile: The name of the file where our settings will be saved. Not a path and without the extension. If not defined, our settings will be retrieved from the default file VS uses to store settings (usually currentsettings.vssetings).
 settingsPath: The path to our settings in the settings file. (see above)

   1:  #Region " ImportSettings "
   2:      Friend Function ImportSettings(Optional ByVal externalFile As String = "", Optional ByVal settingsPath As String = "") As Boolean
   3:          Try
   4:              ' The profile manager that provides programmatic control of settings.
   5:              Dim prf As IVsProfileDataManager = CType(m_Package.GetVSService(GetType(SVsProfileDataManager)), _
   6:                                                      IVsProfileDataManager)
   7:   
   8:              ' All the settings in a .vssettings file.
   9:              Dim sets As IVsProfileSettingsTree = Nothing
  10:              ' The specific settings we wish to import.
  11:              Dim wl As IVsProfileSettingsTree = Nothing
  12:              ' Interface used to retrieve progress status.
  13:              Dim ret As IVsSettingsErrorInformation = Nothing
  14:              ' The collection of .vssettings files in a directory.
  15:              Dim col As IVsProfileSettingsFileCollection = Nothing
  16:              ' Info of a .vssettings file.
  17:              Dim prfInfo As IVsProfileSettingsFileInfo = Nothing
  18:              ' Variable that holds the extension of settings files.
  19:              Dim ext As String = ""
  20:              ' The number of .vssettings files in directory.
  21:              Dim setFiles As Int32
  22:              ' The path of the .vssettings file that contains the settings we wish to import.
  23:              Dim setFile As String = ""
  24:              ' The name of the file that contains the settings we wish to import.
  25:              Dim file As String = ""
  26:   
  27:              If prf IsNot Nothing Then
  28:                  ' If the caller has provided a filename, we use this.
  29:                  ' Otherwise we use currentsettings which is the default file
  30:                  ' VS uses to store settings.
  31:                  If externalFile <> "" Then
  32:                      file = externalFile
  33:                  Else
  34:                      file = "currentsettings"
  35:                  End If
  36:   
  37:                  ' Get the standard extension of the settings file.
  38:                  prf.GetSettingsFileExtension(ext)
  39:                  ' Get the collection of settings files in the directory defined by the 
  40:                  ' user in Tools->Options->Environment->General->Import and Export Settings.
  41:                  prf.GetSettingsFiles(CUInt(ShellInterop.__VSPROFILELOCATIONS.PFL_SettingsDir), col)
  42:                  ' Get the number of files in the collection.
  43:                  col.GetCount(setFiles)
  44:   
  45:                  ' Enumerate the collection to find the requested file.
  46:                  For ii As Int32 = 0 To setFiles
  47:                      col.GetSettingsFile(ii, prfInfo)
  48:   
  49:                      If prfInfo IsNot Nothing Then
  50:                          prfInfo.GetFilePath(setFile)
  51:                      End If
  52:   
  53:                      If setFile.Contains(file & ext) Then
  54:                          Exit For
  55:                      End If
  56:                  Next
  57:   
  58:                  ' If the file is found, we can proceed.
  59:                  If prfInfo IsNot Nothing Then
  60:                      Dim retCode As Int32
  61:   
  62:                      ' Get all the settings available in the file.
  63:                      prfInfo.GetSettingsForImport(sets)
  64:   
  65:                      ' If the caller does not request for specific settings,
  66:                      ' proceed with importing all the settings in the file.
  67:                      ' Otherwise we will search for the specified child tree of
  68:                      ' settings in the file. (The settings file has XML structure.)
  69:                      If settingsPath = "" Then
  70:                          ' Import settings. Success/Fail info can be retrieved
  71:                          ' from the IVsSettingsErrorInformation interface we provide
  72:                          ' in the ppsettingsErrorInformation argument. But the call
  73:                          ' will also return an error code we can use to determine errors.
  74:                          retCode = prf.ImportSettings(sets, ret)
  75:                      Else
  76:                          ' Find the specified child tree.
  77:                          sets.FindChildTree(settingsPath, wl)
  78:   
  79:                          If wl IsNot Nothing Then
  80:                              ' Import settings. Success/Fail info can be retrieved
  81:                              ' from the IVsSettingsErrorInformation interface we provide
  82:                              ' in the ppsettingsErrorInformation argument. But the call
  83:                              ' will also return an error code we can use to determine errors.
  84:                              retCode = prf.ImportSettings(wl, ret)
  85:                          Else
  86:                              ' The specified settings path, does not exist in this file.
  87:                              Throw New ArgumentException("Settings path does not exist.", _
  88:                                                          "settingsPath")
  89:                          End If
  90:                      End If
  91:   
  92:                      ' Now we can check success.
  93:                      Dim ex As Exception = Marshal.GetExceptionForHR(retCode)
  94:   
  95:                      ' You can respond to possible failure, anyway you want.
  96:                      If ex IsNot Nothing Then
  97:                          My.TraceInfo.Write(CStr(ex.Message))
  98:                          Return False
  99:                      Else
 100:                          Return True
 101:                      End If
 102:                  Else
 103:                      ' The specified settings file could not be found in the settings directory.
 104:                      Throw New ArgumentException("Invalid file name specified.", _
 105:                                                  "externalFile")
 106:                  End If
 107:              Else
 108:                  ' The profile manager was not obtained.
 109:                  Return False
 110:              End If
 111:          Catch exM As Exception
 112:              ' Any other exceptions?
 113:              My.TraceInfo.Write(CStr(exM.Message))
 114:              Return False
 115:          End Try
 116:      End Function
 117:  #End Region


OK, now these will do the job, as long as the settings have been properly implemented in our VSPackage. These may also be changed to accommodate your needs. Some methods seen in these example ( like My.TraceInfo.Write() or m_Package.ShowFeedback() ) are only used for simplicity. They are implemented by the VSPackage implementer and cannot be found in the interop assemblies provided with Visual Studio SDK.

Hallo

Hallo to everybody in the BloggingAbout.NET community!

I was planning for longer welcome post but my urgent need to start posting technical things didn't let me any time. I just hope I'll have the chance to talk to eveybody in here. I know it's a great place; it's helped in the past so, it's time I put down some of my own work, and share it.

Perikles C. Stephanidis