Все записи автора Андрей Бушман

Autodesk и санкции против России

Заявление генерального директора Autodesk в России и СНГ Алексея Рыжова заставляет серьёзно задуматься о том, какие "сюрпризы" могут неожиданно свалиться нам на голову, поскольку и мы являемся пользователями AutoCAD... На всякий случай продублирую содержимое обозначенного заявления, т.к. не исключено, что со временем оно может исчезнуть с сайта Autodesk, или подвергнуться редактированию (политика ведь и всё такое...).

Итак, официальное письмо генерального директора Autodesk в России и СНГ Алексея Рыжова, адресованное клиентам, пользующимся продуктами компании Autodesk, а так же её технической поддержкой:

Уважаемые коллеги,
После введения санкций США в некоторых российских средствах массовой информации распространилось мнение, что компания Autodesk вводит собственные санкции в отношении ряда российских компаний, произвольно ограничивая доступ к своим продуктам. Я хотел бы опровергнуть эту информацию, разъяснить ситуацию и высказать некоторые мысли по данному вопросу:
  1. То, что Autodesk вводит санкции против России – это миф. Вопросы «вводит ли Autodesk санкции» или «поддерживает ли Autodesk санкции» неверны по своей сути. Как известно, штаб-квартира Autodesk, как и у многих ведущих ИТ-компаний, находится в США. Поэтому если правительство США вводит торговые санкции, действующее законодательство запрещает нам осуществлять поставки для компаний, на которые эти санкции распространяются. По этой причине у нас нет другого выхода, кроме как приостановить поставки для компаний из санкционного списка. Сегодня поставки этим компаниям технически невозможны из-за требований экспортного контроля.

    На сегодняшний день санкции коснулись только небольшого числа клиентов Autodesk. Тем не менее, мы сожалеем о тех негативных последствиях, с которыми столкнулись и эти компании, и мы сами. Для Autodesk эта ситуация является форс-мажором в точно такой же мере, как для других компаний со штаб-квартирой в Европе или США.
  2. ПО Autodesk является для наших клиентов средством производства, и отказ от него может оказаться для них довольно болезненным. Поэтому мы посчитали важным предупредить об ограничении поставок заранее, что и сделали письмом для наших партнеров, которое попало в СМИ. Мы сочли необходимым в сложившейся ситуации работать максимально открыто и заблаговременно сообщить нашим контрагентам о ситуации, вместо того, чтобы блокировать транзакции по факту заказа.
  3. Мы готовы предоставить консультации любым клиентам, кто сомневается, распространяются ли на их компании ограничение поставок.
  4. Выскажу и некоторые свои соображения об импортозамещении и дальнейшем развитии рынка САПР в России. На мой взгляд, отечественные разработки в области САПР должны развиваться вне зависимости от внешнеполитической ситуации. Однако только открытый, конкурентный рынок может помочь клиентам выбрать лучшие инструменты для решения насущных задач, а всем компаниям-поставщикам инвестировать в развитие и разработку новых ИТ-решений.

    Так Autodesk вкладывает в исследования и разработку более 600 миллионов долларов ежегодно (более четверти годового оборота). В то же время мы активно поддерживаем российских разработчиков в рамках инициативы Autodesk Developer Network (ADN). Наши ADN-партнеры, на мой взгляд, формируют наиболее перспективный, развивающийся сегмент на рынке российского программного обеспечения для проектирования.
  5. Успех нашего бизнеса напрямую зависит от развития промышленности и строительства в России. Поэтому мы заинтересованы в стабильности как американской, так и российской экономики. Российский рынок – важная часть глобального бизнеса Autodesk. Autodesk работает здесь с 1989 года, и за это время приобрела более десяти тысяч корпоративных клиентов и миллионы пользователей домашних приложений. В России компания осуществляет многомиллионные инвестиции в сферу образования, а также в проекты, направленные на развитие инноваций, решение социальных и экологических проблем.
  6. Мы продолжаем развивать бизнес в России, и вы сможете сами услышать о наших программах, предложениях и инвестициях на ежегодной конференции Autodesk University, которая пройдет 1-2 октября 2014 г. в Москве.
 
С уважением,
Алексей Рыжов
Генеральный директор Autodesk в России и СНГ


 На мой взгляд, любому здравомыслящему человеку совершенно очевидно, что конечному пользователю не важно, кто именно перекрывает ему кислород - компания Autodesk или же "дядя Обама": важен конечный результат. Отлично понимает это и Алексей Рыжов (было бы очень странно, если бы не понимал). А тут ещё вроде как наша компания выиграла тендер на проектирование моста через Керченский пролив (всё что касается Крыма, как хорошо известно, вызывает у США сильный зуд в нижней части туловища). Не удивлюсь, если американский "список прокажённых" вдруг пополнится наименованием нашей организации. И дело здесь не в Autodesk - по аналогии будут поступать и др. зарубежные компании (пусть даже и вынужденно).

Похоже на то, что нужно постепенно как-то уходить от использования программного обеспечения зарубежных компаний, выискивая подходящие под наши нужны российские аналоги. Из российских разработчиков САПР мне известна только компания Нанософт, хотя и тут ситуация достаточно сложная: nanoCAD спроектирован на основе платформы Teigha компании ODA. Несмотря на то, что в ODA работают и российские программисты, но факт остаётся фактом - ODA является зарубежной компанией, что не может не наводить на некоторые невесёлые размышления...

Что же касается nanoCAD, то версия 6, продемонстрированная сегодня на конференции, меня приятно удивила. Впечатлил и продемонстрированный nanoCAD СПДС. Продукты Нанософт вообще динамически развиваются. То, каким образом и с какой скоростью эта компания реагирует на пожелания и замечания пользователей, так же настраивает на позитивный лад (Autodesk, по моему опыту, в этом отношении нервно курит в сторонке). Да, в ряде достаточно важных вопросов nanoCAD всё ещё отстаёт от AutoCAD, но в то же время в ряде других, не всегда менее важных тем, он уже оставил AutoCAD далеко позади.

Я задумался над тем, чтобы установить триальную версию nanoCAD СПДС одному из сотрудников, занимающихся нормоконтролем, а так же нескольким проектировщикам с тем, чтобы они в течении месяца поработали в nanoCAD вместо AutoCAD. Правда сначала нужно будет научить их грамотно, полноценно использовать nanoCAD СПДС (скорее всего посредством курсов от Нанософт). Затем, по окончанию триального периода, будем анализировать результаты и думать, взвешивая все плюсы и минусы, выявленные в процессе работы... К сожалению, насколько я понял, в настоящее время компания не предоставляет средств электронной проверки чертежей, подобных Design Review, но хочется верить, что со временем и эта проблема будет решена.

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

Маленький пример на тему того, как не меняя программный код можно управлять способом работы с базой данных чертежа: с использованием транзакции, или же с её эмуляцией. Используя такой подход можно моментально поменять способ работы сразу во многих местах, не внося правки в исходный код.
// © Андрей Бушман, 2014
// Commands.cs
// AutoCAD 2009 SP3
// Код предназначен для демонстрации того, как можно, не внося изменений в 
// исходный код, менять способ работы с базой данных чертежа: с использованием 
// транзакции или же с её эмуляцией.

using System;

#if AUTOCAD
using cad = Autodesk.AutoCAD.ApplicationServices.Application;
using Ap = Autodesk.AutoCAD.ApplicationServices;
using Db = Autodesk.AutoCAD.DatabaseServices;
using Ed = Autodesk.AutoCAD.EditorInput;
using Gm = Autodesk.AutoCAD.Geometry;
using Rt = Autodesk.AutoCAD.Runtime;
#endif

[assembly: Rt.CommandClass(typeof(Bushman.CAD.Sandbox.TransactionGames.Commands
    ))]

namespace Bushman.CAD.Sandbox.TransactionGames {
    public sealed class Commands {

        /// <summary>
        /// Тестовая команда
        /// </summary>
        [Rt.CommandMethod("SwitchTransactionSample")]
        public void SwitchTransactionSample() {

            Ap.Document doc = cad.DocumentManager.MdiActiveDocument;
            if (null == doc)
                return;
            Db.Database db = doc.Database;
            Db.TransactionManager tm = db.TransactionManager;
            Ed.Editor ed = doc.Editor;

            Ed.PromptKeywordOptions pso = new Ed.PromptKeywordOptions(
                "Использовать транзакцию?");
            String yes = "Yes";
            String no = "No";
            pso.Keywords.Add(yes);
            pso.Keywords.Add(no);
            pso.AppendKeywordsToMessage = true;
            pso.Keywords.Default = yes;

            Ed.PromptResult pr = ed.GetKeywords(pso);
            ed.WriteMessage("Вы выбрали: {0}n", pr.StringResult);

            if (Ed.PromptStatus.OK != pr.Status) {
                ed.WriteMessage("Выполнение команды прервано.n");
            }

            Boolean transaction_use = pr.StringResult == yes;

            // Т.к. OpenCloseTransaction наследуется от Transaction, то 
            // получить Transaction можно следующим образом:
            using (Db.Transaction tr = GetTransaction(tm, transaction_use)) {

                Db.DBText text = new Db.DBText();
                text.SetDatabaseDefaults(db);
                text.Height = 30;
                Double y = 100;
                String text_string = String.Empty;

                if (transaction_use) {
                    text_string = "Текст создан с использованием транзакции.";
                }
                else {
                    y = 200;
                    text_string = "Текст создан с эмуляцией транзакции.";
                }

                text.Position = new Gm.Point3d(100, y, 0);
                text.TextString = text_string;

                Db.BlockTableRecord model_space = tr.GetObject(
                    db.CurrentSpaceId, Db.OpenMode.ForWrite)
                    as Db.BlockTableRecord;

                model_space.AppendEntity(text);
                tr.AddNewlyCreatedDBObject(text, true);

                tr.Commit();
            }
        }

        /// <summary>
        /// Данный метод позволяет выбрать способ работы с базой данных 
        /// чертежа: с использованием транзакции или же с её эмуляцией.
        /// </summary>
        /// <param name="tm">Менеджер транзакций</param>
        /// <param name="emulate"><c>true</c> - использовать транзакцию; 
        /// <c>false</c> - эмулировать транзакцию. 
        /// </param>
        /// <returns>Возвращается объект <c>Transaction</c> (
        /// <c>OpenCloseTransaction</c> унаследован от этого класса).</returns>
        Db.Transaction GetTransaction(Db.TransactionManager tm,
            Boolean transaction_use) {
            Db.Transaction tr = transaction_use ? tm.StartTransaction() :
                tm.StartOpenCloseTransaction();
            return tr;
        }
    }
}

Последовательность действий AutoCAD при загрузке управляемого расширения

Маленькая шпаргалка на тему того, как AutoCAD обрабатывает загруженное в него управляемое расширение.


При загрузке управляемой сборки в AutoCAD сначала проверяются атрибуты сборки. Если найден атрибут ExtensionApplication, указывающий на публичный класс, реализующий интерфейс IExtensionApplication, то создаётся экземпляр данного класса и вызывается его метод Initialize. В дальнейшем, при завершении сеанса AutoCAD, обязательно будет вызван метод Terminate этого же экземпляра. К моменту вызова Terminate все документы приложения уже закрыты. 

В сборке должно быть не более одного атрибута ExtensionApplication. Если же сборка не содержит атрибута ExtensionApplication, то тогда производится анализ всех публичных классов этой сборки. В случае успешного результата поиска создаётся экземпляр первого найденного публичного класса, реализующего IExtensionApplication. При этом обязательно должна присутствовать возможность создания экземпляра данного класса при помощи конструктора по умолчанию.

Затем AutoCAD проверяет сборку на наличие атрибутов CommandClass, указывающих на её публичные классы, в которых определены команды AutoCAD. Таких атрибутов может быть столько, сколько потребуется разработчику. Если у сборки обнаружены атрибуты CommandClass, то выполняется аналитика содержимого только тех публичных классов, на которые указывают эти атрибуты - все остальные классы игнорируются.

Если в сборка не имеет атрибутов CommandClass, то производится поиск всех публичных классов, в которых определены команды AutoCAD.

Команды, определённые в составе сборки, могут быть как статическими, так и экземплярными. Если команда экземплярная, то объект данного класса создаётся применительно к документу, из контекста которого команда вызвана. Это означает, что значение свойств и полей у такого объекта для каждого документа будут свои. В таком классе обязательно должна присутствовать возможность создать его экземпляр при помощи конструктора по умолчанию. Причём создаётся этот объект в момент первого вызова любой из экземплярных команд данного класса. Время жизни такого объекта определяется временем жизни документа, к которому он привязан. Удаление этого объекта происходит при закрытии документа. Т.о. если класс удаляемого объекта реализует IDisposable, то будет вызываться метод Dispose.

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

Если команды определены в статических методах класса, то тогда экземпляры такого класса создаваться не будут. Код статического конструктора такого класса будет запущен на исполнение один раз, при первом запуске команды этого класса.

Назначение сборке атрибутов ExtensionApplication и CommandClass позволяет AutoCAD быстро найти нужную ему информацию, не анализируя все классы сборки и, соответственно, быстрее загрузить её.

Далее небольшой код, демонстрирующий описанный выше материал. В комментариях показан результат работы кода - содержимое файла MyLog.txt.

/* MyClasses.cs
 * Андрей Бушман, 2014
 * 
 * Код демонстрирует последовательность вызовов
 * методов, конструкторов и деструкторов 
 * различных классов команд, а так же класса, 
 * реализующего интерфейс IExtensionApplication.
 * 
 * Последовательность действий:
 * ---------------------------------------------
 * 1. Запускаем AutoCAD 2015 (или любую др. 
 *      версию).
 * 2. Создаём три новых документа: Drawing1.dwg, 
 *      Drawing2.dwg и Drawing3.dwg.
 * 3. Устанавливаем текущим документ Drawing1.dwg
 * 4. Загружаем сборку при помощи команды NETLOAD
 * 5. Последовательно запускаем команды: TEST, 
 *      TEST2 и снова TEST.
 * 6. Переключаемся в документ Drawing2.dwg.
 * 7. Последовательно запускаем команды: TEST, 
 *      TEST2 и снова TEST.
 * 8. Закрываем все документы и завершаем работу 
 *      AutoCAD.
 * 9. Смотрим содержимое файла MyLog.txt. 
 *      ВНИМАНИЕ! acad.exe ещё некоторое время 
 *      работает даже после того, как все окна 
 *      закрылись. Прежде чем открывать файл 
 *      MyLog.txt дождитесь завершения процесса 
 *      acad.exe, т.к. нам нужно дождаться, когда
 *      выполнится код метода Terminate().
 * ---------------------------------------------
 * 
 * Содержимое сгенерированного файла MyLog.txt: 
 * ==============================================
 * Метод: ExtensionApplication.Initialize(); 
 *  Документ: Drawing1.dwg; Время: 05.08.2014 
 *  11:56:55
 * Метод: Commands.Commands() [статический]; 
 *  Документ: Drawing1.dwg; Время: 05.08.2014 
 *  11:57:27
 * Метод: Commands.Commands(); 
 *  Документ: Drawing1.dwg; Время: 05.08.2014 
 *  11:57:41
 * Метод: StaticCommands.StaticCommands() 
 *  [статический]; Документ: Drawing1.dwg; 
 *  Время: 05.08.2014 11:57:53
 * Метод: Commands.Commands(); 
 *  Документ: Drawing2.dwg; Время: 05.08.2014 
 *  11:58:15
 * Метод: Commands.Dispose(); 
 *  Документ: Drawing2.dwg; Время: 05.08.2014 
 *  11:58:31
 * Метод: Commands.Finalize(); 
 *  Документ: Drawing2.dwg; Время: 05.08.2014 
 *  11:58:33
 * Метод: Commands.Dispose(); 
 *  Документ: Drawing1.dwg; Время: 05.08.2014 
 *  11:58:33
 * Метод: Commands.Finalize(); 
 *  Документ: Drawing1.dwg; Время: 05.08.2014 
 *  11:58:34
 * Метод: ExtensionApplication.Terminate(); 
 *  Документ: null; Время: 05.08.2014 11:59:04
 *  =============================================
 */
using System;
using System.Reflection;
using System.IO;

using cad = Autodesk.AutoCAD.ApplicationServices
.Application;
using Ap = Autodesk.AutoCAD.ApplicationServices;
using Rt = Autodesk.AutoCAD.Runtime;

[assembly: Rt.ExtensionApplication(typeof(Bushman
    .CAD.Logging.ExtApp))]

[assembly: Rt.CommandClass(typeof(Bushman.CAD
    .Logging.Commands))]

[assembly: Rt.CommandClass(typeof(Bushman.CAD
    .Logging.StaticCommands))]

namespace Bushman.CAD.Logging {

    static class LogWritter {
        static String logFileName = Path.Combine(
            Environment.GetFolderPath(Environment
            .SpecialFolder.MyDocuments),
            "MyLog.txt");

        public static void WriteLine(
            String methodName,
            String documentName) {
            using (StreamWriter sw =
                File.AppendText(logFileName)) {
                String line = String.Format(
                "Метод: {0}; Документ: {1}; " +
                "Время: {2}", methodName,
                documentName, DateTime.Now
                .ToString("dd.MM.yyyy hh:mm:ss"))
                ;
                sw.WriteLine(line);
                sw.Flush();
                sw.Close();
            }
        }
    }

    public sealed class ExtApp :
        Rt.IExtensionApplication {

        // Можно дополнительно создать 
        // статический и|или экземплярный 
        // конструкторы класса, однако вряд ли в
        // данном случае это будет иметь смысл, 
        // посколько равно как и метод Initialize
        // они будут вызваны лишь один раз. В 
        // виду этого можно ограничиться 
        // использованием лишь Initialize.
        // Аналогичная ситуация обстоит и с 
        // Dispose (в случае реализации
        // IDisposable) - вполне достаточно 
        // использовать Terminate.

        // Этот метод выполняется один раз при 
        // загрузке сборки в AutoCAD.
        public void Initialize() {
            Ap.Document doc = cad.DocumentManager
                .MdiActiveDocument;
            String className = MethodBase
                .GetCurrentMethod().ReflectedType
                .Name;
            String methodName = MethodBase
                .GetCurrentMethod().Name;
            String name = className + "." +
                methodName + "()";
            LogWritter.WriteLine(name, doc.Name);
        }

        // Этот метод выполняется один раз при 
        // завершении работы AutoCAD. К этому 
        // моменту все документы уже закрыты.
        public void Terminate() {
            Ap.Document doc = cad.DocumentManager
                .MdiActiveDocument;
            String documentName = (null == doc) ?
                "null" : doc.Name;
            String className = MethodBase
                .GetCurrentMethod().ReflectedType
                .Name;
            String methodName = MethodBase
                .GetCurrentMethod().Name;
            String name = className + "." +
                methodName + "()";
            LogWritter.WriteLine(name,
                documentName);
        }
    }

    // Инстанцирование объекта данного класса 
    // происходит индивидуально для каждого 
    // документа и только в том случае, если была
    // вызвана команда, определённая в данном 
    // классе.
    public sealed class Commands : IDisposable {

        String documentName;

        // статический конструктор. Вызывается 
        // один раз при первом вызове из любого 
        // документа любой из команд, 
        // определённых в данном классе.
        static Commands() {
            Ap.Document doc = cad.DocumentManager
                .MdiActiveDocument;
            String documentName = (null == doc) ?
                "null" : doc.Name;
            String className = MethodBase
                .GetCurrentMethod().ReflectedType
                .Name;
            String methodName = MethodBase
                .GetCurrentMethod().Name;
            if (methodName == ".cctor")
                methodName = className;
            String name = className + "." +
                methodName + "() [статический]";
            LogWritter.WriteLine(name,
                documentName);
        }

        // Конструктор, код которого выполняется 
        // только при первом запуске любой из 
        // команд, определённых в составе данного
        // класса. Если в процессе работы над 
        // документом команды данного класса не 
        // запускались, то и экземпляр класса не 
        // создаётся.
        public Commands() {
            Ap.Document doc = cad.DocumentManager
                .MdiActiveDocument;
            documentName = (null == doc) ? "null"
                : doc.Name;
            String className = MethodBase
                .GetCurrentMethod().ReflectedType
                .Name;
            String methodName = MethodBase
                .GetCurrentMethod().Name;
            if (methodName == ".ctor")
                methodName = className;
            String name = className + "." +
                methodName + "()";
            LogWritter.WriteLine(name,
                documentName);
        }

        // Тестовая экземплярная команда
        [Rt.CommandMethod("Test")]
        public void Test() { }

        // Деструктор вызывается сборщиком мусора
        // GC после того, как объект данного 
        // класса будет уничтожен. Т.е. в нашем 
        // случае, это произойдёт только после 
        // вызова метода Dispose. Когда именно 
        // вызывается деструктор - решает GC.
        // Вряд ли имеет смысл в расширениях 
        // AutoCAD прибегать к вызову 
        // деструктора. Данный код предназначен 
        // лишь для демонстрации 
        // последовательностей вызовов.
        ~Commands() {
            String className = MethodBase
                .GetCurrentMethod().ReflectedType
                .Name;
            String methodName = MethodBase
                .GetCurrentMethod().Name;
            String name = className + "." +
                methodName + "()";
            LogWritter.WriteLine(name,
                documentName);
        }

        // При закрытии документа объект данного 
        // класса, ранее созданный в контексте 
        // закрываемого документа (см. 
        // комментарий к конструктору) 
        // уничтожается. Соответственно, будет 
        // вызван Dispose, при условии реализации
        // IDisposable. 
        public void Dispose() {
            String className = MethodBase
                .GetCurrentMethod().ReflectedType
                .Name;
            String methodName = MethodBase
                .GetCurrentMethod().Name;
            String name = className + "." +
                methodName + "()";
            LogWritter.WriteLine(name,
                documentName);
        }
    }

    // В этом классе определена статическая 
    // команда. При этом экземпляры данного 
    // класса не создаются.
    public sealed class StaticCommands {

        static String documentName;

        // статический конструктор. Вызывается 
        // один раз при первом вызове из любого 
        // документа любой из команд, 
        // определённых в данном классе.
        static StaticCommands() {
            Ap.Document doc = cad.DocumentManager
                .MdiActiveDocument;
            documentName = (null == doc) ? "null"
                : doc.Name;
            String className = MethodBase
                .GetCurrentMethod().ReflectedType
                .Name;
            String methodName = MethodBase
                .GetCurrentMethod().Name;
            if (methodName == ".cctor")
                methodName = className;
            String name = className + "." +
                methodName + "() [статический]";
            LogWritter.WriteLine(name,
                documentName);
        }

        // Тестовая статическая команда
        [Rt.CommandMethod("Test2")]
        public static void Test2() { }
    }
}

Единая "точка входа" (DLL файл) в плагин .NET, ARX или VBA, не зависящая от версии AutoCAD

Поскольку .NET плагины AutoCAD (и не только они) имеют зависимость от версий обозначенной САПР, то нередко приходится один и тот же исходный код компилировать отдельно под разные версии AutoCAD. Результаты компиляции я, как правило, размещаю либо в одном и том же каталоге плагина, либо по специальным его подкаталогам, имена которых указывают на целевую версию ядра AutoCAD, а так же зависимость от разрядности приложения (если таковая присутствует). Какой из двух перечисленных вариантов использовать - зависит от конкретного случая.

Т.о. результат может выглядеть, к примеру, либо так:


либо так:


Имена файлов, зависящих от версии AutoCAD, так же содержат в виде суффикса версию ядра приложения, а так же, в случае необходимости, разрядность целевой платформы. Например, согласно обозначенному выше скрину, в подкаталоге .R17.2x64 будет находиться файл RegionTools.17.2x64.dll, а в подкаталоге .R20.0 - файл RegionTools.20.0.dll.

Я использую версию ядра AutoCAD, а не год (2009, 2010, 2011 и т.д.), обозначенный в имени САПР, т.к. программно нужную версию DLL файла удобней находить именно по версии ядра AutoCAD - это наиболее надёжная и точная информация.

Конечно, при такой системе наименований рядовому пользователю может быть сложно понять, какой именно DLL файл из набора имеющихся подкаталогов, ему следует загружать в установленную у него версию AutoCAD. Как вариант: можно в файле readme.txt разместить текстовую информацию о том, для какой версии AutoCAD какой DLL файл следует загружать.

Однако можно эту задачу решить иначе: непосредственно в подкаталоге .bin создавать единственный DLL файл, который предназначен для загрузки в любую версию AutoCAD. Предназначение этого файла заключается в том, чтобы загрузить в AutoCAD наиболее подходящую версию плагина, найдя её либо в текущем каталоге сборки, либо в соответствующих подкаталогах. На скрине показанном выше эта роль возложена на файл RegionTools.dll.

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

Аналогичная проблема актуальна так же для плагинов ARX и VBA (VBA "хромает" по части разрядности: x86x64). Поэтому было бы вполне логично расширить решение таким образом, чтобы оно работало не только для .NET, но так же и для C++, VBA. Для ARX и VBA плагинов систему наименований, а так же структуру подкаталогов использую такие же, как и для .NET (указана мною выше).

Поскольку данный подход удобно применять во всех плагинах,  то его можно оформить в виде шаблона проекта:



Далее приводится подробно комментированный код обозначенной выше логики. Проект компилирую с опцией AnyCPU под .NET 3.5,  хотя AutoCAD 2009 по умолчанию использует 3.0. Мой выбор обусловлен тем, что AutoCAD 2009 может вместо 3.0 использовать 3.5, в которой присутствует технология LINQ, активно мною используемая. Версии ниже чем AutoCAD 2009 мне не интересны. Однако тут возникает один нюанс: на исходном компьютере так же должен быть установлен .NET Framework 3.5 SP1.

/* EntryPoint.cs
 * © Andrey Bushman, 2014
 * Поиск и загрузка версии плагина .NET, ARX или VBA, наиболее пригодной для 
 * текущей версии AutoCAD.
 * http://bushman-andrey.blogspot.ru/2014/06/dll-autocad.html
 */
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;

#if AUTOCAD
using cad = Autodesk.AutoCAD.ApplicationServices.Application;
using Rt = Autodesk.AutoCAD.Runtime;
#endif

[assembly: Rt.ExtensionApplication(typeof(Bushman.CAD.EntryPoint
  .EntryPoint))]

namespace Bushman.CAD.EntryPoint {
  /// <summary>
  /// Задачей данного класса является поиск и загрузка в AutoCAD наиболее 
  /// подходящей для него версии плагина.
  /// </summary>
  public sealed class EntryPoint : Rt.IExtensionApplication {
    const String netPluginExtension = ".dll";
    static readonly String[] extensions = new String[] { ".arx"".dvb" };
    static readonly String[] methodNames = new String[] { "LoadArx""LoadDVB" 
    };

    /// <summary>
    /// Код этого метода будет запущен на исполнение при загрузке сборки в 
    /// AutoCAD. В результате его работы происходит попытка найти и загрузить в
    /// AutoCAD наиболее подходящую версию плагина из имеющихся в наличии.
    /// </summary>
    public void Initialize() {
      // Для начала извлекаем информацию о текущей версии AutoCAD и ищем
      // соответствующую ей версию файла. Имя такого файла должно 
      // формироваться по правилу: 
      //    ИмяТекущейСборки.Major.Minor[x86|x64].(dll|arx|dvb).
      // Где <Major> и <Minor> - это значения одноимённых свойств объекта 
      // Version, полученного из Application.Version.
      Version version = cad.Version;

      String fileFullName = GetType().Assembly.Location;

      Version minVersion = new Version(17, 2);

      FileInfo targetDllFullName = FindFile(fileFullName, version, minVersion);

      if(targetDllFullName == null)
        return;

      // Если найден файл, соответствующий нашей версии AutoCAD, то 
      // загружаем его.
      Assembly asm = null;
      try {
        if(targetDllFullName.Extension.Equals(netPluginExtension,
          StringComparison.CurrentCultureIgnoreCase))
          asm = Assembly.LoadFrom(targetDllFullName.FullName);
        else {
          Int32 index = Array.IndexOf(extensions, targetDllFullName.Extension);

          if(index >= 0) {
            Object application = cad.AcadApplication;

            application.GetType().InvokeMember(methodNames[index], BindingFlags
              .InvokeMethod, null, application, new Object[] { 
                targetDllFullName.FullName });
          }
        }
      }
      catch {
      }
    }

    /// <summary>
    /// Получить имя наиболее подходящего файла, для его последующей загрузки в
    /// AutoCAD. Если такой файл не будет найден, то возвращается null.
    /// </summary>
    /// <param name="fileFullName">"Базовое" имя файла, т.е. полное имя 
    /// файла без указания в нём версий ядра и разрядности платформы.</param>
    /// <param name="expectedVersion">Версия AutoCAD, для которой следует 
    /// выполнить поиск соответствующей версии файла.</param>
    /// <param name="minVersion">Наименьшая версия AutoCAD, ниже которой не 
    /// следует выполнять поиск.</param>
    /// <returns>Возвращается FileInfo наиболее подходящего файла, для его 
    /// последующей загрузки в AutoCAD. Если такой файл не будет найден, то 
    /// возвращается null.</returns>
    private FileInfo FindFile(String fileFullName, Version expectedVersion,
      Version minVersion) {

      if(fileFullName == null)
        throw new ArgumentNullException("fileFullName");

      if(fileFullName.Trim() == String.Empty)
        throw new ArgumentException(
          "fileFullName.Trim() == String.Empty");

      if(expectedVersion < minVersion)
        throw new ArgumentException(
          "expectedVersion < minVersion");

      Int32 major = expectedVersion.Major;
      Int32 minor = expectedVersion.Minor;

      String directory = Path.GetDirectoryName(fileFullName);
      String fileName = Path.GetFileNameWithoutExtension(fileFullName);

      String coreString = String.Format("{0}.{1}", major.ToString(),
        minor.ToString());

      String subDirectoryName = "R" + coreString;
      String subDirectoryName_xPlatform = subDirectoryName + (IntPtr.Size == 4
        ? "x86" : "x64");

      String targetFileName = String.Empty;
      String targetFileName_xPlatform = String.Empty;
      String targetFileFullName = String.Empty;
      String targetFileFullName_xPlatform = String.Empty;

      List<String> items = new List<String>(extensions);
      items.Insert(0, netPluginExtension);

      String name = String.Empty;

      foreach(String extension in items) {

        targetFileName = String.Format("{0}.{1}{2}", fileName, coreString,
          extension);
        targetFileName_xPlatform = String.Format("{0}.{1}{2}{3}", fileName,
          coreString, (IntPtr.Size == 4 ? "x86" : "x64"), extension);

        // Сначала выполняем поиск в текущем каталоге
        targetFileFullName = Path.Combine(directory, targetFileName);
        if(File.Exists(targetFileFullName)) {
          name = targetFileFullName;
          break;
        }
        targetFileFullName_xPlatform = Path.Combine(directory,
          targetFileName_xPlatform);
        if(File.Exists(targetFileFullName_xPlatform)) {
          name = targetFileFullName_xPlatform;
          break;
        }

        // Если в текущем каталоге подходящий файл не найден, то продолжаем
        // поиск по соответствующим подкаталогам
        targetFileFullName = directory + "\" + subDirectoryName +
          "\" + targetFileName;
        if(File.Exists(targetFileFullName)) {
          name = targetFileFullName;
          break;
        }

        targetFileFullName_xPlatform = directory + "\" +
          subDirectoryName_xPlatform + "\" + targetFileName_xPlatform;
        if(File.Exists(targetFileFullName_xPlatform)) {
          name = targetFileFullName_xPlatform;
          break;
        }
      }

      // Если найден файл, соответствующий нашей версии AutoCAD, то возвращаем 
      // соответтствующий ему объект FileInfo.
      if(File.Exists(name)) {
        return new FileInfo(name);
      }
      // Если соответствия не найдено, то продолжаем поиск, последовательно 
      // проверяя наличие подходящего файла для более ранних версий AutoCAD
      else {
        if(minor == 0) {
          minor = 3;
          --major;
        }
        else {
          --minor;
        }

        Version version = new Version(major, minor);
        if(version < minVersion)
          return null;
        FileInfo file = FindFile(fileFullName, new Version(major, minor),
          minVersion);
        return file;
      }
    }

    /// <summary>
    /// Код данного метода выполняется при завершении работы AutoCAD.
    /// </summary>
    public void Terminate() {
    }
  }
}

Без рубрики

Стиль точек в AutoCAD

Задачка, что называется "в два действия", но раз уж стал выкладывать информацию по работе с различными стилями в AutoCAD, то покажу и настройку стиля точек (для полноты картины), хотя, скорее всего, это и без моих заметок ни у кого не должно вызывать трудностей.

Весь код сводится к банальному изменению двух свойств (одноимённых соответствующим системным переменным AutoCAD). Информация об этих переменных присутствует в справочной системе AutoCAD и не должна вызывать каких-либо сложностей в понимании.



/* PointStyleSample.cs
 * © Андрей Бушман, 2014 
 * Пример изменения стиля точек.
 */
using System;

using cad = Autodesk.AutoCAD.ApplicationServices
.Application;
using Ap = Autodesk.AutoCAD.ApplicationServices;
using Db = Autodesk.AutoCAD.DatabaseServices;
using Rt = Autodesk.AutoCAD.Runtime;

[assembly: Rt.CommandClass(typeof(Bushman.CAD
  .Samples.Styles.PointStyleSample))]

namespace Bushman.CAD.Samples.Styles {

  public sealed class PointStyleSample {

    [Rt.CommandMethod("ChangePointStyle",
      Rt.CommandFlags.Modal)]
    public void ChangePointStyle() {
      Ap.Document doc = cad.DocumentManager
        .MdiActiveDocument;
      if(doc == null)
        return;
      using(doc.LockDocument()) {
        Db.Database db = doc.Database;
        db.Pdmode = 35;
        db.Pdsize = -10;
      }
    }
  }
}

Текстовые стили в AutoCAD

Заметка содержит пример программного создания и детальной настройки текстового стиля в AutoCAD.


В результате работы кода (команда CreateOrEditTextStyle) будет создан и настроен новый текстовый стиль:


В коде каждая изменяемая нами опция настроек помечена комментарием, содержащим наименование этой опции в англоязычной версии AutoCAD.

/* TextStyleSample.cs
 * AutoCAD 2009-2015
 * © Андрей Бушман, 2014 
 * Пример по работе с текстовым стилем. 
 */
using System;

#if AUTOCAD
using cad = Autodesk.AutoCAD.ApplicationServices
.Application;
using Ap = Autodesk.AutoCAD.ApplicationServices;
using Db = Autodesk.AutoCAD.DatabaseServices;
using Rt = Autodesk.AutoCAD.Runtime;
#endif

[assembly: Rt.CommandClass(typeof(Bushman.CAD
  .Samples.Styles.TextStyleSample))]

namespace Bushman.CAD.Samples.Styles {
  public static class TextStyleSample {
    // Наименование группы команд
    const String ns = "Samples";
    /// <summary>
    /// В текущем чертеже данная команда либо 
    /// создаёт новый текстовый стиль и полностью
    /// настраивает его, либо выполняет его 
    /// перенастройку, если нужный стиль уже 
    /// существует.
    /// </summary>
    [Rt.CommandMethod(ns,
      "CreateOrEditTextStyle",
      Rt.CommandFlags.Modal)]
    public static void CreateOrEditTextStyle() {
      Ap.Document doc = cad.DocumentManager
        .MdiActiveDocument;
      if(doc == null)
        return;
      using(doc.LockDocument()) {
        CreateOrEditTextStyle(doc.Database);
      }
    }

    /// <summary>
    /// В указанной базе данных данная команда 
    /// либо создаёт новый текстовый стиль и 
    /// полностью настраивает его, либо выполняет
    /// его перенастройку, если нужный стиль уже 
    /// существует.
    /// </summary>
    /// <param name="db">База данных, подлежащая 
    /// обработке</param>
    private static void CreateOrEditTextStyle(
      Db.Database db) {
      if(db == null)
        throw new ArgumentNullException("db");
      using(Db.Transaction tr =
        db.TransactionManager.StartTransaction()) 
        {
        Db.TextStyleTable tst =
          (Db.TextStyleTable)tr.GetObject(
          db.TextStyleTableId, Db.OpenMode
          .ForWrite);

        String textStyleName = "Курсив";
        Db.TextStyleTableRecord rec = null;

        // Если нужного текстового стиля нет, то 
        // создаём его
        if(!tst.Has(textStyleName))
          rec = new Db.TextStyleTableRecord();
        else // Если стиль существует, то 
          // открываем его для редактирования.
          rec = tr.GetObject(tst[textStyleName],
            Db.OpenMode.ForWrite)
            as Db.TextStyleTableRecord;

        // ttf или, к примеру, "txt.shx".
        String fontName = "Times New Roman.ttf";
        String bigFontName = String.Empty;
        FillTextStyle(rec, textStyleName,
          fontName, bigFontName,
          Db.AnnotativeStates.NotApplicable, 0,
          0.8, 15, falsefalsefalsefalse);

        // Если это новый текстовый стиль, то не 
        // забываем добавить его в базу данных 
        // чертежа.
        if(rec.Database == null) {
          tst.Add(rec);
          tr.AddNewlyCreatedDBObject(rec, true);
        }
        tr.Commit();
      }
    }

    /// <summary>
    /// Настройка текстового стиля.
    /// </summary>
    /// <param name="rec">Текстовый стиль, 
    /// подлежащий обработке.</param>
    /// <param name="styleName">Имя текстового 
    /// стиля.</param>
    /// <param name="fontName">Наименование 
    /// используемого шрифта: Font Name в 
    /// диалоговом окне Text Style.</param>
    /// <param name="fontStyle">Наименование 
    /// большого шрифта: Font Style в 
    /// диалоговом окне Text Style. Если этому 
    /// свойству назначить непустое 
    /// значение, то в диалоговом окне Text Style 
    /// будет включена опция Use Big 
    /// Font.</param>
    /// <param name="annotativeStatus">Должен ли 
    /// текстовый стиль быть 
    /// аннотативным: True - аннотативен, False 
    /// или NotApplicable - не 
    /// аннотативен. Опция Annotative в 
    /// диалоговом окне Text Style.</param>
    /// <param name="textHeight">Высота текста: 
    /// Paper Text Height в диалоговом 
    /// окне Text Style.</param>
    /// <param name="widthFactor">Коэффициент 
    /// сжатия текста по горизонтали: 
    /// Width Factor в диалоговом окне Text Style
    /// </param>
    /// <param name="obliquingAngle">Угол наклона
    /// текста. Указывается в градусах и 
    /// отсчитывается от вертикали по часовой 
    /// стрелке. Oblique Angle в диалоговом окне 
    /// Text Style.</param>
    /// <param name="setPaperOrientation">Опция 
    /// Match text orientation to layout в 
    /// диалоговом окне Text Style.</param>
    /// <param name="upsideDown">
    /// Опция Upside down в диалоговом окне Text 
    /// Style.</param>
    /// <param name="backwards">
    /// Опция Backwards в диалоговом окне Text 
    /// Style.</param>
    /// <param name="vertical">
    /// Опция Vertical в диалоговом окне Text 
    /// Style.</param>
    private static void FillTextStyle(
      Db.TextStyleTableRecord rec,
      String styleName, String fontName,
      String fontStyle,
      Db.AnnotativeStates annotativeStatus,
      Double textHeight,
      Double widthFactor, Double obliquingAngle,
      Boolean setPaperOrientation,
      Boolean upsideDown, Boolean backwards,
      Boolean vertical) {

      if(rec == null)
        throw new ArgumentNullException("rec");

      if(styleName == null)
        throw new ArgumentNullException(
          "styleName");
      if(fontName == null)
        throw new ArgumentNullException(
          "fontName");
      if(fontStyle == null)
        throw new ArgumentNullException(
          "fontStyle");

      if(styleName.Trim() == String.Empty)
        throw new ArgumentException("styleName");
      if(fontName.Trim() == String.Empty)
        throw new ArgumentException("fontName");
      if(fontStyle.Length > 0 &&
        fontStyle.Trim() == String.Empty)
        throw new ArgumentException("fontStyle");

      rec.Name = styleName;
      rec.Annotative = annotativeStatus;
      rec.FileName = fontName;
      rec.XScale = widthFactor;
      // Переводим градусы в радианы
      rec.ObliquingAngle = obliquingAngle *
        Math.PI / 180;
      rec.SetPaperOrientation(
        setPaperOrientation);

      // Если мы назначаем значение свойству 
      // BigFontFileName, то в диалоговом окне 
      // Text Style автоматически включается 
      // галочка "Use Big Font"
      rec.BigFontFileName = fontStyle;

      // эти флаги хранят значения опций "Upside 
      // down" и "Backwards"
      rec.FlagBits = (Byte)0;

      rec.FlagBits += upsideDown ? (Byte)2 :
        (Byte)0; // Upside down      
      rec.FlagBits += backwards ? (Byte)4 :
        (Byte)0; // Backwards

      rec.TextSize = textHeight;
      rec.IsVertical = vertical;
    }
  }
}

Стили мультилиний в AutoCAD

Заметка содержит пример программного создания и детальной настройки стиля мультилиний в AutoCAD.

В результате работы кода (команда CreateMultilineStyle) будет создан новый стиль мультилиний:



В коде каждая изменяемая нами опция настроек помечена комментарием, содержащим наименование этой опции в англоязычной версии AutoCAD.

/* MultilineStyleSample.cs
 * © Андрей Бушман, 2014 
 * Пример создания стиля мультилиний.
 */
using System;

using cad = Autodesk.AutoCAD.ApplicationServices
.Application;
using Ap = Autodesk.AutoCAD.ApplicationServices;
using Db = Autodesk.AutoCAD.DatabaseServices;
using Ed = Autodesk.AutoCAD.EditorInput;
using Rt = Autodesk.AutoCAD.Runtime;
using Clr = Autodesk.AutoCAD.Colors;

[assembly: Rt.CommandClass(typeof(Bushman.CAD
  .Samples.Styles.MultilineStyleSample))]

namespace Bushman.CAD.Samples.Styles {

  public class MultilineStyleSample {

    [Rt.CommandMethod("CreateMultilineStyle"
      Rt.CommandFlags.Modal)]
    public void createmlinestyle() {

      Ap.Document doc = cad.DocumentManager
        .MdiActiveDocument;
      if(doc == null)
        return;
      Ed.Editor ed = doc.Editor;
      Db.Database db = doc.Database;

      using(doc.LockDocument()) {
        using(Db.Transaction tr =
        db.TransactionManager.StartTransaction()) 
        {

          Db.DBDictionary mlDict =
            (Db.DBDictionary)tr.GetObject(
            db.MLStyleDictionaryId,
            Db.OpenMode.ForWrite);

          String mlineStyleName = "Пример";

          if(!mlDict.Contains(mlineStyleName)) {

            Db.MlineStyle mlineStyle =
              new Db.MlineStyle();
            // Multiline Style name
            mlineStyle.Name = mlineStyleName;
            mlineStyle.Description =
              "Некоторое описание";

            mlDict.SetAt(mlineStyleName,
              mlineStyle);
            tr.AddNewlyCreatedDBObject(
              mlineStyle, true);

            Double angleGrad = 90.0;
            Double angleRadian = angleGrad
              * Math.PI / 180; // angle

            // Start line
            mlineStyle.StartSquareCap = true;
            // Start Outer arcs
            mlineStyle.StartRoundCap = true;
            // Start Inner arcs
            mlineStyle.StartInnerArcs = true;
            // Start angle
            mlineStyle.StartAngle = angleRadian;

            // End line
            mlineStyle.EndSquareCap = true;
            // End Outer arcs
            mlineStyle.EndRoundCap = true;
            // End Inner arcs
            mlineStyle.EndInnerArcs = true;
            // End angle
            mlineStyle.EndAngle = angleRadian;

            Clr.Color color = Clr.Color.FromRgb(
              255, 0, 0);

            // Fill color
            mlineStyle.Filled = true;
            mlineStyle.FillColor = color;

            // Display joints
            mlineStyle.ShowMiters = true;

            Db.MlineStyleElement element =
              new Db.MlineStyleElement(0.15, 
                color, db.Celtype);
            // First element
            mlineStyle.Elements.Add(element, true
              );

            element = new Db.MlineStyleElement(
              -0.15, color, db.Celtype);

            mlineStyle.Elements.Add(element, 
              false); // Second element
          }
          tr.Commit();
        }
      }
    }
  }
}

Стили мультивыносок в AutoCAD

В заметке показан пример программного создания и детальной настройки стиля мультивыносок в AutoCAD. В коде каждая изменяемая нами опция настроек помечена комментарием, содержащим наименование этой опции в англоязычной версии AutoCAD.
В результате работы кода будет создан и настроен новый стиль мультивыноски:



В коде определена команда CreateMultileaderStyle, выполняющая всю необходимую процедуру.

/* MLeaderStyleSample.cs
 * © Андрей Бушман, 2014 
 * Пример создания и настройки стиля мультивыно-
 * сок.
 */
using System;
using cad = Autodesk.AutoCAD.ApplicationServices
.Application;
using Ap = Autodesk.AutoCAD.ApplicationServices;
using Db = Autodesk.AutoCAD.DatabaseServices;
using Ed = Autodesk.AutoCAD.EditorInput;
using Gm = Autodesk.AutoCAD.Geometry;
using Rt = Autodesk.AutoCAD.Runtime;
using Clr = Autodesk.AutoCAD.Colors;

[assembly: Rt.CommandClass(typeof(Bushman.CAD
  .Samples.Styles.MLeaderStyleSample))]

namespace Bushman.CAD.Samples.Styles {
  public class MLeaderStyleSample {
    [Rt.CommandMethod("CreateMultileaderStyle",
      Rt.CommandFlags.Modal)]
    public void CreatingMleaderStyle() {
      Ap.Document doc = cad.DocumentManager
        .MdiActiveDocument;
      if(doc == null)
        return;
      Ed.Editor ed = doc.Editor;
      Db.Database db = doc.Database;

      using(Db.Transaction tr =
        db.TransactionManager.StartTransaction()) {
        Db.DBDictionary mleaderStylesDict =
          tr.GetObject(
          db.MLeaderStyleDictionaryId,
            Db.OpenMode.ForRead) as
            Db.DBDictionary;

        const String mleaderStyleName = "Точка";

        Db.MLeaderStyle mleaderStyle;
        if(mleaderStylesDict.Contains(
          mleaderStyleName))
          mleaderStyle = (Db.MLeaderStyle)tr
            .GetObject(
            (Db.ObjectId)mleaderStylesDict[
            mleaderStyleName],
            Db.OpenMode.ForWrite);
        else {
          mleaderStyle = new Db.MLeaderStyle();
          Db.ObjectId mleaderStyleId =
            mleaderStyle.PostMLeaderStyleToDb(db,
            mleaderStyleName);
          tr.AddNewlyCreatedDBObject(
            mleaderStyle, true);
        }

        // Ниже рассмотрены все настройки 
        // диалогового окна "Modify Multileader 
        // Style". Все наименования соответствуют
        // английской версии AutoCAD.

        // "Вкладка Leader Format" ***

        // Группа "General":

        mleaderStyle.LeaderLineType =
          Db.LeaderType.StraightLeader; // Type
        mleaderStyle.LeaderLineColor =
          Clr.Color.FromColorIndex(
          Clr.ColorMethod.ByLayer, 256); // Color
        mleaderStyle.LeaderLineTypeId =
          db.ByBlockLinetype; // Linetype

        // Lineweight
        mleaderStyle.LeaderLineWeight =
          Db.LineWeight.LineWeight025;

        // Группа "Arrowhead":

        Db.BlockTable blockTable = tr.GetObject(
          db.BlockTableId, Db.OpenMode.ForRead)
          as Db.BlockTable;
        if(!blockTable.Has("_DOT")) {
          // Загружаем определение блока "_DOT"
          cad.SetSystemVariable("DIMBLK""_DOT")
            ;
        }
        mleaderStyle.ArrowSymbolId = blockTable[
          "_DOT"]; // Symbol
        mleaderStyle.ArrowSize = 1; // Size

        // Группа "Leader break":

        // Break size
        mleaderStyle.BreakSize = 2;

        // Вкладка "Leader Structure" ***

        // Группа "Constrains":

        // Maximum leader points
        mleaderStyle.MaxLeaderSegmentsPoints = 2;

        // First segment angle
        mleaderStyle
          .FirstSegmentAngleConstraint =
          Db.AngleConstraint.DegreesAny;

        // Примечание для First segment angle:

        // Опция выбрана и значение равно 0:
        // mleaderStyle        
        //  .FirstSegmentAngleConstraint = 
        //  Db.AngleConstraint.DegreesHorz; 

        // Опция не выбрана и значение равно 0:
        // mleaderStyle        
        //  .FirstSegmentAngleConstraint = 
        //  Db.AngleConstraint.DegreesAny; 

        // Second segment angle
        mleaderStyle
          .SecondSegmentAngleConstraint =
          Db.AngleConstraint.DegreesAny;

        // Примечание для Second segment angle:

        // Опция выбрана и значение равно 0:
        // mleaderStyle
        //  .SecondSegmentAngleConstraint = 
        //  Db.AngleConstraint.DegreesHorz;

        // Опция не выбрана и значение равно 0:
        // mleaderStyle
        //  .SecondSegmentAngleConstraint = 
        //  Db.AngleConstraint.DegreesAny; 

        // Группа "Landing settings":

        // Автору кода не удалось добраться до 
        // опций "Automatically include landing" 
        // и "Set landing distance", а так же до
        // его значения ни через .NET, ни через 
        // COM.             

        // Группа "Scale":

        // Annotative
        mleaderStyle.Annotative =
          Db.AnnotativeStates.True;

        // Scale multileaders to layout
        // mleaderStyle.Scale = 0; 

        // Specify scale
        mleaderStyle.Scale = 1;


        // Вкладка "Content" ***               

        // *** Если для "Multileader type" выбран
        // вариант "MText": ***

        // Multileader type
        mleaderStyle.ContentType =
          Db.ContentType.MTextContent;

        // Создадим новый текстовый стиль для
        // использования его в нашем стиле
        // мультивыноски
        Db.TextStyleTable tst =
          (Db.TextStyleTable)tr.GetObject(
          db.TextStyleTableId,
          Db.OpenMode.ForWrite);
        Db.TextStyleTableRecord textStyle =
          new Db.TextStyleTableRecord();
        textStyle.Name = "Тип А прямой";
        textStyle.FileName = "Arial.ttf";
        textStyle.XScale = 0.75;
        tst.Add(textStyle);
        tr.AddNewlyCreatedDBObject(textStyle,
          true);

        // Группа "Text options":
        Db.MText defaultMText = new Db.MText();
        defaultMText.SetDatabaseDefaults();

#if AUTOCAD_2009
        defaultMText.TextStyle = 
          textStyle.ObjectId;
#else
        defaultMText.TextStyleId =
          textStyle.ObjectId;
#endif

        defaultMText.Contents = String.Empty;
        // Default text
        mleaderStyle.DefaultMText = defaultMText;

        // Text style
        mleaderStyle.TextStyleId =
          textStyle.ObjectId;
        // Text angle
        mleaderStyle.TextAngleType =
          Db.TextAngleType.HorizontalAngle;
        // Text color
        mleaderStyle.TextColor =
          Clr.Color.FromColorIndex(
          Clr.ColorMethod.ByBlock, 256);
        // Text height
        mleaderStyle.TextHeight = 3.5;
        // Alwais left justify
        mleaderStyle.TextAlignAlwaysLeft = false;
        // Frame text
        mleaderStyle.EnableFrameText = false;

        // Группа "Leader connection":
        mleaderStyle.TextAttachmentType =
          Db.TextAttachmentType.AttachmentMiddle;

        // Left attachment
        mleaderStyle.SetTextAttachmentType(
          Db.TextAttachmentType
          .AttachmentBottomOfTopLine,
          Db.LeaderDirectionType.LeftLeader);

        // Right attachment
        mleaderStyle.SetTextAttachmentType(
          Db.TextAttachmentType
          .AttachmentBottomOfTopLine,
            Db.LeaderDirectionType.RightLeader);

        // Landing gap 
        mleaderStyle.LandingGap = 1;

#if !AUTOCAD_2009
        // AutoCAD 2009 не имеет следующих 
        // свойств:

        // Horisontal attachment:
        mleaderStyle.TextAttachmentDirection =
         Db.TextAttachmentDirection
         .AttachmentHorizontal;

        // Внимание! если в настройках мы 
        // устанавливаем вариант "Horisontal 
        // attachment", то для установки опций
        // "Left attachment" и "Right attachment"
        // нужно методу SetTextAttachmentType
        // вторым аргументом передавать значения
        // LeftLeader и RightLeader 
        // сооветственно:

        // Left attachment
        mleaderStyle.SetTextAttachmentType(
          Db.TextAttachmentType
          .AttachmentBottomOfTopLine,
          Db.LeaderDirectionType.LeftLeader);
        // Right attachment
        mleaderStyle.SetTextAttachmentType(
          Db.TextAttachmentType
          .AttachmentBottomOfTopLine,
          Db.LeaderDirectionType.RightLeader);

        // или:
        // Vertical attachment:
        // mleaderStyle.TextAttachmentDirection =
        // Db.TextAttachmentDirection
        // .AttachmentVertical;

        // Внимание! если в настройках мы 
        // устанавливаем вариант "Vertical 
        // attachment", то для установки опций
        // "Top attachment" и "Bottom attachment"
        // нужно методу SetTextAttachmentType
        // вторым аргументом передавать значения
        // TopLeader и BottomLeader 
        // сооветственно:

        // Top attachment
        // mleaderStyle.SetTextAttachmentType(
        //  Db.TextAttachmentType
        //  .AttachmentCenter,
        //  Db.LeaderDirectionType.TopLeader);

        // Bottom attachment
        // mleaderStyle.SetTextAttachmentType(
        //  Db.TextAttachmentType
        //  .AttachmentCenter,
        //  Db.LeaderDirectionType.BottomLeader);

        // Extend leader to text
        mleaderStyle.ExtendLeaderToText = false;
#endif

        // В этом блоке показаны варианты "Block"
        // и "None": 
#if OTHER_VARIANTS_OF_MULTILEADER_TYPE
        // *** Если для "Multileader type" выбран
        // вариант "Block" ***

        // Multileader type
        mleaderStyle.ContentType =
          Db.ContentType.BlockContent;

        // Группа "Block options":     

        // Создаём нужное нам определение блока
        String blockName = "Блок выноски";
        try {
          Db.SymbolUtilityServices
            .ValidateSymbolName(blockName, false)
            ;
          Db.BlockTable bt = tr.GetObject(
            db.BlockTableId,
            Db.OpenMode.ForWrite) as
            Db.BlockTable;

          Db.ObjectId blockId = Db.ObjectId.Null;

          if(bt.Has(blockName))
            blockId = bt[blockName];
          else {
            Db.BlockTableRecord block =
              new Db.BlockTableRecord();
            block.Name = blockName;
            block.Annotative =
              Db.AnnotativeStates.True;
            bt.Add(block);
            tr.AddNewlyCreatedDBObject(block,
              true);
            // Пусть наш блок выноски будет пред-
            // ставлен окружностью с текстовым
            // атрибутом

            // Окружность
            Db.Circle circle = new Db.Circle();
            circle.Center = new Gm.Point3d(0, 0,
              0);
            circle.Radius = 10.0;
            circle.SetDatabaseDefaults();
            block.AppendEntity(circle);
            tr.AddNewlyCreatedDBObject(circle,
              true);

            // Определение атрибута
            Db.AttributeDefinition attDef =
              new Db.AttributeDefinition();
            attDef.Position = new Gm.Point3d(0,
              0, 0);
            attDef.Tag = "Текст выноски";
#if AUTOCAD_2009
            attDef.TextStyle = db.Textstyle;
#else
            attDef.TextStyleId = db.Textstyle;
#endif
            attDef.Prompt = "Значение атрибута";
            attDef.Preset = true;
            attDef.TextString = String.Empty;
            attDef.Justify = Db.AttachmentPoint
              .MiddleCenter;
            attDef.Height = 5;
            attDef.Annotative =
              Db.AnnotativeStates.True;
            block.AppendEntity(attDef);
            tr.AddNewlyCreatedDBObject(attDef,
              true);
            blockId = block.ObjectId;
          }
          // Source block
          mleaderStyle.BlockId = blockId;
          Gm.Scale3d scale3d = new Gm.Scale3d(
            3.35, 3.35, 3.35);
          // Scale
          mleaderStyle.BlockScale = scale3d;
          // Attachment       
          mleaderStyle.BlockConnectionType =
            Db.BlockConnectionType.ConnectBase;
          // Block color
          mleaderStyle.BlockColor = Clr.Color
            .FromColorIndex(Clr.ColorMethod
            .ByBlock, 256);

          // ***
        }
        catch(Exception ex) {
          ed.WriteMessage("Exception: {0}n",
            ex.Message);
        }

        // *** Если для "Multileader type" выбран
        // вариант "None" ***

        // Multileader type
        mleaderStyle.ContentType =
          Db.ContentType.NoneContent;

        // Вариант "None" не содержит никаких 
        // дополнительных настроек.
#endif
        tr.Commit();
      }
    }
  }
}

Табличные стили в AutoCAD

В заметке показан пример создания и детальной настройки базового табличного стиля. Затем, на его основе создан производный табличный стиль к которому применён пользовательский шаблон таблиц (так же созданный нами в коде). После создания и настройки табличных стилей, в коде продемонстрированы примеры создания таблиц на основе обозначенных выше стилей.

В результате работы кода будут созданы два табличных стиля:


В составе базового стиля создаётся дополнительный набор стилей ячеек и происходит подробная настройка каждой опции стиля:


На основе базового стиля создаётся производный стиль и к нему применяется шаблон:


В коде так же определены две команды (CreateTable и CreateTable2), при помощи которых создаются таблицы на основе созданных нами табличных стилей. При запуске этих команд необходимые стили создаются автоматически, если они ещё отсутствуют в базе данных чертежа. Помимо этого, в коде определено две команды, создающие наши стили: BaseTableStyle и TableStyleWithTemplate.


В коде каждая изменяемая нами опция настроек помечена комментарием, содержащим наименование этой опции в англоязычной версии AutoCAD.

   1:  /* TableStyleSample.cs
   2:   * AutoCAD 2009-2015
   3:   * © Андрей Бушман, 2014  
   4:   * Пример создания табличных стилей (с шаблоном и
   5:   * без), а так же таблиц, использующих эти стили.
   6:   * 
   7:   * Команды, определённые в данном коде: 
   8:   * - CreateTable - создать таблицу на основе 
   9:   *    базового табличного стиля.
  10:   *    
  11:   * - CreateTable2 - создать таблицу на основе 
  12:   *    шаблонного табличного стиля.
  13:   *    
  14:   * - BaseTableStyle - создать базовый табличный 
  15:   *    стиль.
  16:   *    
  17:   * - TableStyleWithTemplate - создать табличный 
  18:   *    стиль на основе шаблона таблицы.
  19:   */
  20:  using System;
  21:  using System.Collections.Generic;
  22:  using System.Linq;
  23:  using System.Text;
  24:  using System.Reflection;
  25:   
  26:  #if AUTOCAD
  27:  using cad = Autodesk.AutoCAD.ApplicationServices
  28:  .Application;
  29:  using Ap = Autodesk.AutoCAD.ApplicationServices;
  30:  using Db = Autodesk.AutoCAD.DatabaseServices;
  31:  using Ed = Autodesk.AutoCAD.EditorInput;
  32:  using Rt = Autodesk.AutoCAD.Runtime;
  33:  using Gm = Autodesk.AutoCAD.Geometry;
  34:  using Clr = Autodesk.AutoCAD.Colors;
  35:  using Wn = Autodesk.AutoCAD.Windows;
  36:  using Hs = Autodesk.AutoCAD.DatabaseServices
  37:  .HostApplicationServices;
  38:  using Us = Autodesk.AutoCAD.DatabaseServices
  39:  .SymbolUtilityServices;
  40:  using Int = Autodesk.AutoCAD.Interop;
  41:  using Com = Autodesk.AutoCAD.Interop.Common;
  42:  #endif // #if AUTOCAD
  43:   
  44:  [assembly: Rt.CommandClass(typeof(
  45:    Bushman.CAD.Samples.Styles.TableStyleSample))]
  46:   
  47:  namespace Bushman.CAD.Samples.Styles {
  48:   
  49:    public class TableStyleSample {
  50:      // Наименование группы команд
  51:      const String group = "Samples";
  52:      const String textStyleName = "Текст таблиц";
  53:      const String tableStyleName = "Базовый";
  54:      const String tableStyle2Name =
  55:        "Спецификация металла";
  56:   
  57:      [Rt.CommandMethod(group, "BaseTableStyle",
  58:        Rt.CommandFlags.Modal)]
  59:      public void BaseTableStyle_Command() {
  60:        Ap.Document doc = cad.DocumentManager
  61:          .MdiActiveDocument;
  62:        if(doc == null)
  63:          return;
  64:        Db.Database db = doc.Database;
  65:        Ed.Editor ed = doc.Editor;
  66:        using(doc.LockDocument()) {
  67:          Int.AcadDocument activeDocument =
  68:            default(Int.AcadDocument);
  69:  #if !NEWER_THAN_AUTOCAD_2009
  70:          activeDocument = (Int.AcadDocument)cad
  71:            .DocumentManager.MdiActiveDocument
  72:            .AcadDocument;
  73:  #else
  74:          activeDocument = (Int.AcadDocument)
  75:            Ap.DocumentExtension.GetAcadDocument(
  76:            cad.DocumentManager.MdiActiveDocument);
  77:  #endif
  78:          using(Db.Transaction tr =
  79:            db.TransactionManager.StartTransaction(
  80:            )) {
  81:   
  82:            // Создаём новый текстовый стиль, 
  83:            // который будем использовать в нашем
  84:            // табличном стиле         
  85:            Db.TextStyleTable tst = tr.GetObject(
  86:              db.TextStyleTableId, Db.OpenMode
  87:              .ForWrite) as Db.TextStyleTable;
  88:            Db.ObjectId textStyleId = Db.ObjectId
  89:              .Null;
  90:   
  91:            if(tst.Has(textStyleName)) {
  92:              textStyleId = tst[textStyleName];
  93:            }
  94:            else {
  95:              Db.TextStyleTableRecord textStyle =
  96:                new Db.TextStyleTableRecord();
  97:              textStyle.Name = textStyleName;
  98:              // ttf или, к примеру, shx файл.
  99:              textStyle.FileName = "Arial.ttf";
 100:              textStyle.XScale = 0.75;
 101:              tst.Add(textStyle);
 102:              tr.AddNewlyCreatedDBObject(textStyle,
 103:                true);
 104:              textStyleId = textStyle.ObjectId;
 105:            }
 106:   
 107:            Db.DBDictionary tableStylesDictionary =
 108:              tr.GetObject(
 109:                db.TableStyleDictionaryId,
 110:                Db.OpenMode.ForWrite) as
 111:                Db.DBDictionary;
 112:   
 113:            Db.TableStyle tableStyle;
 114:            Db.ObjectId tableStyleId = Db.ObjectId
 115:              .Null;
 116:   
 117:            // Если табличный стиль с таким именем 
 118:            // уже существует, то завершаем работу 
 119:            // метода.
 120:            if(tableStylesDictionary.Contains(
 121:              tableStyleName)) {
 122:              return;
 123:            }
 124:            // Если нужный нам табличный стиль 
 125:            // отсутствует, то создаём его.
 126:            else {
 127:              tableStyle = new Db.TableStyle();
 128:              tableStyleId = tableStylesDictionary
 129:                .SetAt(tableStyleName, tableStyle);
 130:              tr.AddNewlyCreatedDBObject(
 131:                tableStyle, true);
 132:            }
 133:   
 134:            // Некоторые операции будут выполняться 
 135:            // через COM, т.к. некоторые настройки 
 136:            // автору этого кода удалось выполнить 
 137:            // только через COM.
 138:  #if !NEWER_THAN_AUTOCAD_2009
 139:            Com.IAcadTableStyle2 customTableStyle =
 140:              (Com.IAcadTableStyle2)tableStyle
 141:              .AcadObject;
 142:  #else
 143:            Com.IAcadTableStyle customTableStyle =
 144:              (Com.IAcadTableStyle)tableStyle
 145:              .AcadObject;
 146:  #endif
 147:            // Табличный стиль создан. Теперь нужно
 148:            // выполнить его настройку. Свойство 
 149:            // Table direction в диалоговом окне 
 150:            // Modify Table Style.
 151:            customTableStyle.FlowDirection =
 152:              Com.AcTableDirection
 153:              .acTableTopToBottom;
 154:   
 155:            // Примечание к нашему табличному стилю
 156:            tableStyle.Description =
 157:              "Этот табличный стиль создан " +
 158:              "программно.";
 159:   
 160:            // Большинство настроек табличного 
 161:            // стиля хранятся в настройках стилей 
 162:            // ячеек (Cell styles). Каждый стиль 
 163:            // ячеек содержит настройки, которые в 
 164:            // диалоговом окне Modify Table Style 
 165:            // отображаются на вкладках General, 
 166:            // Text и Borders. 
 167:   
 168:            // Работа с пользовательскими стилями 
 169:            // ячеек выполняется через COM API:
 170:            CreateCustomCellStyles(tableStyle,
 171:              textStyleId, db.ContinuousLinetype);
 172:            // Работа со встроенными стилями ячеек 
 173:            // возможна как через COM API, так и 
 174:            // через .NET API:
 175:            InitializeEmbededCellStyles(tableStyle,
 176:              textStyleId);
 177:            tr.Commit();
 178:          }
 179:        }
 180:      }
 181:   
 182:      [Rt.CommandMethod(group,
 183:        "TableStyleWithTemplate",
 184:        Rt.CommandFlags.Modal)]
 185:      public void TableStyleWithTemplate_Command() {
 186:        Ap.Document doc = cad.DocumentManager
 187:          .MdiActiveDocument;
 188:        if(doc == null)
 189:          return;
 190:        Db.Database db = doc.Database;
 191:        Ed.Editor ed = doc.Editor;
 192:        using(doc.LockDocument()) {
 193:          Int.AcadDocument activeDocument =
 194:            default(Int.AcadDocument);
 195:  #if !NEWER_THAN_AUTOCAD_2009
 196:          activeDocument = (Int.AcadDocument)
 197:            cad.DocumentManager.MdiActiveDocument
 198:            .AcadDocument;
 199:  #else
 200:          activeDocument = (Int.AcadDocument)
 201:            Ap.DocumentExtension.GetAcadDocument(
 202:            cad.DocumentManager.MdiActiveDocument);
 203:  #endif
 204:          using(Db.Transaction tr =
 205:            db.TransactionManager.StartTransaction(
 206:            )) {
 207:            Db.DBDictionary tableStylesDictionary =
 208:              tr.GetObject(
 209:              db.TableStyleDictionaryId,
 210:              Db.OpenMode.ForWrite) as
 211:              Db.DBDictionary;
 212:   
 213:            Db.TableStyle tableStyle = null;
 214:            Db.ObjectId tableStyleId =
 215:              Db.ObjectId.Null;
 216:   
 217:            // Если целевой табличный стиль уже 
 218:            // существует, то завершаем работу 
 219:            // метода
 220:            if(tableStylesDictionary.Contains(
 221:              tableStyle2Name)) {
 222:              return;
 223:            }
 224:   
 225:            // Если базовый табличный стиль с таким 
 226:            // именем отсутствует, то создаём его
 227:            if(!tableStylesDictionary.Contains(
 228:              tableStyleName)) {
 229:              BaseTableStyle_Command();
 230:            }
 231:            tableStyleId = tableStylesDictionary
 232:              .GetAt(tableStyleName);
 233:            tableStyle = tr.GetObject(tableStyleId,
 234:              Db.OpenMode.ForWrite) as
 235:              Db.TableStyle;
 236:   
 237:  #if !NEWER_THAN_AUTOCAD_2009
 238:            Com.IAcadTableStyle2 customTableStyle =
 239:              (Com.IAcadTableStyle2)tableStyle
 240:              .AcadObject;
 241:  #else
 242:            Com.IAcadTableStyle customTableStyle =
 243:              (Com.IAcadTableStyle)tableStyle
 244:              .AcadObject;
 245:  #endif
 246:            // Теперь посмотрим, как табличному 
 247:            // стилю назначать в качестве шаблона 
 248:            // некоторую таблицу. Т.е. как 
 249:            // программно сделать то, что делает 
 250:            // пользователь при помощи кнопки 
 251:            // "select table to start from"
 252:            // в диалоговом окне "Modify Table 
 253:            // Style".
 254:   
 255:            // Шаг 1: Сначала создаём таблицу, 
 256:            // которая должна применяться в 
 257:            // качестве шаблона.
 258:            Db.Table table = new Db.Table();
 259:            table.SetDatabaseDefaults();
 260:            table.TableStyle = tableStyleId;
 261:            table.Position = new Gm.Point3d(0, 0, 0
 262:              );
 263:            const Int32 columnsCount = 8;
 264:            const Int32 rowsCount = 11;
 265:            table.SetSize(columnsCount, rowsCount);
 266:            table.Height = 15;
 267:            table.Width = 20;
 268:            table.ColorIndex = 5;
 269:   
 270:            // Заполняем таблицу...
 271:            String[,] str = new String[
 272:              columnsCount, rowsCount];
 273:            // Для начала инициализируем всё 
 274:            // пустыми значениями, дабы массив в
 275:            // дальнейшем можно было обрабатывать в 
 276:            // цикле
 277:            for(Int32 i = 0; i < columnsCount; i++) {
 278:              for(int j = 0; j < rowsCount; j++) {
 279:                str[i, j] = String.Empty;
 280:              }
 281:            }
 282:   
 283:            // Теперь заполняем конкретные, 
 284:            // интересующие нас ячейки
 285:            str[0, 0] = "Спецификация металла";
 286:            str[1, 0] = "Марка";
 287:            str[1, 1] = "Поз.";
 288:            str[1, 2] = "Наименование";
 289:            str[1, 3] = "Сечение";
 290:            str[1, 5] = "Длина";
 291:            str[1, 6] = "Кол.";
 292:            str[1, 7] = "Масса, кг";
 293:            str[2, 7] = "ед.";
 294:            str[2, 8] = "общ.";
 295:            str[1, 9] = "Материал";
 296:            str[1, 10] = "Приме- чание";
 297:            str[2, 3] = "t";
 298:            str[2, 4] = @"A1;{A, смH0.7x;S2^;}";
 299:   
 300:            for(int i = 0; i < columnsCount; i++) {
 301:              for(int j = 0; j < rowsCount; j++) {
 302:  #if !NEWER_THAN_AUTOCAD_2009
 303:                table.SetColumnWidth(j, 60);
 304:                table.SetTextHeight(i, j, 5);
 305:                table.SetTextString(i, j, str[i, j]
 306:                );
 307:                table.SetAlignment(i, j, 
 308:                Db.CellAlignment.MiddleCenter);
 309:  #else
 310:                // Так можно назначить высоту 
 311:                // текста в конкретной ячейке:
 312:                // table.Cells[i, j].TextHeight = 
 313:                // 5;
 314:   
 315:                // Назначаем значение конкретной 
 316:                // ячейке
 317:                table.Cells[i, j].SetValue(str[
 318:                  i, j],
 319:                  Db.ParseOption.ParseOptionNone);
 320:                // Так можно выровнять содержимое 
 321:                // конкретной ячейки
 322:                // table.Cells[i, j].Alignment = 
 323:                // Db.CellAlignment.MiddleCenter;
 324:  #endif
 325:              }
 326:  #if !NEWER_THAN_AUTOCAD_2009
 327:              table.SetRowHeight(i, 10.0);
 328:   
 329:              table.SetColumnWidth(0, 10);
 330:              table.SetColumnWidth(1, 10);
 331:              table.SetColumnWidth(2, 50);
 332:              table.SetColumnWidth(3, 10);
 333:              table.SetColumnWidth(4, 10);
 334:              table.SetColumnWidth(5, 10);
 335:              table.SetColumnWidth(6, 10);
 336:              table.SetColumnWidth(7, 15);
 337:              table.SetColumnWidth(8, 15);
 338:              table.SetColumnWidth(9, 25);
 339:              table.SetColumnWidth(10, 15);
 340:  #else
 341:              // При желании можно назначать 
 342:              // настройки строкам или столбцам:
 343:              // Наименование таблицы
 344:              // table.Rows[0].TextHeight = 5;
 345:              //Заголовок
 346:              // table.Rows[1].TextHeight = 3.5;
 347:              // table.Rows[2].TextHeight = 3.5;
 348:              // однако предпочтительней 
 349:              // пользоваться настройками стилей 
 350:              // ячеек.
 351:   
 352:              table.Rows[i].Height = 8;
 353:              table.Columns[0].Width = 10;
 354:              table.Columns[1].Width = 10;
 355:              table.Columns[2].Width = 50;
 356:              table.Columns[3].Width = 10;
 357:              table.Columns[4].Width = 10;
 358:              table.Columns[5].Width = 15;
 359:              table.Columns[6].Width = 10;
 360:              table.Columns[7].Width = 15;
 361:              table.Columns[8].Width = 15;
 362:              table.Columns[9].Width = 25;
 363:              table.Columns[10].Width = 15;
 364:  #endif
 365:            }
 366:   
 367:  #if !NEWER_THAN_AUTOCAD_2009
 368:            // Автору кода не удалось найти способ 
 369:            // назначить стиль ячеек строкам, а не 
 370:            // ячейкам для AutoCAD 2009. 
 371:   
 372:            // Автору кода не удалось найти способ 
 373:            // задать выравнивание столбцам для 
 374:            // AutoCAD 2009 
 375:   
 376:            // Дополнительные настройки следует 
 377:            // выполнять после того, как ячейкам, 
 378:            // строкам и столбцам будут назначены 
 379:            // нужные стили. Иначе все изменения 
 380:            // будут переопределены настройками 
 381:            // стилей ячеек.
 382:            // Например, назначаем поворот 
 383:            // содержимого некоторых ячеек в 
 384:            // заголовке таблицы:
 385:            table.SetTextRotation(1, 0, 
 386:              Db.RotationAngle.Degrees090);
 387:            table.SetTextRotation(1, 1, 
 388:              Db.RotationAngle.Degrees090);
 389:  #else
 390:            // Назначаем выравнивание столбцам
 391:            for(Int32 i = 0; i < columnsCount; i++) {
 392:              table.Columns[i].Alignment =
 393:                Db.CellAlignment.MiddleCenter;
 394:            }
 395:   
 396:            // Назначаем выравнивание строкам 
 397:            // (наименованию таблицы и заголовку)
 398:            for(Int32 i = 0; i < 3; i++) {
 399:              table.Rows[i].Alignment =
 400:                Db.CellAlignment.MiddleCenter;
 401:              if(i == 0)
 402:                table.Rows[i].Style = "_TITLE";
 403:              else
 404:                table.Rows[i].Style = "_HEADER";
 405:            }
 406:            // Дополнительные настройки следует 
 407:            // выполнять после того, как
 408:            // ячейкам, строкам и столбцам будут 
 409:            // назначены нужные стили. 
 410:            // Иначе все изменения будут 
 411:            // переопределены настройками стилей 
 412:            // ячеек. Например, назначаем поворот 
 413:            // содержимого некоторых ячеек в 
 414:            // заголовке таблицы:
 415:            table.Cells[1, 0].Contents[0].Rotation
 416:              = 90.0 * Math.PI / 180;
 417:            table.Cells[1, 1].Contents[0].Rotation
 418:              = 90.0 * Math.PI / 180;
 419:  #endif
 420:   
 421:  #if !NEWER_THAN_AUTOCAD_2009
 422:            Db.CellRange rng = new Db.CellRange(2, 
 423:              0, 3, 1);
 424:   
 425:            // Вертикальное объединение ячеек
 426:            Db.CellRange rngMark = new Db.CellRange
 427:              (1, 0, 2, 0);
 428:            Db.CellRange rngPos = new Db.CellRange
 429:              (1, 1, 2, 1);
 430:            Db.CellRange rngName = new Db.CellRange
 431:              (1, 2, 2, 2);
 432:            Db.CellRange rngCount = 
 433:              new Db.CellRange(1, 6, 2, 6);
 434:            Db.CellRange rngMaterial = 
 435:              new Db.CellRange(1, 9, 2, 9);
 436:            Db.CellRange rngDescript = 
 437:              new Db.CellRange(1, 10, 2, 10);
 438:   
 439:            // Горизонтальное объединение ячеек
 440:            Db.CellRange rngSection = 
 441:              new Db.CellRange(1, 3, 1, 4);
 442:            Db.CellRange rngArea = 
 443:              new Db.CellRange(2, 4, 2, 5);
 444:            Db.CellRange rngMass = 
 445:              new Db.CellRange(1, 7, 1, 8);
 446:   
 447:   
 448:  #else
 449:            // Вертикальное объединение ячеек
 450:            Db.CellRange rngMark = Db.CellRange
 451:              .Create(table, 1, 0, 2, 0);
 452:            Db.CellRange rngPos = Db.CellRange
 453:              .Create(table, 1, 1, 2, 1);
 454:            Db.CellRange rngName = Db.CellRange
 455:              .Create(table, 1, 2, 2, 2);
 456:            Db.CellRange rngCount = Db.CellRange
 457:              .Create(table, 1, 6, 2, 6);
 458:            Db.CellRange rngMaterial = Db.CellRange
 459:              .Create(table, 1, 9, 2, 9);
 460:            Db.CellRange rngDescript = Db.CellRange
 461:              .Create(table, 1, 10, 2, 10);
 462:   
 463:            // Горизонтальное объединение ячеек
 464:            Db.CellRange rngSection = Db.CellRange
 465:              .Create(table, 1, 3, 1, 4);
 466:            Db.CellRange rngArea = Db.CellRange
 467:              .Create(table, 2, 4, 2, 5);
 468:            Db.CellRange rngMass = Db.CellRange
 469:              .Create(table, 1, 7, 1, 8);
 470:  #endif
 471:   
 472:            table.MergeCells(rngMark);
 473:            table.MergeCells(rngPos);
 474:            table.MergeCells(rngName);
 475:            table.MergeCells(rngCount);
 476:            table.MergeCells(rngMaterial);
 477:            table.MergeCells(rngDescript);
 478:            table.MergeCells(rngSection);
 479:            table.MergeCells(rngArea);
 480:            table.MergeCells(rngMass);
 481:   
 482:            table.GenerateLayout();
 483:   
 484:            // Шаг 2: Назначаем созданную нами выше 
 485:            // таблицу, шаблоном для табличного 
 486:            // стиля
 487:            Db.TableStyle tableStyle2 =
 488:              new Db.TableStyle();
 489:            tableStyle2.CopyFrom(tableStyle);
 490:   
 491:            Db.ObjectId tableStyle2Id =
 492:              tableStylesDictionary.SetAt(
 493:              tableStyle2Name, tableStyle2);
 494:            tr.AddNewlyCreatedDBObject(tableStyle2,
 495:              true);
 496:   
 497:            Db.TableTemplate template =
 498:              new Db.TableTemplate(
 499:                table, Db.TableCopyOptions
 500:                .TableCopyColumnWidth |
 501:                Db.TableCopyOptions
 502:                .TableCopyRowHeight |
 503:                Db.TableCopyOptions
 504:                .ConvertFormatToOverrides);
 505:            db.AddDBObject(template);
 506:            tr.AddNewlyCreatedDBObject(template,
 507:              true);
 508:   
 509:  #if NEWER_THAN_AUTOCAD_2009
 510:            tableStyle2.Template =
 511:              template.ObjectId;
 512:  #else
 513:            Com.IAcadTableStyle2 customTableStyle2 
 514:            = (Com.IAcadTableStyle2)tableStyle2
 515:            .AcadObject;
 516:  #if PLATFORM_X86
 517:            customTableStyle2.TemplateId = template
 518:            .ObjectId.OldIdPtr.ToInt32();
 519:  #else
 520:            customTableStyle2.TemplateId = template
 521:            .ObjectId.OldIdPtr.ToInt64();
 522:  #endif // #if PLATFORM_X86
 523:  #endif // #if NEWER_THAN_AUTOCAD_2009
 524:            tr.Commit();
 525:          }
 526:        }
 527:      }
 528:   
 529:      /// <summary>
 530:      /// В пространстве модели создать таблицу на 
 531:      /// основе табличного стиля "Базовый". Точка 
 532:      /// вставки таблицы: 0,0,0.
 533:      /// </summary>
 534:      [Rt.CommandMethod(group, "CreateTable",
 535:        Rt.CommandFlags.Modal)]
 536:      public void CreateTable() {
 537:        Ap.Document doc = cad.DocumentManager
 538:          .MdiActiveDocument;
 539:        if(doc == null)
 540:          return;
 541:        Db.Database db = doc.Database;
 542:        using(doc.LockDocument()) {
 543:          using(Db.Transaction tr =
 544:            db.TransactionManager.StartTransaction(
 545:            )) {
 546:            Db.DBDictionary dict = tr.GetObject(
 547:              db.TableStyleDictionaryId,
 548:              Db.OpenMode.ForRead) as
 549:              Db.DBDictionary;
 550:            if(!dict.Contains(tableStyle2Name))
 551:              TableStyleWithTemplate_Command();
 552:            Db.TableStyle ts = tr.GetObject(dict
 553:              .GetAt(tableStyleName),
 554:              Db.OpenMode.ForRead) as Db.TableStyle
 555:              ;
 556:            // Включим отображение толщин линий, 
 557:            // дабы увидеть результат нашей работы
 558:            cad.SetSystemVariable("LWDISPLAY", 1);
 559:            // Создаём новую таблицу, на основе 
 560:            // созданного нами стиля.
 561:            Db.Table tableInstance = new Db.Table()
 562:              ;
 563:            tableInstance.SetDatabaseDefaults();
 564:            tableInstance.TableStyle = ts.ObjectId;
 565:            tableInstance.SetSize(10, 5);
 566:            tableInstance.GenerateLayout();
 567:            tableInstance.Position =
 568:              new Gm.Point3d(0, 0, 0);
 569:            Db.BlockTable bt = tr.GetObject(
 570:              db.BlockTableId, Db.OpenMode.ForRead)
 571:                as Db.BlockTable;
 572:            Db.BlockTableRecord modelSpace =
 573:              tr.GetObject(bt[
 574:              Db.BlockTableRecord.ModelSpace],
 575:              Db.OpenMode.ForWrite) as
 576:              Db.BlockTableRecord;
 577:            modelSpace.AppendEntity(tableInstance);
 578:            tr.AddNewlyCreatedDBObject(
 579:              tableInstance, true);
 580:            tr.Commit();
 581:          }
 582:        }
 583:        Object acadObject = cad.AcadApplication;
 584:        acadObject.GetType().InvokeMember(
 585:          "ZoomExtents", BindingFlags.InvokeMethod,
 586:          null, acadObject, null);
 587:      }
 588:   
 589:      /// <summary>
 590:      /// В пространстве модели создать таблицу на 
 591:      /// основе шаблона, указанного в табличном 
 592:      /// стиле "Спецификация металла". Точка 
 593:      /// вставки таблицы: 50,0,0.
 594:      /// </summary>
 595:      [Rt.CommandMethod(group, "CreateTable2",
 596:        Rt.CommandFlags.Modal)]
 597:      public void CreateTable2() {
 598:        Ap.Document doc = cad.DocumentManager
 599:          .MdiActiveDocument;
 600:        if(doc == null)
 601:          return;
 602:        Db.Database db = doc.Database;
 603:        using(doc.LockDocument()) {
 604:          using(Db.Transaction tr =
 605:            db.TransactionManager.StartTransaction(
 606:            )) {
 607:            Db.DBDictionary dict = tr.GetObject(
 608:              db.TableStyleDictionaryId,
 609:              Db.OpenMode.ForRead) as
 610:              Db.DBDictionary;
 611:            if(!dict.Contains(tableStyle2Name))
 612:              TableStyleWithTemplate_Command();
 613:            Db.TableStyle ts = tr.GetObject(dict
 614:              .GetAt(tableStyle2Name),
 615:              Db.OpenMode.ForRead) as Db.TableStyle
 616:              ;
 617:            Db.TableTemplate template =
 618:              tr.GetObject(ts.Template,
 619:              Db.OpenMode.ForRead) as
 620:              Db.TableTemplate;
 621:            // Включим отображение толщин линий, 
 622:            // дабы увидеть результат нашей работы
 623:            cad.SetSystemVariable("LWDISPLAY", 1);
 624:            // Создаём новую таблицу, на основе 
 625:            // созданного нами шаблона.
 626:            Db.Table tableInstance = new Db.Table()
 627:              ;
 628:            tableInstance.CopyFrom(template,
 629:              Db.TableCopyOptions.FillTarget);
 630:            tableInstance.GenerateLayout();
 631:            tableInstance.Position =
 632:              new Gm.Point3d(50, 0, 0);
 633:            Db.BlockTable bt = tr.GetObject(
 634:              db.BlockTableId, Db.OpenMode.ForRead)
 635:                as Db.BlockTable;
 636:            Db.BlockTableRecord modelSpace =
 637:              tr.GetObject(bt[
 638:              Db.BlockTableRecord.ModelSpace],
 639:              Db.OpenMode.ForWrite) as
 640:              Db.BlockTableRecord;
 641:            modelSpace.AppendEntity(tableInstance);
 642:            tr.AddNewlyCreatedDBObject(
 643:              tableInstance, true);
 644:            tr.Commit();
 645:          }
 646:        }
 647:        Object acadObject = cad.AcadApplication;
 648:        acadObject.GetType().InvokeMember(
 649:          "ZoomExtents", BindingFlags.InvokeMethod,
 650:          null, acadObject, null);
 651:      }
 652:   
 653:      /// <summary>
 654:      /// Настройка встроенных стилей ячеек. 
 655:      /// Встроенными являются стили: _TITLE, 
 656:      /// _HEADER и _DATA.
 657:      /// </summary>
 658:      /// <param name="tableStyle">Табличный стиль, 
 659:      /// подлежащий редактированию.
 660:      /// </param>
 661:      /// <param name="textStyleId">Идентификатор 
 662:      /// текстового стиля, который 
 663:      /// должен использоваться в создаваемых 
 664:      /// стилях ячеек.</param>
 665:      private void InitializeEmbededCellStyles(
 666:        Db.TableStyle tableStyle,
 667:        Db.ObjectId textStyleId) {
 668:        Db.Database db = tableStyle.Database;
 669:        // Некоторые операции будут выполняться 
 670:        // через COM, т.к. некоторые 
 671:        // настройки автору этого кода удалось 
 672:        // выполнить только через COM.
 673:  #if !NEWER_THAN_AUTOCAD_2009
 674:        Com.IAcadTableStyle2 customTableStyle =
 675:          (Com.IAcadTableStyle2)tableStyle
 676:          .AcadObject;
 677:  #else
 678:        Com.IAcadTableStyle customTableStyle =
 679:          (Com.IAcadTableStyle)tableStyle
 680:          .AcadObject;
 681:  #endif
 682:   
 683:        // Большинство настроек табличного стиля 
 684:        // хранятся в настройках стилей ячеек (Cell
 685:        // styles). Каждый стиль ячеек содержит 
 686:        // настройки, которые в диалоговом окне 
 687:        // Modify Table Style отображаются на 
 688:        // вкладках General, Text и Borders.      
 689:   
 690:        //*****************************************
 691:   
 692:        // 1. ВКЛАДКА "GENERAL" ДИАЛОГОВОГО ОКНА 
 693:        // "MODIFY TABLE STYLE":
 694:   
 695:        // В интересующем нас стиле ячеек, 
 696:        // устанавливем цвет фона ячеек (если это 
 697:        // нам нужно). Автор кода не нашёл способа 
 698:        // установить цвет фона для 
 699:        // пользовательских стилей ячеек. Есть 
 700:        // возможность устанавливать фон ячеек для 
 701:        // встроенных стилей Title, Header и Data:          
 702:        String strColor = String.Format(
 703:          "Autocad.AcCmColor.{0}", cad.Version
 704:          .Major);
 705:        Com.AcadAcCmColor color = (
 706:          (Int.AcadApplication)cad.AcadApplication)
 707:            .GetInterfaceObject(strColor) as
 708:            Com.AcadAcCmColor;
 709:        color.SetRGB(250, 0, 0);
 710:   
 711:        // Однако установить и получить фон мы 
 712:        // можем и посредством .NET API:
 713:        // tableStyle.SetBackgroundColor(Clr.Color
 714:        // .FromColorIndex(Clr.ColorMethod
 715:        // .Foreground, 31), (Int32)Db.RowType
 716:        // .DataRow);
 717:   
 718:        // Убираем фон
 719:        tableStyle.SetBackgroundColorNone(true,
 720:          (Int32)Db.RowType.DataRow);
 721:   
 722:        // Получаем фон через .NET API:
 723:        //Clr.Color dataBgColor = tableStyle
 724:        // .BackgroundColor(Db.RowType.DataRow);
 725:   
 726:        // Назначить выравнивание в стиле ячеек 
 727:        // через .NET API
 728:        tableStyle.SetAlignment(
 729:          Db.CellAlignment.TopLeft,
 730:          (Int32)Db.RowType.DataRow);
 731:   
 732:        // Получить выравнивание в стиле ячеек 
 733:        // через .NET API
 734:        // Db.CellAlignment _datAlignment = 
 735:        // tableStyle.Alignment(Db.RowType.DataRow)
 736:        // ;
 737:   
 738:        // Опция 'Format' (кнопка с тремя точками)
 739:        // Если нажать на эту кнопку, то откроется 
 740:        // диалоговое окно "Table Cell Format".
 741:        // Значением 'format' является строковое 
 742:        // значение. Простейший способ получения 
 743:        // этого значения следующий:
 744:        // 1. Установите в диалоговом окне 
 745:        // "Table Cell Format" нужный формат 
 746:        // вручную.
 747:        // 2. Посмотрите значение сгенерированной 
 748:        // строки, воспользовавшись методом 
 749:        // GetFormat2 (см. код ниже). 
 750:   
 751:        // Устанавливаем формат стиля ячеек через 
 752:        // .NET API
 753:        tableStyle.SetFormat("%lu2%pr3%ps[,%]",
 754:          Db.RowType.DataRow);
 755:   
 756:        // Получаем формат стиля ячеек через .NET 
 757:        // API
 758:        // String datFormat = tableStyle.Format(
 759:        // Db.RowType.DataRow);
 760:   
 761:        // Устанавливаем тип данных и единиц 
 762:        // измерения через .NET API
 763:        tableStyle.SetDataType(Db.DataType.Double,
 764:          Db.UnitType.Distance, Db.RowType.DataRow)
 765:          ;
 766:        // Получаем тип данных и единиц измерения 
 767:        // через .NET API
 768:        // Db.DataType _dataType = tableStyle
 769:        // .DataType(Db.RowType.DataRow);
 770:        // Db.UnitType _unitType = tableStyle
 771:        // .UnitType(Db.RowType.DataRow);
 772:   
 773:        // Устанавливаем значения опцииям 
 774:        // 'horizontal margins' и 'vertical 
 775:        // margins'
 776:   
 777:        // Встроенные стили ячеек имеют имена: 
 778:        // _TITLE, _HEADER, _DATA.
 779:        String name = "_HEADER";
 780:  #if !NEWER_THAN_AUTOCAD_2009
 781:        // Мне не удалось получить и назначить 
 782:        // значения опциям 'horizontal 
 783:        // margins' и 'vertical margins' в AutoCAD 
 784:        // 2009 ни через .NET API, ни через COM API
 785:  #else
 786:        tableStyle.SetMargin(Db.CellMargins.Left,
 787:          1.0, name);
 788:        tableStyle.SetMargin(Db.CellMargins.Top,
 789:          1.0, name);
 790:   
 791:        // Получить значения опций 'horizontal 
 792:        // margins' и 'vertical margins'
 793:        // можно так:
 794:        // Double _leftMargin = tableStyle.Margin(
 795:        // Db.CellMargins.Left, name);
 796:        // Double _topMargin = tableStyle.Margin(
 797:        // Db.CellMargins.Top, name);
 798:  #endif
 799:        // Устанавливаем значение свойства 'Merge 
 800:        // cells on row/column creation' через COM 
 801:        // API
 802:        customTableStyle.EnableMergeAll(name, false
 803:          );
 804:   
 805:        // Получаем значение свойства 'Merge cells
 806:        // on row/column creation' через COM API
 807:        // Boolean isMergedCells = customTableStyle
 808:        // .GetIsMergeAllEnabled(name);
 809:   
 810:        //*****************************************
 811:   
 812:        //2. ВКЛАДКА 'TEXT' ДИАЛОГОВОГО ОКНА 
 813:        // "MODIFY TABLE STYLE":
 814:   
 815:        // Получение через COM API текстового 
 816:        // стиля, назначенного стилю ячеек 
 817:        // Db.ObjectId _textStyleId = 
 818:        // new Db.ObjectId(new IntPtr(
 819:        // customTableStyle.GetTextStyleId(name)));
 820:   
 821:        // Назначение текстового стиля стилю ячеек 
 822:        // через .NET API
 823:        tableStyle.SetTextStyle(textStyleId,
 824:          (Int32)Db.RowType.TitleRow);
 825:        tableStyle.SetTextStyle(textStyleId,
 826:          (Int32)Db.RowType.HeaderRow);
 827:        tableStyle.SetTextStyle(textStyleId,
 828:          (Int32)Db.RowType.DataRow);
 829:        // Получение через .NET API текстового 
 830:        // стиля, назначенного стилю ячеек 
 831:        // Db.ObjectId _dataRowTextStyleId = 
 832:        // tableStyle.TextStyle(Db.RowType.DataRow)
 833:        // ;
 834:   
 835:        // Назначение высоты текста через .NET API          
 836:        tableStyle.SetTextHeight(5,
 837:          (Int32)Db.RowType.TitleRow); // TITLE
 838:        tableStyle.SetTextHeight(3.5,
 839:          (Int32)Db.RowType.HeaderRow); // HEADER
 840:        tableStyle.SetTextHeight(3.5,
 841:          (Int32)Db.RowType.DataRow); // DATA
 842:   
 843:        // Получение высоты текста через .NET API
 844:        // Double _dataRowTextHeight = tableStyle
 845:        // .TextHeight(Db.RowType.DataRow);
 846:   
 847:        // Назначение цвета тексту через .NET API      
 848:        Autodesk.AutoCAD.Colors.Color colTitleText
 849:          = Clr.Color.FromColorIndex(
 850:           Clr.ColorMethod.ByColor, 10);
 851:        Autodesk.AutoCAD.Colors.Color colHdText =
 852:          Clr.Color.FromColorIndex(
 853:            Clr.ColorMethod.ByColor, 210);
 854:        Autodesk.AutoCAD.Colors.Color colDataText =
 855:          Clr.Color.FromColorIndex(
 856:           Clr.ColorMethod.ByColor, 10);
 857:   
 858:        tableStyle.SetColor(colTitleText,
 859:          (Int32)Db.RowType.TitleRow);
 860:        tableStyle.SetColor(colHdText,
 861:          (Int32)Db.RowType.HeaderRow);
 862:        tableStyle.SetColor(colDataText,
 863:          (Int32)Db.RowType.DataRow);
 864:   
 865:        // Получение цвета текста через .NET API
 866:        // Clr.Color _textColor2 = tableStyle
 867:        // .Color(Db.RowType.DataRow);
 868:   
 869:        // Назначение угла наклона текста через COM 
 870:        // API
 871:        Double angle = 0; // значение в градусах
 872:        // Градусы необходимо преобразовывать в 
 873:        // радианы
 874:        customTableStyle.SetRotation(name, angle *
 875:          Math.PI / 180);
 876:   
 877:        // Получение угла наклона текста через COM 
 878:        // API
 879:        // Double _angle = customTableStyle
 880:        // .GetRotation(name) * 180.0 / Math.PI;
 881:   
 882:        //*****************************************
 883:   
 884:        // ВКЛАДКА 'BORDERS' ДИАЛОГОВОГО ОКНА 
 885:        // "MODIFY TABLE STYLE":
 886:   
 887:        // Назначение толщин линий границам ячеек 
 888:        // через .NET API
 889:        tableStyle.SetGridLineWeight(Db.LineWeight
 890:          .LineWeight050,
 891:            (Int32)Db.GridLineType.VerticalInside,
 892:            (Int32)Db.RowType.DataRow);
 893:   
 894:        tableStyle.SetGridLineWeight(Db.LineWeight
 895:          .LineWeight018,
 896:            (Int32)Db.GridLineType.HorizontalInside
 897:            , (Int32)Db.RowType.DataRow);
 898:   
 899:        tableStyle.SetGridLineWeight(Db.LineWeight
 900:          .LineWeight050,
 901:            (Int32)Db.GridLineType.OuterGridLines,
 902:            (Int32)Db.RowType.DataRow);
 903:   
 904:        tableStyle.SetGridLineWeight(Db.LineWeight
 905:          .LineWeight050,
 906:            (Int32)Db.GridLineType.AllGridLines,
 907:            (Int32)Db.RowType.HeaderRow);
 908:   
 909:        // Получение толщин линий границам ячеек 
 910:        // через .NET API
 911:        // Db.LineWeight _dataRowLineWeight = 
 912:        //  tableStyle.GridLineWeight(
 913:        //    Db.GridLineType.HorizontalTop, 
 914:        //    Db.RowType.DataRow);
 915:   
 916:        // Получаем идентификатор типа линии, 
 917:        // которая будет использоваться при 
 918:        // прорисовке контура ячеек, использующих 
 919:        // созданный нами стиль ячеек.
 920:        Db.ObjectId linetypeId =
 921:          db.ContinuousLinetype;
 922:   
 923:  #if !NEWER_THAN_AUTOCAD_2009
 924:        // Мне не удалось получить, либо назначить 
 925:        // значение GridLinetype стилю ячеек в 
 926:        // AutoCAD 2009 ни через .NET API, ни через
 927:        // COM API.
 928:  #else
 929:        // Назначаем стилю ячеек нужный тип линии 
 930:        // через .NET API
 931:        tableStyle.SetGridLinetype(linetypeId,
 932:          Db.GridLineType.AllGridLines, name);
 933:   
 934:        // Получаем стиль ячеек нужного нам типа 
 935:        // линии через .NET API
 936:        // Db.GridLineStyle _linestyle = tableStyle
 937:        //  .GridLineStyle(
 938:        // Db.GridLineType.AllGridLines, name);
 939:  #endif
 940:        Autodesk.AutoCAD.Colors.Color
 941:          outerLinesColor =
 942:          Clr.Color.FromColorIndex(
 943:            Clr.ColorMethod.ByColor, 90);
 944:        // Назначаем цвет линиям сетки нашего стиля 
 945:        // ячеек через .NET API
 946:        tableStyle.SetGridColor(outerLinesColor,
 947:          (Int32)(Db.GridLineType.OuterGridLines |
 948:          Db.GridLineType.VerticalGridLines),
 949:          (Int32)Db.RowType.DataRow);
 950:   
 951:        tableStyle.SetGridColor(outerLinesColor,
 952:          (Int32)(Db.GridLineType.AllGridLines),
 953:          (Int32)Db.RowType.HeaderRow);
 954:   
 955:        Autodesk.AutoCAD.Colors.Color
 956:          horInsideLinesColor =
 957:          Clr.Color.FromColorIndex(
 958:          Clr.ColorMethod.ByColor, 130);
 959:   
 960:        tableStyle.SetGridColor(
 961:          horInsideLinesColor,
 962:          (Int32)(Db.GridLineType
 963:          .HorizontalInside), (Int32)Db.RowType
 964:          .DataRow);
 965:   
 966:        // Получаем цвет линий сетки нашего стиля 
 967:        // ячеек через .NET API
 968:        // tableStyle.GridColor((Db.GridLineType
 969:        // .HorizontalInside | Db.GridLineType
 970:        // .VerticalLeft), Db.RowType.DataRow);
 971:   
 972:        // Установка значения опции 'Double line'
 973:  #if !NEWER_THAN_AUTOCAD_2009
 974:        // Мне не удалось получить или назначить в 
 975:        // AutoCAD 2009 значение опции 'Double 
 976:        // line' ни через COM API, ни через .NET 
 977:        // API.
 978:   
 979:        // Мне не удалось получить или назначить в 
 980:        // AutoCAD 2009 значение опции 'Spacing' ни 
 981:        // через COM API, ни через .NET API.            
 982:  #else
 983:        // Установка значения опции 'Double line' 
 984:        // через .NET API
 985:        tableStyle.SetGridLineStyle(
 986:          Db.GridLineStyle.Single,
 987:          Db.GridLineType.AllGridLines, name);
 988:   
 989:        // Получение значения опции 'Double line' 
 990:        // через .NET API
 991:        // Db.GridLineStyle _dataGridLinetypeStyle 
 992:        // = tableStyle.GridLineStyle(
 993:        //    Db.GridLineType.AllGridLines, name);
 994:   
 995:        // Установка значения опции 'Spacing' через
 996:        // .NET API
 997:        tableStyle.SetGridDoubleLineSpacing(3,
 998:          Db.GridLineType.AllGridLines, name);
 999:   
1000:        // Получение значения опции 'Spacing' через
1001:        // .NET API
1002:        // Double _spacing = tableStyle
1003:        // .GridDoubleLineSpacing(
1004:        //  Db.GridLineType.AllGridLines, name);
1005:  #endif
1006:      }
1007:   
1008:      /// <summary>
1009:      /// Создать и настроить дополнительный набор 
1010:      /// стилей ячеек: Группа, Формула и Итого, 
1011:      /// помимо встроенных стилей _TITLE, _HEADER 
1012:      /// и _DATA.
1013:      /// </summary>
1014:      /// <param name="tableStyle">Табличный стиль, 
1015:      /// подлежащий редактированию.
1016:      /// </param>
1017:      /// <param name="textStyleId">Идентификатор 
1018:      /// текстового стиля, который 
1019:      /// должен использоваться в создаваемых 
1020:      /// стилях ячеек.</param>
1021:      /// <param name="linetypeId">Идентификатор 
1022:      /// типа линии, которая должна
1023:      /// использоваться в создаваемых стилях ячеек
1024:      /// </param>
1025:      private void CreateCustomCellStyles(
1026:        Db.TableStyle tableStyle, Db.ObjectId
1027:        textStyleId, Db.ObjectId linetypeId) {
1028:  #if !NEWER_THAN_AUTOCAD_2009
1029:        Com.IAcadTableStyle2 customTableStyle =
1030:          (Com.IAcadTableStyle2)tableStyle
1031:          .AcadObject;
1032:  #else
1033:        Com.IAcadTableStyle customTableStyle =
1034:          (Com.IAcadTableStyle)tableStyle
1035:          .AcadObject;
1036:  #endif
1037:   
1038:        // Имена дополнительных стилей ячеек, 
1039:        // которые мы создадим
1040:        String[] names = new String[] { "Группа", 
1041:          "Формула", "Итого" };
1042:   
1043:        foreach(String name in names) {
1044:          customTableStyle.CreateCellStyle(name);
1045:          //***************************************
1046:   
1047:          // 1. ВКЛАДКА "GENERAL" ДИАЛОГОВОГО ОКНА 
1048:          // "MODIFY TABLE STYLE":
1049:   
1050:          // Установить цвет фона пользовательского 
1051:          // стиля ячеек (опция "Fill color" в 
1052:          // настройках табличного стиля) можно 
1053:          // через COM API. Сначала нужно создать 
1054:          // цвет:
1055:          String strColor = String.Format(
1056:            "Autocad.AcCmColor.{0}",
1057:            cad.Version.Major);
1058:          Com.AcadAcCmColor color = (
1059:            (Int.AcadApplication)cad
1060:            .AcadApplication)
1061:              .GetInterfaceObject(strColor) as
1062:              Com.AcadAcCmColor;
1063:          color.SetRGB(149, 203, 149);
1064:   
1065:          // Теперь пользовательскому стилю ячеек 
1066:          // можно, посредством COM API, установить
1067:          // фон:
1068:          // customTableStyle.SetBackgroundColor2(
1069:          // name, color);
1070:   
1071:          // Сбросить ранее установленный фон можно 
1072:          // следующим образом
1073:          //Int32 cellClass = customTableStyle
1074:          // .GetCellClass(name);
1075:          //Boolean result = false;
1076:          //customTableStyle
1077:          // .SetBackgroundColorNone(cellClass, ref
1078:          // result);
1079:   
1080:          // Получить цвет фона пользовательского 
1081:          // стиля ячеек можно так же посредством 
1082:          // COM API:
1083:          // color = customTableStyle
1084:          // .GetBackgroundColor2(name);
1085:   
1086:          // Назначаем выравнивание             
1087:          customTableStyle.SetAlignment2(name,
1088:            name == "Группа" ?
1089:            Com.AcCellAlignment.acMiddleLeft :
1090:            Com.AcCellAlignment.acMiddleCenter);
1091:   
1092:          // Получить выравнивание в стиле ячеек 
1093:          // через COM API
1094:          // Com.AcCellAlignment alignment = 
1095:          // customTableStyle.GetAlignment2(name);
1096:   
1097:          // Опция 'Format' (кнопка с тремя точками
1098:          // ). Если нажать на эту кнопку, то 
1099:          // откроется диалоговое окно "Table Cell 
1100:          // Format". Значением 'format' является 
1101:          // строковое значение. Простейший способ 
1102:          // получения этого значения следующий:
1103:          // 1. Установите в диалоговом окне "Table 
1104:          // Cell Format" нужный формат вручную.
1105:          // 2. Посмотрите значение сгенерированной 
1106:          // строки, воспользовавшись методом 
1107:          // GetFormat2 (см. код ниже).                
1108:   
1109:          // String _format = null;
1110:          // Устанавливаем нужный нам формат стиля 
1111:          // ячеек через COM API
1112:          customTableStyle.SetFormat2(name,
1113:            "%au0%pr3");
1114:          // Получаем формат стиля ячеек через COM 
1115:          // API
1116:          // customTableStyle.GetFormat2(name, out
1117:          // _format);
1118:   
1119:          // Устанавливем тип данных и единиц 
1120:          // измерения через COM API
1121:          customTableStyle.SetDataType2(name,
1122:            Com.AcValueDataType.acDouble,
1123:            Com.AcValueUnitType.acUnitArea);
1124:          // Получаем тип данных и единиц измерения 
1125:          // через COM API
1126:          // Com.AcValueDataType valueDataType;
1127:          // Com.AcValueUnitType unitType;
1128:          // customTableStyle.GetDataType2(name, 
1129:          // out valueDataType, out unitType);
1130:   
1131:          // Устанавливаем значения опцииям 
1132:          // 'horizontal margins' и 'vertical 
1133:          // margins'
1134:  #if !NEWER_THAN_AUTOCAD_2009
1135:          // Автору кода не удалось получить и 
1136:          // назначить значения опциям 'horizontal
1137:          // margins' и 'vertical margins' в 
1138:          // AutoCAD 2009 ни через .NET API, ни 
1139:          // через COM API.
1140:  #else
1141:   
1142:          tableStyle.SetMargin(Db.CellMargins.Left,
1143:            1, name);
1144:          tableStyle.SetMargin(Db.CellMargins.Top,
1145:            1, name);
1146:   
1147:          // Получить значения опций 'horizontal 
1148:          // margins' и 'vertical margins'
1149:          // можно так:
1150:          // Double _leftMargin = tableStyle
1151:          // .Margin(Db.CellMargins.Left, name);
1152:          // Double _topMargin = tableStyle
1153:          // .Margin(Db.CellMargins.Top, name);
1154:  #endif
1155:          // Устанавливаем значение свойства 'Merge
1156:          // cells on row/column creation' через 
1157:          // COM API
1158:          customTableStyle.EnableMergeAll(name,
1159:            name == "Группа" ? true : false);
1160:          // Получаем значение свойства 'Merge 
1161:          // cells on row/column creation'  через 
1162:          // COM API
1163:          // Boolean isMergedCells = 
1164:          // customTableStyle.GetIsMergeAllEnabled(
1165:          // name);
1166:   
1167:          //***************************************
1168:   
1169:          //2. ВКЛАДКА 'TEXT' ДИАЛОГОВОГО ОКНА 
1170:          // "MODIFY TABLE STYLE":
1171:   
1172:          // Назначение текстового стиля стилю 
1173:          // ячеек через COM API
1174:  #if PLATFORM_X86
1175:          customTableStyle.SetTextStyleId(name,
1176:            textStyleId.OldIdPtr.ToInt32());
1177:  #else
1178:          customTableStyle.SetTextStyleId(name, 
1179:            textStyleId.OldIdPtr.ToInt64());
1180:  #endif
1181:   
1182:          // Получение через COM API текстового 
1183:          // стиля, назначенного стилю ячеек
1184:          // Db.ObjectId _textStyleId = 
1185:          // new Db.ObjectId(new IntPtr(
1186:          // customTableStyle.GetTextStyleId(name))
1187:          // );
1188:   
1189:          // Назначение высоты текста через COM API
1190:          customTableStyle.SetTextHeight2(name,
1191:            name == "Формула" ? 3.5 : 5);
1192:          // Получение высоты текста через COM API
1193:          // Double _textHeight = customTableStyle
1194:          // .GetTextHeight2(name);
1195:   
1196:          // Назначение цвета тексту через COM API
1197:          switch(name) {
1198:            case "Группа":
1199:              color.SetRGB(0, 63, 255);
1200:              break;
1201:            case "Формула":
1202:              color.SetRGB(255, 0, 0);
1203:              break;
1204:            case "Итого":
1205:              color.SetRGB(0, 153, 0);
1206:              break;
1207:            default:
1208:              break;
1209:          }
1210:          customTableStyle.SetColor2(name, color);
1211:          // Получение цвета текста через COM API
1212:          // Com.AcadAcCmColor _textColor = 
1213:          // customTableStyle.GetColor2(name);
1214:   
1215:          // Назначение угла наклона текста через 
1216:          // COM API
1217:          Double angle = 0; // значение в градусах
1218:          // Градусы необходимо преобразовывать в 
1219:          // радианы
1220:          customTableStyle.SetRotation(name, angle
1221:            * Math.PI / 180);
1222:          // Получение угла наклона текста через 
1223:          // COM API
1224:          // Double _angle = customTableStyle
1225:          // .GetRotation(name) * 180.0 / Math.PI;
1226:   
1227:          //***************************************
1228:   
1229:          // ВКЛАДКА 'BORDERS' ДИАЛОГОВОГО ОКНА 
1230:          // "MODIFY TABLE STYLE":
1231:   
1232:          // Назначение толщин линий границам ячеек
1233:          // через COM API
1234:          customTableStyle.SetGridLineWeight2(name,
1235:            Com.AcGridLineType.acHorzTop |
1236:            Com.AcGridLineType.acHorzBottom |
1237:          Com.AcGridLineType.acVertLeft |
1238:          Com.AcGridLineType.acVertRight,
1239:          Com.ACAD_LWEIGHT.acLnWt050);
1240:   
1241:          customTableStyle.SetGridLineWeight2(name,
1242:            Com.AcGridLineType.acVertInside,
1243:            Com.ACAD_LWEIGHT.acLnWt018);
1244:   
1245:          customTableStyle.SetGridLineWeight2(name,
1246:            Com.AcGridLineType.acHorzInside |
1247:            Com.AcGridLineType.acVertInside,
1248:            Com.ACAD_LWEIGHT.acLnWt018);
1249:   
1250:          // Получение толщин линий границам ячеек 
1251:          // через COM API
1252:          // Com.ACAD_LWEIGHT _lineWeight = 
1253:          // customTableStyle.GetGridLineWeight2(
1254:          //  name, Com.AcGridLineType.acHorzTop);
1255:   
1256:  #if !NEWER_THAN_AUTOCAD_2009
1257:          // Автору кода не удалось получить, либо 
1258:          // назначить значение GridLinetype стилю
1259:          // ячеек в AutoCAD 2009 ни через .NET 
1260:          // API, ни через COM API.
1261:  #else
1262:          // Назначаем стилю ячеек нужный тип линии
1263:          // через .NET API
1264:          tableStyle.SetGridLinetype(linetypeId,
1265:            Db.GridLineType.AllGridLines,
1266:            name);
1267:          // Получаем стиль ячеек нужного нам типа 
1268:          // линии через .NET API
1269:          // Db.GridLineStyle _linestyle = 
1270:          // tableStyle.GridLineStyle(
1271:          //  Db.GridLineType.AllGridLines, name);
1272:  #endif
1273:          // Назначаем цвет линиям сетки нашего 
1274:          // стиля ячеек через COM API
1275:          color.SetRGB(70, 158, 250);
1276:   
1277:          customTableStyle.SetGridColor2(name,
1278:            Com.AcGridLineType.acHorzTop |
1279:            Com.AcGridLineType.acHorzBottom |
1280:          Com.AcGridLineType.acVertLeft |
1281:          Com.AcGridLineType.acVertRight,
1282:            color);
1283:   
1284:          color.SetRGB(175, 39, 65);
1285:          customTableStyle.SetGridColor2(name,
1286:            Com.AcGridLineType.acHorzInside |
1287:            Com.AcGridLineType.acVertInside, color)
1288:            ;
1289:   
1290:          // Получаем цвет линий сетки нашего стиля 
1291:          // ячеек через COM API
1292:          // Com.AcadAcCmColor _Clror = 
1293:          // customTableStyle.GetGridColor2(
1294:          //  name, Com.AcGridLineType.acHorzTop);
1295:   
1296:          // Установка значения опции 'Double line'
1297:  #if !NEWER_THAN_AUTOCAD_2009
1298:          // Автору кода не удалось получить или 
1299:          // назначить в AutoCAD 2009 значение 
1300:          // опции 'Double line' ни через COM API, 
1301:          // ни через .NET API.
1302:   
1303:          // Мне не удалось получить или назначить 
1304:          // в AutoCAD 2009 значение опции 'Spacing'
1305:          // ни через COM API, ни через .NET API. 
1306:  #else
1307:          // Установка значения опции 'Double line'
1308:          // через .NET API
1309:          tableStyle.SetGridLineStyle(
1310:            Db.GridLineStyle.Single,
1311:            Db.GridLineType.AllGridLines, name);
1312:          // Получение значения опции 'Double line'
1313:          // через .NET API
1314:          //Db.GridLineStyle _dataGridLinetypeStyle
1315:          // = tableStyle.GridLineStyle(
1316:          // Db.GridLineType.AllGridLines, name);
1317:   
1318:          // Установка значения опции 'Spacing' 
1319:          // через .NET API
1320:          tableStyle.SetGridDoubleLineSpacing(1,
1321:            Db.GridLineType.AllGridLines, name);
1322:          // Получение значения опции 'Spacing' 
1323:          // через .NET API
1324:          // Double _spacing = tableStyle
1325:          // .GridDoubleLineSpacing(
1326:          // Db.GridLineType.AllGridLines, name);
1327:  #endif
1328:        }
1329:      }
1330:    }
1331:  }


Размерные стили в AutoCAD

В данной заметке показан подробный пример по созданию и настройке размерного стиля в AutoCAD. Рассмотрена программная настройка каждой опции каждой вкладки диалогового окна редактирования размерных стилей.

Результат работы обозначенного ниже кода (команда CreateDimStyle) будет выглядеть следующим образом:


В коде каждая изменяемая нами опция настроек помечена комментарием, содержащим наименование этой опции в англоязычной версии AutoCAD.

/* DimStyleSample.cs
 * © Андрей Бушман, 2014 
 * Пример создания и настройки размерного стиля.
 */
using System;

using cad = Autodesk.AutoCAD.ApplicationServices
.Application;
using Ap = Autodesk.AutoCAD.ApplicationServices;
using Db = Autodesk.AutoCAD.DatabaseServices;
using Ed = Autodesk.AutoCAD.EditorInput;
using Rt = Autodesk.AutoCAD.Runtime;
using Clr = Autodesk.AutoCAD.Colors;
//************************************

[assembly: Rt.CommandClass(typeof(Bushman.CAD
  .Samples.Styles.DimStyleSample))]

namespace Bushman.CAD.Samples.Styles {

  public class DimStyleSample {

    [Rt.CommandMethod("CreateDimStyle",
      Rt.CommandFlags.Modal)]
    public void CreateDimStyle() {
      Ap.Document doc = cad.DocumentManager
        .MdiActiveDocument;
      if(doc == null)
        return;
      Ed.Editor ed = doc.Editor;
      Db.Database db = doc.Database;

      using(Db.Transaction tr =
        db.TransactionManager.StartTransaction()) 
        {

        // Создадим новый текстовый стиль для
        // использования его в наших размерных 
        // стилях
        Db.TextStyleTable tst =
          (Db.TextStyleTable)tr.GetObject(
          db.TextStyleTableId,
          Db.OpenMode.ForWrite);
        Db.TextStyleTableRecord textStyle =
          new Db.TextStyleTableRecord();
        textStyle.Name = "Тип А прямой";
        textStyle.FileName = "Arial.ttf";
        textStyle.XScale = 0.75;
        tst.Add(textStyle);
        tr.AddNewlyCreatedDBObject(textStyle,
          true);

        // Создаём новый размерный стиль...
        Db.DimStyleTable dst =
          (Db.DimStyleTable)tr.GetObject(
          db.DimStyleTableId,
          Db.OpenMode.ForWrite);
        Db.DimStyleTableRecord dimStyle =
          new Db.DimStyleTableRecord();
        dimStyle.Name = "Основной без допусков";

        // Откройте диалоговое окно "Modify 
        // Dimension Style" (команда _DIMSTYLE) и
        // нажмите кнопку "Modify.." - мы будем
        // программно изменять эти настройки. 
        // Наименования вкладок, групп настроек и
        // конкретных опций будут приводиться для
        // английской версии AutoCAD.

        // *** Вкладка LINES ***

        // Группа настроек "Dimension lines":

        Db.ObjectId lineTypeId =
          // db.ContinuousLinetype;
          // или по блоку:
          db.ByBlockLinetype;

        // цвет "ByLayer"
        Clr.Color colorByLayer = Clr.Color
          .FromColorIndex(Clr.ColorMethod
          .ByLayer, 256);

        // Размерным линиям назначаем цвет 
        // "ByLayer"
        dimStyle.Dimclrd = colorByLayer; // Color 

        // Linetype
        dimStyle.Dimltype = lineTypeId;
        // Lineweight
        dimStyle.Dimlwd = Db.LineWeight
          .ByLineWeightDefault;
        // Extend Beyond Ticks
        dimStyle.Dimdle = 2;
        dimStyle.Dimdli = 7; // Baseline Spacing
        // Suppress dim line 1
        dimStyle.Dimsd1 = false;
        // Suppress dim line 2
        dimStyle.Dimsd2 = false;

        // Группа настроек "Extension Lines":
        dimStyle.Dimclre = colorByLayer; // Color
        // Linetype Ext 1
        dimStyle.Dimltex1 = lineTypeId;
        // Linetype Ext 2
        dimStyle.Dimltex2 = lineTypeId;
        dimStyle.Dimlwe = Db.LineWeight
          .ByLineWeightDefault; // Lineweight
        // Suppress Ext line 1
        dimStyle.Dimse1 = false;
        // Suppress Ext line 2
        dimStyle.Dimse2 = false;
        // Extend Beyond Dim Lines
        dimStyle.Dimexe = 2.0;
        // Offset From Origin
        dimStyle.Dimexo = 0;
        // Fixed Length Extension Lines
        dimStyle.DimfxlenOn = false;
        dimStyle.Dimfxlen = 1; // Length

        // *** Вкладка SYMBOL AND ARROWS ***

        // Группа "Arrowheads":
        // Внимание: Аннотативные блоки не могут
        // быть использованными в качестве 
        // пользовательского варианта для опций
        // First, Second и Leader. В обозначенных
        // опциях пользовательский вариант
        // представлен в виде элемента "User 
        // Arrow..." в самом низу раскрывающегося
        // списка.
        Db.BlockTable bt = (Db.BlockTable)tr
          .GetObject(db.BlockTableId,
          Db.OpenMode.ForRead);

        // Получаем идентификаторы интересующих
        // нас определений блоков
        Db.ObjectId id1 = GetArrowObjectId_dim(
          "DIMBLK1""_DOT");
        Db.ObjectId id2 = GetArrowObjectId_dim(
          "DIMBLK2""_CLOSED");
        Db.ObjectId id3 = GetArrowObjectId_dim(
          "DIMBLK2""_Oblique");

        // Убедитесь, что вы установили в true
        // значение свойства "Dimsah", если вам 
        // нужно назначить опциям First и Second
        // разные значения!
        dimStyle.Dimsah = true;

        // В качестве значения опций группы 
        // Arrowheads вы можете назначить 
        // Db.ObjectId.Null - в этом случае будет
        // использоваться маркер по умолчанию.

        // Опция "First" на вкладке "Symbols and
        // Arrows" (системная переменная Dimblk1)
        dimStyle.Dimblk1 = id3;
        // Опция "Second" на вкладке "Symbols and
        // Arrows" (системная переменная Dimblk2)   
        dimStyle.Dimblk2 = id3;

        // При желании, вы можете изменить оба
        // параметра (First и Second) 
        // одновременно, задав значение свойству
        // Dimblk. Но в этом случае вы не должны
        // в коде назначать значения для свойств
        // 'Dimblk1' и 'Dimblk2':
        // dimStyle.Dimblk = id3; 

        // Опция Leader. Если в качестве значения
        // указать ObjectId.Null, то будет 
        // использоваться вариант 'Closed filled'
        dimStyle.Dimldrblk = Db.ObjectId.Null;
        dimStyle.Dimasz = 3; // Arrow Size

        // Группа "Center marks":

        // Значения, допустимые для свойства 
        // 'Dimcen':
        //  0 - None; 
        //  1 - Mark; 
        // -1 - Line
        Int32 centerMarks = -1;
        Double centerMarksSize = 2.0;
        // Размер центрального маркера или 
        // центральной линии
        dimStyle.Dimcen = centerMarks *
          centerMarksSize;

        // Значение опции "Dimension Break" 
        // хранится в расширенных данных (XData)
        // размерного стиля. Давайте доберёмся до
        // него...

        // Для начала, получаем таблицу имён 
        // зарегистрированных приложений
        Db.RegAppTable regTable =
          (Db.RegAppTable)tr.GetObject(
          db.RegAppTableId, Db.OpenMode.ForRead);
        String xName = "cad_DSTYLE_DIMBREAK";

        // Если нужный нам элемент не 
        // зарегистрирован - выполняем его 
        // регистрацию
        if(!regTable.Has(xName)) {
          regTable.UpgradeOpen();
          Db.RegAppTableRecord app =
            new Db.RegAppTableRecord();
          app.Name = xName;
          regTable.Add(app);
          tr.AddNewlyCreatedDBObject(app, true);
        }

        Db.ResultBuffer rb = new Db.ResultBuffer(
            new Db.TypedValue((Int32)Db.DxfCode
              .ExtendedDataRegAppName, xName),
            new Db.TypedValue((Int32)Db.DxfCode
              .ExtendedDataInteger16, 391),
            new Db.TypedValue((Int32)Db.DxfCode
              .ExtendedDataReal, 0.0
          /* Наше значение свойства "Dimension 
           * Break" */));

        dimStyle.XData = rb;

        // Группа (опция) "Arc Length Symbol":
        // Значения, допустимые для свойства 
        // 'Dimarcsym' (три переключателя):
        // 0 - Precending dimension text
        // 1 - Above dimension text
        // 2 - None

        // Опция "Arc Length Symbol"
        dimStyle.Dimarcsym = 1;

        // Группа "Radius Jog Dimensions":
        // Jog Angle
        dimStyle.Dimjogang = 45 * Math.PI / 180;

        // Группа "Linear Jog Dimensions":
        // Значение "Linear Jog Size" хранится в 
        // расширенных данных (XData) размерного 
        // стиля.                
        xName = "cad_DSTYLE_DIMJAG";
        if(!regTable.Has(xName)) {
          regTable.UpgradeOpen();
          Db.RegAppTableRecord app =
            new Db.RegAppTableRecord();
          app.Name = xName;
          regTable.Add(app);
          tr.AddNewlyCreatedDBObject(app, true);
        }
        rb = new Db.ResultBuffer(
            new Db.TypedValue((Int32)Db.DxfCode
              .ExtendedDataRegAppName, xName),
            new Db.TypedValue((Int32)Db.DxfCode
              .ExtendedDataInteger16, 388),
            new Db.TypedValue((Int32)Db.DxfCode
              .ExtendedDataReal, 1.4995
          /* Значение для свойства "Linear Jog 
           * Size" */));
        dimStyle.XData = rb;

        // *** Вкладка TEXT ***

        // Группа "Text Appearance": 

        // Text Style
        dimStyle.Dimtxsty = textStyle.ObjectId;
        dimStyle.Dimclrt = Clr.Color
          .FromColorIndex(Clr.ColorMethod.ByAci,
          210); // Text Color

        // Свойство 'Dimtfill' влияет на 
        // поведение опции "Fill Color" и 
        // принимает одно из следующих значений:
        // 0 - Фон отсутствует
        // 1 - Использовать текущий фон чертежа
        // 2 - Фон, указанный в свойстве 
        // Dimtfillclr.
        dimStyle.Dimtfill = 0;

        dimStyle.Dimtfillclr = Clr.Color
          .FromColorIndex(Clr.ColorMethod.ByAci,
          256); // Fill Color (см. Dimtfill выше)
        dimStyle.Dimtxt = 3.5; // Text Height
        // Fraction Height Scale
        dimStyle.Dimfrac = 2;
        // ВключитьОтключить опцию "Draw Frame 
        // Around Text"
        Boolean drawFrameAroundText = false;

        // Группа "Text Placement":

        // Опция Vertical (свойство 'Dimtad') 
        // может принимать только следующие 
        // значения:
        // 0 - Centered: центрировать размерный 
        //    текст между выносными линиями.
        // 1 - Above: разместить размерный текст
        //    над размерной линией, за 
        //    исключением случаев, когда 
        //    размерная линия не горизонтальна и
        //    текст внутри выносных линий рамещён 
        //    горизонтально (DIMTIH = 1). 
        //    Расстояние от размерной линии до
        //    самой нижней строки текста 
        //    определяется значением переменной
        //    DIMGAP (свойство Dimgap).
        // 2 - Outside: разместить размерный 
        //    текст в стороне от размерной линии, 
        //    подальше от определённых точек.
        // 3 - JIS: разместить размерный текст в 
        //    соответствии с Японским 
        //    Индустриальным Стандартом.
        dimStyle.Dimtad = 1; // Vertical 

        // Опция Horizontal (свойство 'Dimjust')
        // принимает только следующие значения:
        // 0 - Centered
        // 1 - At Ext Line 1
        // 2 - At Ext Line 2
        // 3 - Over Ext Line 1
        // 4 - Over Ext Line 2
        dimStyle.Dimjust = 0; // Horizontal

        // View Direction
#if NEWER_THAN_2009
        dimStyle.Dimtxtdirection = true;
#endif
        // Offset from Dim Line
        dimStyle.Dimgap = 1 *
          (drawFrameAroundText ? -1 : 1);

        // Группа "Text Alignment":

        // Для того, чтобы выбрать один из трёх
        // доступных вариантов, следует назначить
        // значение сразу двум свойствам: Dimtih
        // и Dimtoh.
        //
        // Horizontal: 
        //   Dimtih = true;
        //   Dimtoh = true;
        //
        // Aligned with Dimension Line:
        //   Dimtih = false;
        //   Dimtoh = false;
        //
        // ISO Standard: 
        //   Dimtih = false;
        //   Dimtoh = true;
        //                   

        // Text Alignment
        dimStyle.Dimtih = false;
        dimStyle.Dimtoh = false;

        // *** Вкладка FIT ***

        // Группа "Fit Options":

        // Свойство 'Dimatfit' может принимать
        // только следующие значения:
        // 0 - Выбрать опцию Both text and arrows
        // 1 - Выбрать опцию Arrows
        // 2 - Выбрать опцию Text
        // 3 - Выбрать опцию "Either text or 
        //    arrows (best fit)"

        // Для того, чтобы назначить свойству
        // 'Dimatfit' нужное значение 0-3, нужно
        // сначала назначить false свойству 
        // Dimtix. Если Dimtix назначить true, то
        // будет выбрана опция "Always Keep Text 
        // Between Ext Lines".

        // Опция "Always Keep Text Between Ext 
        // Lines"
        dimStyle.Dimtix = false;

        // Не забываем предварительно установить 
        // 'Dimtix' в false
        dimStyle.Dimatfit = 3;

        // Suppress Arrows If They Don't Fit 
        // Inside Extension Lines
        dimStyle.Dimsoxd = false;

        // Группа "Text placement" group:

        // Свойство Dimtmove может принимать 
        // только следующие значения:
        // 0 - Выбрана опция "Beside the 
        //    dimension line"
        // 1 - Выбрана опция "Over dimension 
        //    line, with leader"
        // 2 - Выбрана опция "Over dimension 
        //    line, without leader"
        dimStyle.Dimtmove = 1;

        // Группа"Scale for Dimension Features":
        dimStyle.Annotative =
          Db.AnnotativeStates.True; // Annotative                
        dimStyle.Dimscale = 1.0; // Dimscale

        // Для того, чтобы установить опцию 
        // "Scale Dimensions To Layout" нужно
        // свойству Dimscale присвоить 0:
        // dimStyle.Dimscale = 0;

        // Группа "Fine Tuning":

        // Place Text Manually
        dimStyle.Dimupt = false;
        // Draw Dim Line Between Ext Lines
        dimStyle.Dimtofl = false;

        // *** Вкладка Primary Units ***

        // Группа "Leader dimensions"

        // Опция "Unit format" (свойство 
        // 'Dimlunit') может принимать только 
        // следующие значения:
        // 1 - Scientific
        // 2 - Decimal
        // 3 - Engineering
        // 4 - Architectural
        // 5 - Fractional
        // 6 - Windows Desktop

        // Unit format
        dimStyle.Dimlunit = 2;

        // Масштабный коэффициент высоты 
        // текста, записанного в виде дроби. Эта
        // высота расчитывается путём умножения
        // указанного в свойстве Dimtfac коэффи-
        // циента на высоту текста, указанную в
        // свойстве Dimtxt.
        dimStyle.Dimtfac = 0.5;

        // Количество знаков после запятой:
        dimStyle.Dimdec = 0; // Precision

        // Опция "Fraction format" (свойство 
        // 'Dimfrac') принимает одно из следующих
        // значений:
        // 0 - Horizontal
        // 1 - Diagonal
        // 2 - Not stacked (например 1/2)
        dimStyle.Dimfrac = 0; // Fraction Format

        // Если опции "Unit format" в качестве
        // значения назначен 'Decimal', то в тек-
        // сте размеров, вместо точки, в качестве 
        // разделителя десятичной части будет
        // использоваться иной разделитель, кото-
        // рый указывается при помощи свойства 
        // 'Dimdsep'. Если свойству 'Dimdsep' 
        // присвоить NULL в качестве значения, то
        // в качестве десятичного разделителя 
        // будет использоваться точка.

        // Опция "Decimal separator" (свойство 
        // 'Dimdsep') может принимать только 
        // следующие значения:
        // '.' - Точка
        // ',' - Запятая
        // ' ' - Пробел

        // Decimal Separator
        dimStyle.Dimdsep = ',';
        dimStyle.Dimrnd = 0.0; // Round Off

        // Prefix (префикс) и Suffix (суффикс)
        // Префикс и суффикс указываются в 
        // составе строкового значения, 
        // присваиваемого свойству Dimpost.
        // Пример: "L = <> m"
        // Где:
        //    "L = " - префикс
        //    <> - вычисленное числовое значение
        //    " m" - суффикс 
        dimStyle.Dimpost = "<>";

        // Группа "Measurement Scale":

        dimStyle.Dimlfac = 1; // Scale Factor

        // Выделить или снять выделение опции
        // "Apply to Layout Dimensions Only" на 
        // вкладке "Primary Units":
        Boolean applyToLayoutDimensionsOnly =
          false;
        // Если свойству 'Dimfrac' назначить 
        // отрицательное значение, то опция 
        // "Apply to Layout Dimensions Only" 
        // будет включена:
        dimStyle.Dimlfac =
          applyToLayoutDimensionsOnly ? -1 *
          Math.Abs(dimStyle.Dimlfac) :
            Math.Abs(dimStyle.Dimlfac);

        // Подгруппа "Zero Suppression" группы
        // "Leader dimensions":

        // Свойству 'Dimzin' property' следует
        // назначать одно из следующих значений:
        // 0 - Подавляет нулевые значения для 
        //    футов и дюймов
        // 1 - Записывает нулевые значения для 
        //    футов и дюймов
        // 2 - Записывает нулевые значения для 
        //    футов и подавляет нулевые значения 
        //    для дюймов
        // 3 - Записывает нулевые значения для 
        //    дюймов и подавляет нулевые значения
        //    для футов
        // 4 - Подавляет ведущие нули в десятич-
        //    ных размерах (например, 0,5000 
        //    записывается как ,5000)
        // 8 - Подавляет замыкающие нули в деся-
        //    тичных размерах (например, 12,5000
        //    записывается как 12,5)
        // 12 - Подавляет и ведущие, и замыкающие
        //    нули (например, 0,5000 записывается
        //    как ,5)                
        dimStyle.Dimzin = 8;

        // Если включено подавление ведущих ну-
        // лей, то становятся доступными для
        // редактирования опции "Sub-units 
        // factor" и "Sub-units suffix".
#if NEWER_THAN_2009
        // TODO: Автору кода не удалось программ-
        // но добраться до этих свойств, т.к.
        // не существует переменных и свойств 
        // размерного стиля с именами "DIMMZF" и
        // "DIMMZS"        
#endif
        // Группа "Angular Dimensions":

        // Опции "Units format" (свойство 
        // 'Dimaunit) следует назначать одно из
        // следующих значений:
        // 0 - Decimal degrees
        // 1 - Degrees/minutes/seconds
        // 2 - Gradians
        // 3 - Radians
        dimStyle.Dimaunit = 1; // Units Format

        // Опция Precision (свойство 'Dimadec')
        // должно содержать одно из следующих
        // значений:
        // -1 - В угловых размерах отображается 
        //    количество знаков после запятой, 
        //    указанных с помощью переменной 
        //    DIMDEC.
        // 0-8 - Указывает количество знаков 
        //    после запятой, отображаемых в 
        //    угловых размерах (независимо от 
        //    переменной DIMDEC)
        dimStyle.Dimadec = 4; // Precision        

        // Подгруппа "Zero Suppression" в составе
        // группы "Angular Dimensions" управляет
        // подавлением нулей для всех угловых 
        // размеров.
        // Свойство 'Dimazin' должно содержать
        // одно из следующих значений:
        // 0 - Отображает все ведущие и замыкаю-
        //    щие нули.
        // 1 - Подавляет ведущие нули в десятич-
        //    ных размерах (например, 0,5000 
        //    записывается как ,5000)
        // 2 - Подавляет замыкающие нули в 
        //    десятичных размерах (например, 
        //    12,5000 записывается как 12,5)
        // 3 - Подавление ведущих и замыкающих 
        //    нулей (например, 0,5000 записывает-
        //    ся как ,5) 
        dimStyle.Dimazin = 2;

        // *** Вкладка ALTERNATIVE UNITS ***

        // Display Alternate Units
        dimStyle.Dimalt = false;

        // Группа "Alternate Units":

        // Опция "Unit Format" (свойство 
        // 'Dimaltu') должна содержать одно из 
        // следующих значений:
        // 1 - Scientific
        // 2 - Decimal
        // 3 - Engineering
        // 4 - Architectural Stacked
        // 5 - Fractional Stacked
        // 6 - Architectural
        // 7 - Fractional
        // 8 - Windows Desktop
        dimStyle.Dimaltu = 2; // Unit Format
        dimStyle.Dimaltd = 0; // Precision
        // Multiplier for Alternate Units
        dimStyle.Dimaltf = 25.4;
        // Round Distances To
        dimStyle.Dimaltrnd = 0;

        // Prefix (префикс) и Suffix (суффикс)
        // Префикс и суффикс указываются в 
        // составе строкового значения, 
        // присваиваемого свойству Dimapost.
        // Пример: "L = <> m"
        // Где:
        //    "L = " - префикс
        //    <> - вычисленное числовое значение
        //    " m" - суффикс
        dimStyle.Dimapost = "<>";

        // Группа "Zero Suppression":

        // Свойству 'Dimaltz' property' следует
        // назначать одно из следующих значений:
        // 0 - Подавляет нулевые значения для 
        //    футов и дюймов
        // 1 - Записывает нулевые значения для 
        //    футов и дюймов
        // 2 - Записывает нулевые значения для 
        //    футов и подавляет нулевые значения 
        //    для дюймов
        // 3 - Записывает нулевые значения для 
        //    дюймов и подавляет нулевые значения
        //    для футов
        // 4 - Подавляет ведущие нули в десятич-
        //    ных размерах (например, 0,5000 
        //    записывается как ,5000)
        // 8 - Подавляет замыкающие нули в деся-
        //    тичных размерах (например, 12,5000
        //    записывается как 12,5)
        // 12 - Подавляет и ведущие, и замыкающие
        //    нули (например, 0,5000 записывается
        //    как ,5)               
        dimStyle.Dimaltz = 0; // Zero Suppression

        // Если включено подавление ведущих ну-
        // лей, то становятся доступными для
        // редактирования опции "Sub-units 
        // factor" и "Sub-units suffix".

#if NEWER_THAN_2009
        // TODO: Автору кода не удалось программ-
        // но добраться до этих свойств, т.к.
        // не существует переменных и свойств 
        // размерного стиля с именами "DIMALTMZF"
        // и "DIMALTMZS".
#endif
        // Группа "Placement":
        const String bpv = @"X";
        // Переключение опций данной группы 
        // выполняетя при помощи добавления или 
        // удаления суффикса "X" в значении 
        // свойства Dimpost:

        // Если нужно выбрать опцию "Below 
        // primary value":
        // dimStyle.Dimpost = dimStyle.Dimpost
        //  .EndsWith(bpv) ? dimStyle.Dimpost :
        //  dimStyle.Dimpost + bpv;

        // Если нужно выбрать опцию "After 
        // primary value":
        dimStyle.Dimpost = !dimStyle.Dimpost
         .EndsWith(bpv) ? dimStyle.Dimpost :
         dimStyle.Dimpost.Substring(0,
         dimStyle.Dimpost.Length - bpv.Length);


        // *** Вкладка Tolerances ***

        // Группа "Tolerance Format":

        // Dimtol = true,  Dimlim = true  - 
        //    'Limits', but don't set this 
        //    combinations(!!!), or you will get 
        //    "Style Overrides" for Dimension 
        //    Style name. For getting the 
        //    'Limits' value, look below.
        //
        // Symmetrical:
        //    Dimtol = true
        //    Dimlim = false
        //
        // Limits (рекомендуемый вариант):
        //    Dimtol = false
        //    Dimlim = true
        //
        // None:
        //    Dimtol = false
        //    Dimlim = false
        // 
        // Basic:
        //    dimStyle.Dimgap = -1 * Math.Abs(
        //      dimStyle.Dimgap);
        //
        // Deviation:
        //    Dimtol = true
        //    Dimtm = 1.0e-009

        dimStyle.Dimtol = false;
        dimStyle.Dimlim = false;

        dimStyle.Dimtdec = 0; // Precision
        dimStyle.Dimtp = 1; // Upper Value
        dimStyle.Dimtm = 0; // Lower Value

        // Scaling for Height
        dimStyle.Dimtfac = 0.5;

        // Опция "Vertical Position" (свойство 
        // 'Dimtolj') должно принимать одно из
        // следующих значений:
        // 0 - Bottom
        // 1 - Middle
        // 2 - Top

        // Vertical Position
        dimStyle.Dimtolj = 1;

        // Группа "Tolerance Alignment":

        // TODO: Автору кода не удалось изменить
        // состояние переключателей "Align 
        // Decimal Separators" и "Align 
        // Operational Symbols" 

        // Подгруппа "Zero Suppression" в группе
        // "Tolerance Format":

        // 'Dimtzin' property's allowed values:
        // 0 - Подавляет нулевые значения для 
        //    футов и дюймов
        // 1 - Записывает нулевые значения для 
        //    футов и дюймов
        // 2 - Записывает нулевые значения для 
        //    футов и подавляет нулевые значения 
        //    для дюймов
        // 3 - Записывает нулевые значения для 
        //    дюймов и подавляет нулевые значения
        //    для футов
        // 4 - Подавляет ведущие нули в десятич-
        //    ных размерах (например, 0,5000 
        //    записывается как ,5000)
        // 8 - Подавляет замыкающие нули в деся-
        //    тичных размерах (например, 12,5000
        //    записывается как 12,5)
        // 12 - Подавляет и ведущие, и замыкающие
        //    нули (например, 0,5000 записывается
        //    как ,5)        
        dimStyle.Dimtzin = 8; // Zero Suppression

        // Группа "Alternate Unit Tolerance":

        dimStyle.Dimalttd = 0; // Precision

        // Подгруппа "Zero Suppression" в составе
        // группы "Alternate Unit Tolerance":

        // 'Dimalttz' property's allowed values:
        // 0 - Подавляет нулевые значения для 
        //    футов и дюймов
        // 1 - Записывает нулевые значения для 
        //    футов и дюймов
        // 2 - Записывает нулевые значения для 
        //    футов и подавляет нулевые значения 
        //    для дюймов
        // 3 - Записывает нулевые значения для 
        //    дюймов и подавляет нулевые значения
        //    для футов
        //
        // Для подавления ведущих или замыкающих 
        // нулей к выбранному значению может быть
        // добавлено:
        //
        // 4 - Подавление ведущих нулей
        // 8 - Подавление замыкающих нулей.

        // Zero Suppression
        dimStyle.Dimalttz = 0;

        // ***
        // Сохраняем выполненные изменения                
        dst.Add(dimStyle);
        tr.AddNewlyCreatedDBObject(dimStyle,
          true);

        // Устраняем потенциально возможную 
        // проблему появления в перечне размерных
        // стилей дополнительного элемента,
        // именованного как "Style Overrides":        
        db.Dimstyle = dimStyle.ObjectId;
        db.SetDimstyleData(dimStyle);

        // Теперь, на базе созданного нами основ-
        // ного размерного стиля, можно создавать
        // его деталлизированные варианты для:
        //    - радиальных размеров
        //    - угловых размеров
        //    - линейных размеров
        //    - и т.д.

        // Для дополнительной информации можно
        // почитать раздел документации:
        // ObjectARX Reference Guide > Additional
        // Information > Dimension Styles > 
        // Dimension Style Families.

        // Дочерние размерные стили создаются на
        // основе базового. Имена формируются по
        // правилу: ИмяБазовогоСтиля + Суффикс.
        // В качестве суффиксов используется один
        // из следующих вариантов:
        String[] names = new String[] {
                      "$0"//  Линейный 
                      "$2"//  Угловой  
                      "$3"//  Диаметральный
                      "$4"//  Радиальный
                      "$6"//  Ординатный
                      "$7"  //  Выноски
                  };
        foreach(String item in names) {
          Db.DimStyleTableRecord childStyle;
          String childName = dimStyle.Name +
            item;
          if(dst.Has(childName)) {
            childStyle =
              (Db.DimStyleTableRecord)tr
              .GetObject(dst[childName],
              Db.OpenMode.ForWrite);
          }
          else {
            childStyle =
              (Db.DimStyleTableRecord)dimStyle
              .Clone();
            childStyle.Name = childName;
            dst.Add(childStyle);
            tr.AddNewlyCreatedDBObject(
              childStyle, true);
          }
        }
        // Далее можно выполнять настройку
        // унаследованных размерных стилей,
        // выполняя тем самым необходимую
        // деталлизацию для конкретных типов 
        // размеров. 
        // Редактирование выполняется точно так
        // же, как мы это проделали выше с базо-
        // вым стилем, поэтому в нашем примере
        // не будем повторяться и на этом 
        // завершим работу.

        tr.Commit();
      }
    }

    static Db.ObjectId GetArrowObjectId_dim(
      string arrow, string newArrName) {
      Db.ObjectId arrObjId = Db.ObjectId.Null;
      Ap.Document doc = cad.DocumentManager
        .MdiActiveDocument;
      Db.Database db = doc.Database;

      string oldArrName = cad.GetSystemVariable(
        arrow) as string;

      // (эта операция может создать в чертеже
      // новое определение блока)
      cad.SetSystemVariable(arrow, newArrName);

      // Восстанавливаем предыдущее значение
      if(oldArrName.Length != 0)
        cad.SetSystemVariable(arrow, oldArrName);

      // Получаем идентификатор блока
      Db.Transaction tr = db.TransactionManager
        .StartTransaction();
      using(tr) {
        Db.BlockTable bt = (Db.BlockTable)tr
          .GetObject(db.BlockTableId, Db.OpenMode
          .ForRead);
        arrObjId = bt[newArrName];
        tr.Commit();
      }
      return arrObjId;
    }
  }
}