1. Общий интерфейс работы со стилями

Дата публикации: 29.06.2011
Дата редактирования: 04.07.2011
Состояние: завершена
Целевая версия: AutoCAD 2009

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

Интерфейс IStyleManager<T, V> where T : new() where V: IStyleSettings<V>, new()


    Тип T - это "родной" тип .Net API AutoCAD, представляющий собой объект конкретного стиля (например для стиля текста это будет класс TextStyleTableRecord). Обратите внимание, на второй параметр обобщённого интерфейса IStyleManager<T, V> - это некий тип V, который для каждого типа стилей будет своим. Нам нужно будет самостоятельно разработать эти типы (благо, что сделать это будет очень просто). должен реализовывать интерфейс IStyleSettings<T> и содержать в себе все настройки, которые мы хотим назначать стилю, или извлекать из него. Далее представлен код интерфейса IStyleManager<T, V>.

IStyleManager<T, V>

   1:  using System;
   2:  using System.Collections;
   3:  using System.Collections.Generic;
   4:  using Autodesk.AutoCAD.ApplicationServices;
   5:  using Autodesk.AutoCAD.DatabaseServices;
   6:  using System.Xml.Serialization;
   7:   
   8:  namespace Bushman.AutoCAD.Styles
   9:  {
  10:      /// <summary>
  11:      /// Обобщённый интерфейс менеджера стилей AutoCAD. Задача этого интерфейса - предоставить единый
  12:      /// подход в работе со стилями любого типа, имеющимся в AutoCAD. Интерфейс определяет общий набор
  13:      /// операций, которые могут потребоваться при работе с любым стилем.
  14:      /// </summary>
  15:      /// <typeparam name="T">Тип, которым в .Net API AutoCAD определяется искомый стиль</typeparam>
  16:      /// <typeparam name="V">Тип, в котором упакованы значения представляющие собой 
  17:      /// настройки конкретного стиля</typeparam>
  18:      public interface IStyleManager<T, V> where T : new() where V: IStyleSettings<V>, new()
  19:      {
  20:          /// <summary>
  21:          /// Получение примитивов, использующих указанный стиль
  22:          /// </summary>
  23:          /// <param name="styleName">Имя стиля</param>
  24:          /// <returns>Возвращается коллекция идентификаторов примитивов</returns>
  25:          ObjectId[] GetDependentPrimitives(string styleName);
  26:          /// <summary>
  27:          /// Получение всех примитивов, использующих стиль, имя которого присутствует в указанном перечне стилей.
  28:          /// В результате формируется общий массив идентификаторов таких объектов.
  29:          /// </summary>
  30:          /// <param name="styleNames">Массив имён стилей</param>
  31:          /// <returns>Возвращается словарь, каждая запись которого содержит в себе массивов идентификаторов. 
  32:          /// В качестве ключа для каждого массива используется имя соответствующего стиля</returns>
  33:          Dictionary<string, ObjectId[]> GetDependentPrimitives(string[] styleNames);
  34:          /// <summary>
  35:          /// Переименовать стиль
  36:          /// </summary>
  37:          /// <param name="oldStyleName">Старое имя стиля</param>
  38:          /// <param name="newStyleName">Новое имя стиля</param>
  39:          /// <returns>Возвращается идентификатор переименованного стиля. Если искомый стиль не найден -
  40:          /// возвращается null</returns>
  41:          ObjectId Rename(string oldStyleName, string newStyleName);
  42:          /// <summary>
  43:          /// Создаёт в базе данных чертежа новый стиль, после чего сразу же инициализирует его значениями, 
  44:          /// переданными через параметр метода.
  45:          /// </summary>
  46:          /// <param name="styleSettings">Набор значений, которыми должен быть инициализирован объект стиля</param>
  47:          /// <returns>Возвращается идентификатор добавленного в чертёж стиля</returns>
  48:          ObjectId CreateNew(V styleSettings);
  49:          /// <summary>
  50:          /// Получить текущие настройки стиля
  51:          /// </summary>
  52:          /// <param name="styleName">Имя стиля</param>
  53:          /// <returns>Возвращается объект, представляющий собой настройки стиля</returns>
  54:          V GetSettings(string styleName);
  55:          /// <summary>
  56:          /// Получить текущие настройки стиля
  57:          /// </summary>
  58:          /// <param name="styleId">Идентификатор стиля</param>
  59:          /// <returns>Возвращается объект, представляющий собой настройки стиля</returns>
  60:          V GetSettings(ObjectId styleId);
  61:          /// <summary>
  62:          /// Изменить настройки стиля
  63:          /// </summary>
  64:          /// <param name="styleName">имя редактируемого стиля</param>
  65:          /// <param name="settings">Объект, представляющий собой настройки которые должны быть назначены 
  66:          /// стилю.</param>
  67:          void SetSettings(string styleName, V settings);
  68:          /// <summary>
  69:          /// База данных чертежа, со стилями которой должен работать менеджер
  70:          /// </summary>
  71:          Database TargetDb { get; }
  72:          /// <summary>
  73:          /// Проверка на то, существует ли в чертеже стиль с указанным именем
  74:          /// </summary>
  75:          /// <param name="styleName">Имя стиля, поиск которого выполняется</param>
  76:          /// <returns>True - стиль с указанным именем существует в базе данных чертежа; 
  77:          /// False - не существует</returns>
  78:          bool Exists(string styleName);
  79:          /// <summary>
  80:          /// Получить стиль из базы данных чертежа по его имени
  81:          /// </summary>
  82:          /// <param name="styleName">Имя искомого стиля</param>
  83:          /// <returns>Возвращается объект стиля. Если стиль не найден - возвращается null</returns>
  84:          T GetStyle(string styleName);
  85:          /// <summary>
  86:          /// Получить стиль из базы данных чертежа по его идентификатору
  87:          /// </summary>
  88:          /// <param name="styleName">Идентификатор искомого стиля</param>
  89:          /// <returns>Возвращается объект стиля. Если стиль не найден - возвращается null</returns>
  90:          T GetStyle(ObjectId styleId);
  91:          /// <summary>
  92:          /// Получить все стили, имеющиеся в базе данных чертежа
  93:          /// </summary>
  94:          /// <returns>Возвращается массив объектов стилей</returns>
  95:          T[] GetStyles();
  96:          /// <summary>
  97:          /// Импорт стиля из другого чертежа
  98:          /// </summary>
  99:          /// <param name="database">База данных (объект класса Database), 
 100:          /// из которой нужно импортировать стиль</param>
 101:          /// <param name="sourceStyleName">Имя импортируемого стиля</param>  
 102:          /// <param name="resultStyleName">Имя результирующего стиля</param> 
 103:          /// <returns>Возвращается ссылка на добавленный в базу данных чертежа стиль.
 104:          /// Если импорт не удался - возвращается null</returns>
 105:          T ImportFromDb(Database database, string sourceStyleName, string resultStyleName);
 106:          /// <summary>
 107:          /// Импорт стиля из другого чертежа
 108:          /// </summary>
 109:          /// <param name="drawingName">Имя файла чертежа, из которой нужно импортировать стиль</param>
 110:          /// <param name="password">Пароль к файлу чертежа. Если пароля нет - следует 
 111:          /// передавать пустую строку</param>
 112:          /// <param name="sourceStyleName">Имя импортируемого стиля</param>  
 113:          /// <param name="resultStyleName">Имя результирующего стиля</param> 
 114:          /// <returns>Возвращается ссылка на добавленный в базу данных чертежа стиль.
 115:          /// Если импорт не удался - возвращается null</returns>
 116:          T ImportFromDrawing(string drawingName, string password, string sourceStyleName, string resultStyleName);
 117:          /// <summary>
 118:          /// Объединение двух стилей в один. Стиль, имя которого передано в качестве первого параметра
 119:          /// будет удалён и останется только стиль, имя которого передано в качестве второго параметра. Все
 120:          /// объекты, использовавшие первый стиль, станут использовать второй стиль.
 121:          /// </summary>
 122:          /// <param name="unitedStyleName">Имя стиля, который должен исчезнуть после объединения</param>
 123:          /// <param name="resultStyleName">Имя результирующего стиля</param>
 124:          /// <returns>Возвращает ObjectId результирующего стиля. В случае неудачи возвращается ObjectId.Null</returns>
 125:          ObjectId Union(string unitedStyleName, string resultStyleName);
 126:          /// <summary>
 127:          /// Объединение сразу нескольких стилей в один. Стили, имена которых указаны в переданном  
 128:          /// параметре метода будут удалены - останется только стиль, имя которого указано 
 129:          /// вторым параметром. Все объекты, использовавшие удаляемые стили станут использовать целевой стиль.
 130:          /// </summary>
 131:          /// <param name="unitedStyleNames">Имена стилей, которые должны исчезнуть после объединения</param>
 132:          /// <param name="resultStyleName">Имя результирующего стиля</param>
 133:          /// <returns>Возвращает ObjectId результирующего стиля. В случае неудачи возвращается ObjectId.Null</returns>
 134:          ObjectId Union(string[] unitedStyleNames, string resultStyleName);
 135:          /// <summary>
 136:          /// Проверка на то, может ли указанный стиль быть удалён из базы данных чертежа.
 137:          /// Если стиль не имеет имени 'Standard' и при этом не может быть удалён, значит он используется
 138:          /// какими-то примитивами.
 139:          /// </summary>
 140:          /// <param name="styleName">Имя стиля, который нужно проверить на предмет возможности его 
 141:          /// удаления из чертежа</param>
 142:          /// <returns>True - стиль может быть удалён; False - не может быть удалён.</returns>
 143:          bool CanBeRemoved(string styleName);
 144:          /// <summary>
 145:          /// Удалить стиль
 146:          /// </summary>
 147:          /// <param name="styleName">Имя стиля, который нужно удалить</param>
 148:          void Remove(string styleName);
 149:      }
 150:  }

    Код подробно комментирован, т.о. не должно возникнуть особых трудностей в его понимании. Прежде чем приступить к разработке интерфейса настроек, сделаем маленькое отступление - создадим обобщённый интерфейс ICloneable<T> (не знаю, по какой причине Microsoft не реализовала его в .Net Framework). Этот интерфейс будет входить в состав интерфейса настроек. 

Интерфейс ICloneable<T>

    Интерфейс ICloneable<T> - это обобщённый вариант интерфейса ICloneable. Я не хочу каждый раз при создании копии объекта выполнять принудительное приведение типа, поэтому и написал его. Код такой:

   1:  using System;
   2:  using System.Collections.Generic;
   3:   
   4:  namespace Bushman.Common {
   5:      /// <summary>
   6:      /// Интерфейс для создания копии объекта определённого
   7:      /// типа (без необходимости последующего приведения).
   8:      /// </summary>
   9:      /// <typeparam name="T">Тип объекта</typeparam>
  10:      public interface ICloneable<T>{
  11:          /// <summary>
  12:          /// Создать копию объекта
  13:          /// </summary>
  14:          /// <returns>Возвращается полная копия объекта</returns>
  15:          T Clone();
  16:      }
  17:  }

Интерфейс IStyleSettings<T> : IEquatable<T>, ICloneable<T>, IXmlSerializable

    Настройки каждого стиля так же должны реализовывать некий начальный, общий для всех набор возможностей. Для этой цели мы напишем интерфейс IStyleSettings<T>

    Код интерфейса будет таким:

   1:  using System;
   2:  using System.Collections.Generic;
   3:  using System.Xml.Serialization;
   4:  using System.Xml.Linq;
   5:  using System.Text;
   6:  using Bushman.Common;
   7:   
   8:  namespace Bushman.AutoCAD.Styles {
   9:      /// <summary>
  10:      /// Интерфейс, который должны реализовывать все классы, представляющие собой
  11:      /// настройки стилей AutoCAD
  12:      /// </summary>
  13:      /// <typeparam name="T">Тип класса настроек</typeparam>
  14:      public interface IStyleSettings<T> : IEquatable<T>, ICloneable<T>, IXmlSerializable {
  15:          /// <summary>
  16:          /// Упаковать настройки в виде объекта XElement
  17:          /// </summary>
  18:          /// <param name="encoding">Кодировка (рекомендую UTF8)</param>
  19:          /// <returns>Возвращается объект XElement, в котором упакованы 
  20:          /// настройки текстового стиля</returns>
  21:          XElement GetXElement(Encoding encoding);
  22:          /// <summary>
  23:          /// Загрузить настройки из xml-файла
  24:          /// </summary>
  25:          /// <param name="xmlFileName">Имя файла, в котором сериализованы настройки</param>
  26:          void Load(string xmlFileName);
  27:          /// <summary>
  28:          /// Загрузить настройки из xml-элемента
  29:          /// </summary>
  30:          /// <param name="xml">xml-элемент, в котором содержатся настройки</param>
  31:          void Load(XElement xml);
  32:          /// <summary>
  33:          /// Сохранить настройки в новый xml-файл
  34:          /// </summary>
  35:          /// <param name="xmlFileName">Имя файла</param>
  36:          /// <param name="encoding">Кодировка (рекомендуется UTF8)</param>
  37:          void Save(string xmlFileName, Encoding encoding);
  38:      }
  39:  }

    Интерфейс настроек очень простой и благодаря комментариям его содержимое, на мой взгляд, вполне понятно.

Общий принцип работы с любым стилем будет одним и тем же:

    Для того, чтобы создать новый стиль, мы сначала должны сформировать экземпляр IStyleSettings<V> (настройки нового стиля), определив все его значения так как нам это нужно, после чего можно приступать к созданию самого стиля, передав наши настройки (экземпляр IStyleSettings<V>) методу CreateNew (в качестве параметра) менеджера соответствующего стиля. Реализацию IStyleSettings<V> я предпочитаю делать по следующему принципу: открываю в AutoCAD окно редактора стилей, смотрю какие настройки в нём есть - именно такие свойства (с такими же именами) и создаю в своём классе (имхо в "родном" .Net API по настройке некоторых стилей сам чёрт ногу сломит).

    Для того, чтобы изменить существующий стиль - нам нужно получить из него объект V (с помощью метода GetSettings), содержащий текущие настройки стиля, выполнить над ними необходимые изменения, после чего вернуть их обратно (используя метод SetSettings). Можно и не использовать GetSettings, а создать и настроить V что называется "с нуля". 

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

Подводим итоги

    На мой взгляд должна получится удобная библиотека по работе со стилями AutoCAD. В классах и интерфейсах я умышленно избегал использование класса Document, ограничиваясь лишь Database - это сделано для того, чтобы была возможность пользоваться библиотекой и применительно к чертежам, которые не были открыты в AutoCAD. Т.о. данная библиотека может быть использована для работы с чертежами в фоновом режиме (например пакетная обработка чертежей без их непосредственного визуального открытия в AutoCAD).

P.S.

    В менеджерах стилей нами будет активно использоваться класс, созданный ранее в статье "Выборка примитивов из базы данных чертежа":



Comments