Как сделать Xamarin Studio чуточку лучше?

от автора

image

Итак, прошло уже полтора года с тех пор как я начал разрабатывать мобильные приложения с помощью Xamarin и C#. За это время ребята из Xamarin основательно поработали над своей IDE, так что от связки iMac-Parallels Desktop-Visual Studio-Android я с радостью отказался в пользу iMac-Xamarin-Genymotion. Однако, Xamarin Studio все еще находится на том уровне, когда некоторые действия приходится выполнять вручную, но что делать, если это приходится совершать 5, 10, 15 и более раз за день? Ответ простой – проапгрейдить Xamarin Studio, написав Add-in, который будет делать всю работу за тебя. В этой статье я расскажу как создать простой Add-in и куда двигаться, если нужно что-то посерьезнее.

Подготовка

Для создания Add-in’а потребуется:

  • Xamarin Studio;
  • Addin maker.

Список не длинный. Addin maker устанавливается следующим образом:

  1. Открываем Xamarin Studio и заходим в менеджер дополнений

    image

  2. В менеджере дополнений выбираем вкладку Gallery и вбиваем в поисковую строку Addin maker. Как только менеджер найдет Add-in, устанавливаем его

    image

Если вдруг менеджер дополнений такой Add-in не нашел, берем его здесь.
После установки Addin Maker перезапускаем Xamarin Studio. Теперь все готово для того чтобы начать.

Цель создаваемого Add-in

Основная и единственная цель Add-in, о создании которого я расскажу, будет заключаться в удалении bin и obj папок в основной директории проекта нажатием на одну кнопку. Работая с Xamarin Studio на Mac, такую операцию порой приходится выполнять по несколько раз в час, вне зависимости от того под Android или под iOS дебажится проект.

Cоздание проекта

Для создания проекта в Xamarin Studio выбираем New Solution > C# > Xamarin Studio > Xamarin Studio Addin, вводим имя и выбираем расположение:

image

После создания проекта его структура должна выглядеть следующим образом:

image

AddinInfo и AssemblyInfo, как ясно из названия, несут в себе информацию об Add-in и сборке соответственно. В Manifest.addin же как раз описывается модель создаваемого расширения. Поэтому открываем его и прописываем следующие строки:

<?xml version="1.0" encoding="UTF-8"?> 
<ExtensionModel> 
    <Extension path = "/MonoDevelop/Ide/Commands">
         <Command id = "BinObjCleaner.BinObjCleanerCommands.DeleteBinObj"
              _label = "Delete bin/obj"
              _description = "Delete /bin and /obj folders in every solution project"  
             defaultHandler = "BinObjCleaner.DeleteBinObjHandler" /> 
    </Extension>

      <Extension path = "/MonoDevelop/Ide/MainMenu/Build"> 
        <CommandItem id="BinObjCleaner.BinObjCleanerCommands.DeleteBinObj" />
     </Extension>
 </ExtensionModel> 

А теперь по порядку. Строка:

<Extension path = "/MonoDevelop/Ide/Commands"> 

Она говорит о том, в какую категорию попадает Add-in. Так как создаем кнопку, категорией будет «Commands», если бы создавали шаблон файла, то в качестве категории нужно было бы указать «FileTemplates» (конечно же двумя категориями дело не ограничивается). Идем дальше.

<Command id = "BinObjCleaner.BinObjCleanerCommands.DeleteBinObj" 
             _label = "Delete bin/obj" 
             _description = "Delete /bin and /obj folders in every solution project"  
             defaultHandler = "BinObjCleaner.DeleteBinObjHandler" /> 

Здесь задается:

  • id’шник кнопки, представляет из себя путь (с указанием namespace’а) до enum’а с типом кнопки, т.е. {namespace}.{enum_name}.{enum_type_name};
  • label — текст, который будет отображаться;
  • description — описание, что кнопка умеет делать;
  • defaultHandler — указываем путь до класса, который будет содержать в себе логику работы кнопки, т.е. {namespace}.{class_name}.

Следующие строки:

<Extension path = "/MonoDevelop/Ide/MainMenu/Build">
         <CommandItem id="BinObjCleaner.BinObjCleanerCommands.DeleteBinObj" /> 

Определяют расположение Add-in’а (в нашем случае кнопки) с конкретным id в IDE. Т.е., после запуска Add-in’а, кнопку мы должны увидеть здесь:

image

Продолжаем двигаться к этой цели. Необходимо теперь создать пустой enum (Клик правой кнопкой мыши на проекте > Add > New file)

image

И прописать в нем:

using System;

  namespace BinObjCleaner 
{
     public enum BinObjCleanerCommands
     { 
        DeleteBinObj
     } 
} 

Это мы добавили в проект enum для следующей строчки Manifest.addin’а:

<Command id = «BinObjCleaner.BinObjCleanerCommands.DeleteBinObj" 

Дальше, создаем пустой класс аналогично enum’у:

image

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

defaultHandler = "BinObjCleaner.DeleteBinObjHandler" 

Наследуем его от CommandHandler’а и переопределяем два метода — Run и Update. Должно получится следующее:

using System; 
using MonoDevelop.Components.Commands; 
using MonoDevelop.Ide;
 using MonoDevelop.Core;  

namespace BinObjCleaner 
{ 
    public class DeleteBinObjHandler : CommandHandler
     { 
        protected override void Run () 
        {
             
       } 

        protected override void Update (CommandInfo info) 
        {
             
       }
     } 
} 

Переопределение метода Update необходимо для управления видимостью кнопки, т.е. если не открыто ни одно решение, то кнопка показываться не должна, так как тогда не понятно в каком именно решении нужно удалить bin и obj папки. Для этого нужно добавить в метод Update одну строку:

info.Visible = IdeApp.Workspace.IsOpen; 

В переопределении метода Run будет содержаться вся логика, связанная с удалением bin и obj папок. Вызывается этот метод после нажатия на кнопку. Перед удалением папок нужно проверить запущено решение или начата ли его сборка, а также что эти операции завершены. Добавим в метод Run следующий код:

var projectOperation = IdeApp.ProjectOperations; 
var isBuild = projectOperation.IsBuilding (projectOperation.CurrentSelectedSolution);
 var isRun = projectOperation.IsRunning (projectOperation.CurrentSelectedSolution);

  if (!isBuild && !isRun  
   && IdeApp.ProjectOperations.CurrentBuildOperation.IsCompleted   
  && IdeApp.ProjectOperations.CurrentRunOperation.IsCompleted) 
{ 
  
} 

Таким образом мы получим список операций, которые могут выполнятся над решением, проверим не началась/завершилась ли сборка решения, не был ли начат/закончен запуск решения.
После того как мы убедились, что удаление bin и obj папок ничего не сломает, необходимо получить список проектов, входящих в решение, для этого внутрь if’а добавим следующую строчку:

var solutionItems = projectOperation.CurrentSelectedSolution.Items; 

Теперь, когда мы знаем список проектов в решении, остается лишь пройтись по пути до каждого проекта, добавляя «/bin» или «/obj» и удаляя эти папки, предварительно проверив их существование:

foreach (var item in solutionItems)  { 
  var binPath = item.BaseDirectory.FullPath + "/bin"; 
  if (FileService.IsValidPath (binPath) && FileService.IsDirectory (binPath)) 
     FileService.DeleteDirectory (binPath);  

  var objPath = item.BaseDirectory.FullPath + "/obj"; 
  if (FileService.IsValidPath (objPath) && FileService.IsDirectory (objPath))
      FileService.DeleteDirectory (objPath); 
} 

В итоге DeleteBinObjHandler должен выглядеть следующим образом:

using System;
 using MonoDevelop.Components.Commands; 
using MonoDevelop.Ide;
 using MonoDevelop.Core;

 namespace BinObjCleaner 
{ 
    public class DeleteBinObjHandler : CommandHandler 
    {
         protected override void Run () 
        {
             var projectOperation = IdeApp.ProjectOperations;
             var isBuild = projectOperation.IsBuilding (projectOperation.CurrentSelectedSolution); 
            var isRun = projectOperation.IsRunning (projectOperation.CurrentSelectedSolution);  

            if (!isBuild && !isRun  
                && IdeApp.ProjectOperations.CurrentBuildOperation.IsCompleted
                  && IdeApp.ProjectOperations.CurrentRunOperation.IsCompleted) 
            { 
                IdeApp.Workbench.StatusBar.BeginProgress("Deleting /bin and /obj folders");  

                var solutionItems = projectOperation.CurrentSelectedSolution.Items;

                 foreach (var item in solutionItems)                 { 
                    var binPath = item.BaseDirectory.FullPath + "/bin"; 
                    if (FileService.IsValidPath (binPath) && FileService.IsDirectory (binPath))
                         FileService.DeleteDirectory (binPath);  

                    var objPath = item.BaseDirectory.FullPath + "/obj"; 
                    if (FileService.IsValidPath (objPath) && FileService.IsDirectory (objPath)) 
                        FileService.DeleteDirectory (objPath);  
                }  

                IdeApp.Workbench.StatusBar.EndProgress ();
                 IdeApp.Workbench.StatusBar.ShowMessage ("Deleted success"); 
            }
         }

          protected override void Update (CommandInfo info) 
        { 
            info.Visible = IdeApp.Workspace.IsOpen; 
        } 
    } 
} 

Все, что касается статус бара IDE (IdeApp.Workbench.StatusBar), нужно для отображения пользователю визуального уведомления, что процесс удаления начался или закончился. Выглядит так:

image

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

Публикация Add-in

Если вы, как и я, создаете Add-in для внутреннего пользования, то достаточно будет лишь выполнить в консоле команду «mdtool setup pack BinObjCleaner.dll»:

image

И распространить .mpack (если собираете с Mac, то найдете его в домашней папке) среди нуждающихся. Установка производится через менеджер дополнений в Xamarin Studio с помощью установки из файла (Xamarin Studio > Add-in Manager > Install from file).
Если же вы решите выкладывать в официальный репозиторий, то я, к сожалению, могу помочь только ссылкой, т.к. сколько я ни старался, пробиться через многочисленные ошибки при сборке мне не удалось.

Куда двигаться дальше? Вместо заключения

После написания простого Add-in’а, когда я осознал возможности расширения Xamarin Studio, я решил написать шаблон решения и проекта для внутренней разработки с дополнительной логикой, например, c автоматической регистрацией контроллеров при их создании (самая частая ошибка наших новичков – забыть зарегистрировать контроллер 🙂 ). Исходники этого Add-in и Add-in’а, рассмотренного в статье, можно найти на гитхабе здесь и здесь.
В написании обоих мне сильно помогали и помогают следующие ресурсы:

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

На этом все, надеюсь статья вам пригодится. Спасибо, что прочитали!

ссылка на оригинал статьи http://habrahabr.ru/post/256393/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *