Стартовая страница в Visual Studio 2010

Пользовательский интерфейс Visual Studio 2010 построен на основе технологии Windows Presentation Foundation. Кроме эмоций (положительных и отрицательных) у этого явления есть еще и рациональное зерно – возможности для расширения различных частей Visual Studio. Поскольку в WPF пользовательский интерфейс описывается на основе языка XAML, то это дает возможность модифицировать его более просто. Одним из наиболее ярких примеров такого расширения функциональности – стартовые страницы Visual Studio 2010.

Стартовые страницы — это то, что мы видим сразу же после запуска Visual Studio. На них обычно собирается полезная для разработчика информация, такая как ссылки на ресурсы для разработчиков, новости, недавние проекты и др. Если взглянуть на то, как устроены стартовые страницы в Visual Studio 2010, то можно обнаружить, что это – не более, чем файл с разметкой XAML.

Изменение содержимого стартовой страницы Visual Studio 2010 может быть полезным в самых различных ситуациях. Например, здесь можно подключить ленту корпоративного блога, для того, чтобы оперативно узнавать новости, происходящие внутри команды разработчиков. На самом деле сценарии здесь ограничиваются только фантазией разработчиков. Давайте рассмотрим процесс создания и модификации стартовых страниц.

Если вы устанавливали Visual Studio 2010 не изменяя стандартного пути, то содержимое стартовых страниц будет расположено в папке C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\StartPages\. В предварительных версиях Visual Studio здесь находились XAML-файлы, которые поддавались модификации. В финальной сборке Visual Studio 2010 здесь находятся файлы, описывающие содержимое страниц, однако, это – не файлы XAML. Вообще говоря, не стоит изменять содержимое этих файлов, можно пойти более цивилизованным путем – использовать стандартные инструменты для модификации стартовых страниц.

Для создания стартовых страниц уже существует стандартный шаблон в Visual Studio Galery - Custom Start Page Project Template. При установке его в среду Visual Studio появляется новый тип проекта в разделе ExtensibilityCustom Start Page.

Преимуществом этого шаблона является то, что при создании проекта создаются шаблоны для встраивания, элементы управления WPF, а также шаблон VSIX, который позволяет быстро собрать модуль расширения для Visual Studio когда все будет готово.

Для отладки стартовой страницы можно просто запустить проект (F5). При этом запустится новый экземпляр Visual Studio 2010, который будет служить средой для просмотра результатов работы. Однако, по умолчанию новая стартовая страница отображаться не будет, для того, чтобы она отобразилась необходимо задать соответствующие параметры в Visual Studio. Для этих целей существует настройка ToolsOptionsEnvironmentStartupCustomize Start Page. Здесь необходимо указать разрабатываемую страницу.

Теперь у нас готов проект для создания собственного представления стартовой страницы Visual Studio 2010.

Структура проекта

Расширение состоит из двух проектов — главного проекта, в котором содержаться все необходимые элементы для установки страницы и вспомогательного проекта, содержащего элемент управления WPF.

Главный проект содержит файл описания стартовой страницы StartPage.xaml, на которой размещается все содержимое стартовой страницы. По умолчанию на этой странице содержатся все стандартные вкладки, присутствующие в обычной стартовой странице Visual Studio 2010, и дополнительная вкладка с вашим содержимым. В дополнительной вкладке содержится элемент управления из вспомогательного проекта.

Также главный проект содержит манифест для создания пакета VSIX – файлы расширения Visual Studio 2010, которые можно установить в несколько нажатий мышью. Таким образом, при сборке приложения в папке с результатом сразу будет находится готовый VSIX-пакет, который можно устанавливать на другие рабочие места.

Создание собственного содержимого в стартовой странице

Теперь, когда мы разобрались со структурой проекта, давайте перейдем к формированию содержимого стартовой страницы.

Для управления содержимым стартовой страницы существует два варианта – изменение страницы StartPage.xaml в основном проекте и изменение элемента управления MyControl.xaml во вспомогательном проекте.

Если мы воспользуемся первым способом, то у нас будет больше возможностей по изменению внешнего вида страницы. Например, мы можем добавить логотип своей компании на стартовую страницу, изменить положение элементов Создать проект, Открыть проект или списка проектов, отрываемых недавно. Сценариев, которые мы можем выполнять, изменяя содержимое этого файла – огромное количество. Общая структура этого файла выглядит следующим образом.

<Grid xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:sp="clr-namespace:Microsoft.VisualStudio.PlatformUI;assembly=Microsoft.VisualStudio.Shell.StartPage"
      xmlns:vs="clr-namespace:Microsoft.VisualStudio.PlatformUI;assembly=Microsoft.VisualStudio.Shell.10.0"
      xmlns:vsfx="clr-namespace:Microsoft.VisualStudio.Shell;assembly=Microsoft.VisualStudio.Shell.10.0"
      xmlns:my="clr-namespace:MyStartPageControl;assembly=MyStartPageControl"
      mc:Ignorable="d" 
      xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" d:DesignHeight="600" d:DesignWidth="800">

    <Grid.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="/Microsoft.VisualStudio.Shell.StartPage;component/Styles/startpageresources.xaml" />
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Grid.Resources>

    <Grid x:Name="LayoutRoot"
          Background="{DynamicResource {x:Static vsfx:VsBrushes.StartPageBackgroundKey}}"
          Style="{DynamicResource StartPage.OuterGridStyle}">

        <Grid.ColumnDefinitions>
            <!-- Left Hand Column -->
            <!-- This column is just wide enough to fit the Command Buttons and Start Page Options.
                 The Recent Project area is constrained to the width of the Commmand Buttons. -->
            <ColumnDefinition MinWidth="{Binding ElementName=commandButtonsGrid, Path=ActualWidth}"
                              Width="3*" MaxWidth="330" />
            <!-- Gutter -->
            <ColumnDefinition Width="6"/>
            <!-- Allow main column to collapse to 0 -->
            <ColumnDefinition Width="7*" MinWidth="0"/>
        </Grid.ColumnDefinitions>

        <Grid.RowDefinitions>
            <!-- Logo-->
            <RowDefinition Height="130" />
            <!-- Main Content Area -->
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>

        <!--Logo Image -->
        <!--Note: To reference files relative to this XAML file, use syntax like the following: <Image Source="{sp:StartPageRelative myImage.png}" .../> -->
        <Image Grid.Column="0" Grid.Row="0" Grid.ColumnSpan="3"
               Source="{Binding Background.Logo}"
               HorizontalAlignment="Left"
               VerticalAlignment="Top" Width="1600" />

        <!--Left Column-->
        <Grid Width="Auto" Grid.Column="0" Grid.Row="1"
              Margin="15,-35,0,15" VerticalAlignment="Stretch">
            <Grid.RowDefinitions>
                <!-- Command Buttons for New Projects -->
                <RowDefinition Height="Auto"/>
                <!-- MRU Row -->
                <RowDefinition Height="*"/>
                <!--Gutter at bottom of page -->
                <RowDefinition Height="Auto"/>
            </Grid.RowDefinitions>

            <!-- Command Buttons -->
            <Grid x:Name="commandButtonsGrid" MinWidth="270"
                  Grid.Row="0" Margin="0,15,0,30" HorizontalAlignment="Left">
                <Grid.RowDefinitions>
                    <RowDefinition Height="Auto"/>
                    <RowDefinition Height="3"/>
                    <RowDefinition Height="Auto"/>
                    <RowDefinition Height="3"/>
                    <RowDefinition Height="Auto"/>
                </Grid.RowDefinitions>
                <!-- ... -->

            </Grid>
            
           
            <!-- Recent Projects Section -->
            <Grid Grid.Row="1" HorizontalAlignment="Left" Width="Auto">
                <Grid.RowDefinitions>
                    <!-- Heading -->
                    <RowDefinition Height="Auto" />
                    <!-- MRU Container -->
                    <RowDefinition Height="*" />
                </Grid.RowDefinitions>

                <!-- Recent Projects Heading-->
                <StackPanel Grid.Row="0" Margin="0,0,0,10" Orientation="Horizontal">
                    <TextBlock Text="Recent Projects" VerticalAlignment="Top"
                               Style="{DynamicResource StartPage.HeadingTextStyle}"
                               x:Uid="RecentProjects"/>
                    <Path VerticalAlignment="Center" Margin="6,0,0,-4" Width="Auto"
                          Height="1" Stretch="Fill" StrokeThickness="1" StrokeLineJoin="Round"
                          Stroke="{DynamicResource {x:Static vsfx:VsBrushes.StartPageSeparatorKey}}"
                          Data="F1 M 0.5,0.5L 199.5,0.5"/>
                </StackPanel>
                <!-- MRU List Container -->

                <!-- ... -->

            </Grid>

            <!-- Start Page Options -->
            <StackPanel Margin="0,5,0,0" Grid.Column="0" Grid.Row="2">
                <CheckBox x:Uid="AutoClose_Option" Content="Close page after project load"
                          IsChecked="{Binding ClosePageOnOpenProject, Mode=TwoWay}"
                          Margin="0,0,2,0"/>
                <CheckBox x:Uid="ShowOnStartUp_Option" Content="Show page on startup"
                          IsChecked="{Binding ShowPageAtStartup, Mode=TwoWay}"
                          Margin="0,2,0,0"/>
            </StackPanel>
        </Grid>

        <!--Center Content-->
        <Grid Grid.Column="2" Grid.Row="1" Margin="0,-35,15,15">
            <TabControl Style="{DynamicResource StartPage.TabControlStyle}"
                        SelectedIndex="{Binding SelectedTabItemIndex, Mode=TwoWay}">

            <!-- ... -->
                
            </TabControl>
        </Grid>
    </Grid>
</Grid>

Как видно, при описании страницы используются ряд стандартных пространств имен. Они необходимы для получения информации для отображения, а также для выполнения команд. По этой разметке нетрудно догадаться каким образом определяется содержимое страницы. Видно также, что для основного содержимого используется элемент управления TabControl, который содержит ряд стандартных вкладок.

Если нам не требуется радикально изменять внешний вид стартовой страницы, то рекомендуется создавать набор необходимых вкладок, которые будут отображать нужную информацию. Как раз здесь и задается дополнительная вкладка для нашего содержимого. Эта вкладка содержит ссылку ну элемент управления, определенный в дополнительной сборке.

<TabControl Style="{DynamicResource StartPage.TabControlStyle}"
            SelectedIndex="{Binding SelectedTabItemIndex, Mode=TwoWay}">

    <!-- ... -->

    <TabItem Header="MyControl" Style="{DynamicResource StartPage.TabItemStyle}">
        <my:MyControl/>
    </TabItem>
</TabControl>

В нашем случае давайте воспользуемся этим подходом и не будем модифицировать основной файл страницы. Вместо этого добавим свою функциональность во внешний элемент управления.

Давайте создадим специальную вкладку, на которой будем видеть все наши задачи, которые находятся у нас в данный момент в Outlook 2010. Для этого в элемент управления MyControl добавим элемент ListBox, который и будет содержать список задач из Outlook.

<ListBox x:Name="Tasks"
         Foreground="{DynamicResource {x:Static vsfx:VsBrushes.StartPageTextBodyKey}}"
         Background="Transparent">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <TextBlock Text="{Binding }" Margin="20" FontSize="16" />
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

Теперь определим обработчик загрузки элемента управления, в котором будет содержаться логика получения элементов из Outlook. Для получения данных воспользуемся интерфейсами COM, для чего сделаем ссылку на Microsoft.Office.Interop.Outlook. Код для получения информации выглядит очень просто – в нем происходит обращение к Outlook и отображение полученных данных в созданном ранее элементе управления ListBox.

private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
    var app = new Outlook.Application();
    var folder = app.Session.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderTasks);
    Tasks.ItemsSource = folder.Items.OfType<Outlook.TaskItem>().Select(t => t.Subject);
}

Теперь запустим проект и увидим, что появилась новая вкладка, в которой содержаться все актуальные задачи.

В данном случае расширение получилось очень простым. Однако, аналогичным образом доработать его до требуемого результата достаточно просто.

Таким образом, мы без особых усилий изменили вид стартовой страницы Visual Studio 2010 разместив на ней нужное нам содержимое.