Tabs Studio Blog (organizing Visual Studio document tabs)

August 27, 2009

Saver add-in

Filed under: Uncategorized — Tags: — Sergey Vlasov @ 6:37 am

Saver restores tab positions when opening a solution to the last state. Let’s start from the design questions.

When to save tab positions?

Visual Studio has the OnBeforeCloseSolution event in the IVsSolutionEvents interface. When OnBeforeCloseSolution fires all tabs are still opened and this event seems like a perfect place to save tab positions.

When to restore tab positions?

There is the OnAfterOpenSolution event, but when it fires no solution files are opened yet. So, I did use this event to load tab positions, but implemented tab ordering in response to the ITabsStudioEngine.TabCreated events tab by tab.

Where to store tab positions?

Ideal place to store tab positions would be Solution User Options (.suo) file. Unfortunately, this storage is available only to VSPackages. Instead, I decided to store tab positions in a separate file with the tss extension in a directory along with the sln and suo files.

Implementation

In the OnConnection method Saver gets the IVsSolution Visual Studio interface and subscribes to solution events.

In the OnBeforeCloseSolution method file path of each tab extension (or title for non document windows) in appropriate order are stored in a tss file. Information is stored in human readable xml format.

In the OnAfterOpenSolution method tab positions are loaded from a tss file and stored in the tabsOrderList instance variable.

OnTabCreated event handler gets file path of created tab extension (or title for non document windows) as the tabCreatedID string, finds appropriate tab group in tabsOrderList, scans TabPanel from the beginning looking for a tab that is greater (based on saved order index) than the just created one and if greater tab is found moves the just created tab before it.

OnTabCreated processing should work only while solution is opening. UpdateTabsToGo method is called after loading tab positions from a tss file and calculates the number of tabs that are going to be opened. OnTabCreated event handler each time checks that there are tabsToGo and decrements this counter, thus preventing OnTabCreated to work in Saver add-in after solution is completely loaded.

Download Saver v1.0.0 for Tabs Studio v1.5.5.

Navigator add-in

Filed under: Uncategorized — Tags: — Sergey Vlasov @ 4:18 am

Navigator allows you to switch tabs using keyboard. It provides two new Visual Studio commands to activate tabs on the left and on the right of the currently selected one.

Navigator is a good example of Visual Studio commands creation in Tabs Studio’s add-in. Main Navigator class implements the EnvDTE.IDTCommandTarget interface. Commands class registers two new commands, reports their status as always enabled and checks when it is time to execute them.

Actual navigation is implemented in CommandsImpl class. Uisng the ITabsStudioEngine.GetTabs method it gets information about all tabs, determines selected tab group, determines selected tab, finds adjacent tab in TabPanel, finds active extension in adjacent tab and activates corresponding Visual Studio window using Activate method.

Assigning keyboard shortcuts to these commands in Visual Studio keyboard options I noticed that assigning, for example, Ctrl+2 in Global scope to TabsStudio.Connect.NavigateToNextTab works, but to assign Ctrl+Shift+Right Arrow it is needed to be in Text Editor scope or default Text Editor command overrides it.

Download Navigator v1.0.0 for Tabs Studio v1.5.5.

Tracer add-in

Filed under: Uncategorized — Tags: — Sergey Vlasov @ 3:41 am

Tracer should be useful for add-ins development. Using System.Diagnostics.Trace.WriteLine method it traces when
ITabsStudioEngine events occur. I use DebugView to view these traces.

Tracer also demonstrates use of the TabsStudioExt.IConfigurable interface showing settings dialog when Configure… selected in Add-in Manager. These settings are not preserved after closing Visual Studio.

Download Tracer v1.0.0 for Tabs Studio v1.5.5.

August 26, 2009

Sorter add-in v1.0.1

Filed under: Uncategorized — Tags: — Sergey Vlasov @ 8:17 am

Sorter v1.0.1 for Tabs Studio v1.5.5 is released – added support for tab renaming, added support for latest Tabs Studio SDK.

Tabs Studio v1.5.5 is released

Filed under: Releases — Sergey Vlasov @ 8:13 am

Tabs Studio v1.5.5 is released – added add-in manager, added ability to customize file modification status, extended extensibility interfaces, fixed Tabs Studio dialogs centering when Visual Studio is maximized.

If you have Sorter add-in v1.0.0, uninstall it or upgrade to Sorter v1.0.1.

Customizing file modification status

Filed under: Uncategorized — Tags: — Sergey Vlasov @ 5:42 am

Traditionally Tabs Studio used asterisk as file modification marker. I’ve added additional controls for tab and tab extension modification markers to allow their XAML styling:

TabNameModificationMarker Label control was added before TabNameReadOnlyImage with the following default style:

<Style x:Key="DefaultTabNameModificationMarkerStyle" TargetType="TabsStudio:TabNameModificationMarker">
    <Setter Property="Margin" Value="0,0,0,0"/>
    <Setter Property="Padding" Value="0,0,0,0"/>
    <Setter Property="Content" Value="*"/>
    <Setter Property="Visibility" Value="Collapsed"/>
    <Style.Triggers>
        <Trigger Property="IsNameModified" Value="True">
            <Setter Property="Visibility" Value="Visible"/>
        </Trigger>
    </Style.Triggers>
</Style>

TabExtensionModificationMarker Label control was added before TabExtensionReadOnlyImage with the following default style:

<Style x:Key="DefaultTabExtensionModificationMarkerStyle" TargetType="TabsStudio:TabExtensionModificationMarker">
    <Setter Property="Margin" Value="0,0,0,0"/>
    <Setter Property="Padding" Value="0,0,0,0"/>
    <Setter Property="Content" Value="*"/>
    <Setter Property="Visibility" Value="Collapsed"/>
    <Style.Triggers>
        <Trigger Property="IsExtensionModified" Value="True">
            <Setter Property="Visibility" Value="Visible"/>
        </Trigger>
    </Style.Triggers>
</Style>

Default asterisk file modification status

Default asterisk file modification status


Let’s see some new styling examples that are possible now.

By default, modified tab width is greater than non modified one due to additional asterisk. It may inconveniently cause tabs to change rows when editing and saving files. To reserve space for the asterisk when tab extension is not modified use the following style (same approach can be used for tab name):

<Style TargetType="TabsStudio:TabExtensionModificationMarker" 
    BasedOn="{StaticResource DefaultTabExtensionModificationMarkerStyle}">
    <Setter Property="Visibility" Value="Hidden"/>
</Style>

Space reserved for file modification status

Space reserved for file modification status


A custom string (e.g. exclamation mark) can be used instead of asterisk as the file modification marker for tab extension (same approach can be used for tab name):

<Style TargetType="TabsStudio:TabExtensionModificationMarker" 
    BasedOn="{StaticResource DefaultTabExtensionModificationMarkerStyle}">
    <Setter Property="Content" Value="!"/>
</Style>

Exclamation mark as file modification marker

Exclamation mark as file modification marker


Instead of showing additional text marker, appearance of file name can be customized (e.g. setting red color for modified tab text):

<Style TargetType="TabsStudio:TabExtensionModificationMarker" 
    BasedOn="{StaticResource DefaultTabExtensionModificationMarkerStyle}">
    <Style.Triggers>
        <Trigger Property="IsExtensionModified" Value="True">
            <Setter Property="Visibility" Value="Collapsed"/>
        </Trigger>
    </Style.Triggers>
</Style>
<Style TargetType="TabsStudio:TabExtension" 
    BasedOn="{StaticResource DefaultTabExtensionStyle}">
    <Style.Triggers>
        <Trigger Property="IsExtensionModified" Value="True">
            <Setter Property="Foreground" Value="Red"/>
        </Trigger>
    </Style.Triggers>
</Style>

<Style TargetType="TabsStudio:TabNameModificationMarker" 
    BasedOn="{StaticResource DefaultTabNameModificationMarkerStyle}">
    <Style.Triggers>
        <Trigger Property="IsNameModified" Value="True">
            <Setter Property="Visibility" Value="Collapsed"/>
        </Trigger>
    </Style.Triggers>
</Style>
<Style TargetType="TabsStudio:TabName" 
    BasedOn="{StaticResource DefaultTabNameStyle}">
    <Style.Triggers>
        <Trigger Property="IsNameModified" Value="True">
            <Setter Property="Foreground" Value="Red"/>
        </Trigger>
    </Style.Triggers>
</Style>
Red text color as file modification status

Red text color as file modification status

August 25, 2009

Add-in Manager

Filed under: Uncategorized — Tags: — Sergey Vlasov @ 6:57 am

I’ve created the Add-in Manager dialog that you can open using the Tabs Studio context menu or TabsStudio.Connect.Addins command:

Tabs Studio Add-in Manager dialog

Tabs Studio Add-in Manager dialog


The dialog shows all loaded add-ins. Add-in information is extracted from the version resource in the add-in dll file. Add-in Name is extracted from the ProductName key, in C# project it is set using the AssemblyProduct attribute. Description is extracted from the FileDescription key defined by the AssemblyTitle attribute. Version is extracted from the ProductVersion key defined by the AssemblyFileVersion attribute.

To support configurability the add-in’s main class must implement the TabsStudioExt.IConfigurable interface:

public class Tracer : TabsStudioExt.ITabsStudioAddin, TabsStudioExt.IConfigurable

This interface has only one method:

void Configure(System.Windows.Window parent);

The parent parameter is to set the Owner property of the add-in configuration dialog:

public void Configure(System.Windows.Window parent)
{
    SettingsDialog dialog = new SettingsDialog(settings);
    dialog.Owner = parent;
    dialog.ShowDialog();
}

August 24, 2009

More tab events

Filed under: Uncategorized — Tags: — Sergey Vlasov @ 3:25 am

I’ve added more events to the ITabsStudioEngine interface:

TabExtensionCreated, TabExtensionDestroyed and TabExtensionRenamed events with TabExtensionEventArgs argument containing TabExtension, Tab and Tabs properties.

TabCreated, TabDestroyed and TabRenamed events with TabEventArgs argument containing Tab and Tabs properties.

TabsCreated and TabsDestroyed events with TabsEventArgs argument containing Tabs property.

Argument types are corresponding TabsStudioExt classes, not plain WPF controls as before.

August 22, 2009

Visual Studio commands support for add-ins

Filed under: Uncategorized — Tags: — Sergey Vlasov @ 3:17 pm

In upcoming SDK, an add-in for Tabs Studio can add Visual Studio commands that are accessible via menu, toolbars and keyboard shortcuts in Visual Studio IDE.

To add new command get EnvDTE80.Commands2 object and call AddNamedCommand2 function:

EnvDTE80.Commands2 commands = dte.Commands as EnvDTE80.Commands2;
commands.AddNamedCommand2(addin, "NavigateToNextTab", ...

AddNamedCommand2 function requires EnvDTE.AddIn parameter that you can get from the new ITabsStudioEngine.GetAddIn function. During testing you may want to delete your experimental command from Visual Studio:

commands.Item("TabsStudio.Connect.NavigateToNextTab", 0).Delete();

Note that you add new command with a short name (“NavigateToNextTab”), but delete, execute and set command status using the full name (“TabsStudio.Connect.NavigateToNextTab”).

To execute command and set command availability status your main add-in class must implement the EnvDTE.IDTCommandTarget interface (Exec and QueryStatus methods):

public class Navigator : TabsStudioExt.ITabsStudioAddin, EnvDTE.IDTCommandTarget

You will find complete code sample utilizing commands in my next Navigator add-in.

Internal tab order

Filed under: Uncategorized — Tags: — Sergey Vlasov @ 8:43 am

Tab order in Tabs.TabPanel and Tabs.TabList can be different. By default a new tab is added to the end of both TabPanel and TabList. After that tabs can be reordered in TabPanel, manually using drag’n’drop or automatically using Sorter like add-in. Order in TabList remains constant. If it is important to handle tabs the way a user sees it then process them in TabPanel order.

To find TabItem in TabPanel having TabsStudioExt.Tab just use the Tab.TabItem property. To find TabsStudioExt.Tab having TabItem in TabPanel use function like this:

private TabsStudioExt.Tab FindTabByItem(System.Windows.Controls.TabItem tabItem, TabsStudioExt.Tabs tabs)
{
    foreach(TabsStudioExt.Tab tab in tabs.TabList)
    {
        if (tab.TabItem == tabItem)
            return tab;
    }
    return null;
}

You can sort TabList according to TabPanel ordering and then work with TabList collection having sure tab order is in the way a user sees it:

tabList.Sort(
    delegate(TabsStudioExt.Tab lhs, TabsStudioExt.Tab rhs)
    {
        return tabs.TabPanel.Children.IndexOf(lhs.TabItem).CompareTo(
                    tabs.TabPanel.Children.IndexOf(rhs.TabItem));
    });
Older Posts »

Blog at WordPress.com.