Тема: Автоматическое размещение объекта на нужном слое

Исходники взяты отсюда https://sites.google.com/site/bushmansn … ox/stati/x
Автору огромное спасибо.

Немного изменено под свои нужды. Но...
При подгрузке DLL все работает замечательно.
При открытии или создании нового файла происходит Fatal Error и акад захлопывается. В чем проблема не пойму.

using Autodesk.AutoCAD.ApplicationServices;
using acad = Autodesk.AutoCAD.ApplicationServices.Application;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Colors;
using System.Linq;
using System.Collections;
using System.Collections.Generic;
using System.Xml.Linq;
using Autodesk.AutoCAD.Runtime;
using System.IO;
using System;
using Autodesk.AutoCAD.EditorInput;
using System.Text;
using System.Threading.Tasks;
using Autodesk.AutoCAD.Geometry;

namespace Bushman.AutoCAD.Samples
{
    public class Class1 : IExtensionApplication
    {
        //Словарь, в котором каждой команде сопоставлен свой слой
        Dictionary<string, LayerTableRecord> dict;
        //Объекты общих настроек и настроек слоёв
        XElement settings, layersInfo;
        //Полное имя каталога, в котором находится данная сборка
        string currentDir;
        //Имя слоя, который был текущим до того, как была выполнена команда
        string previousLayerName;

        public void Initialize()
        {
            //Получаем путь к каталогу, в котором находится данная сборка
            currentDir = new FileInfo(this.GetType().Assembly.Location).DirectoryName;

            
            //Считываем данные файла настроек
            settings = XElement.Load(Path.Combine(currentDir, "Settings.xml"));

            //Считываем информацию о локализации ресурсов, установленной пользователем.
            string localization = settings.Element("CurrentResourcesLocalization").Value.Trim();

            //На основании указанной локализации, определяем, какой файл описания слоёв следует использовать в работе
            layersInfo = localization == string.Empty ? XElement.Load(Path.Combine(currentDir, "Layers.xml")) :
                XElement.Load(Path.Combine(currentDir, string.Format("Layers.{0}.xml", localization)));

            //Создаём словарь, в котором каждой интересующей нас команде сопоставляем нужный слой, согласно указанной локализации
            dict = new Dictionary<string, LayerTableRecord>();

            //получаем таблицу типов линий
            Document dwg = acad.DocumentManager.MdiActiveDocument;
            Database db = dwg.Database;
            LinetypeTable LineTypeTblRec;
            
            using (Transaction t = db.TransactionManager.StartTransaction())
            {
                //открытие таблицы типов линий для чтения                
                LineTypeTblRec = t.GetObject(db.LinetypeTableId, OpenMode.ForRead) as LinetypeTable;
                t.Commit();
            }

            //Заполняем словарь данными
            foreach (XElement layer in layersInfo.Elements("Layer"))
            {
                LayerTableRecord lt = new LayerTableRecord();
                lt.Name = layer.Element("Name").Value.Trim();
                lt.Description = layer.Element("Description").Value.Trim();
                lt.LineWeight = (LineWeight)Enum.Parse(typeof(LineWeight), layer.Element("LineWeight").Value.Trim());
                short color;
                color = Convert.ToInt16(layer.Element("Color").Value.Trim());
                lt.Color = Color.FromColorIndex(ColorMethod.ByAci, color);

                string lineTypeName;
                lineTypeName = layer.Element("LineType").Value.Trim();
                if (LineTypeTblRec.Has(lineTypeName))
                    lt.LinetypeObjectId = LineTypeTblRec[lineTypeName];

                dict.Add(layer.Attribute("Commands").Value.Trim(), lt);
            }
            //Подписываемся на событие открытия документа
            Application.DocumentManager.DocumentCreated += new DocumentCollectionEventHandler(DocumentManager_DocumentCreated);
            //Подготавливаем текущий чертёж
            MainMethod();
        }

        public void Terminate()
        {
        }

        private void MainMethod()
        {
            Document dwg = acad.DocumentManager.MdiActiveDocument;
            Database db = dwg.Database;

            #region Подписка на события обработки команд
            dwg.CommandWillStart += new CommandEventHandler(acDoc_CommandWillStart);
            dwg.CommandCancelled += new CommandEventHandler(acDoc_CommandEFC);
            dwg.CommandEnded += new CommandEventHandler(acDoc_CommandEFC);
            dwg.CommandFailed += new CommandEventHandler(acDoc_CommandEFC);
            #endregion

            //начало транзакции
            using (Transaction t = db.TransactionManager.StartTransaction())
            {
                //открытие таблицы слоев для чтения
                LayerTable layersTable;
                layersTable = t.GetObject(db.LayerTableId, OpenMode.ForRead) as LayerTable;

                //проверка, есть ли уже слои с таким же именем 
                foreach (KeyValuePair<string, LayerTableRecord> i in dict)
                {
                    if (!layersTable.Has(i.Value.Name))
                    {
                        layersTable.UpgradeOpen();
                        layersTable.Add(i.Value);
                        t.AddNewlyCreatedDBObject(i.Value, true);
                    }
                }
                t.Commit();
            }
        }

        //создание документа
        private void DocumentManager_DocumentCreated(object sender, DocumentCollectionEventArgs e)
        {
            MainMethod();
        }

        //начало команды
        private void acDoc_CommandWillStart(object sender, CommandEventArgs e)
        {
            //установка текущего слоя для размеров, текста и т.д...
            previousLayerName = (string)acad.GetSystemVariable("CLAYER");
            foreach (KeyValuePair<string, LayerTableRecord> item in dict)
            {
                if (item.Key.Split(';').Contains(e.GlobalCommandName))
                {
                    acad.SetSystemVariable("CLAYER", dict[item.Key].Name);
                    break;
                }
            }
        }

        //конец, ошибка, отмена команды
        private void acDoc_CommandEFC(object sender, CommandEventArgs e)
        {
            //восстановление текущего слоя
            foreach (KeyValuePair<string, LayerTableRecord> item in dict)
            {
                if (item.Key.Split(';').Contains(e.GlobalCommandName))
                {
                    acad.SetSystemVariable("CLAYER", previousLayerName);
                    break;
                }
            }
        }
    }
}

Путем практических исключений вывелось что проблема вот в этой строке метода MainMethod()
t.AddNewlyCreatedDBObject(i.Value, true);

Может база не готова к моменту когда меиод начинает работу... не знаю.
Просьба оказать посильную помощь кто может.

Autocad 2014 x64 Win7x64

Re: Автоматическое размещение объекта на нужном слое

Ошибка в моём коде. Сегодня подправить не смогу, т.к. уже убегаю. Смогу глянуть завтра.

Re: Автоматическое размещение объекта на нужном слое

Андрей, Ваши ресурсы я уже нашел. Штудирую. За ссылки спасибо. Ждем-с.

Re: Автоматическое размещение объекта на нужном слое

Баг исправлен. Код на странице обновлён, исходники перезалиты.

Re: Автоматическое размещение объекта на нужном слое

При открытии или создании нового файла происходит Fatal Error и акад захлопывается. В чем проблема не пойму.

Проблема была в том, что в таблицу слоёв добавлялись непосредственно записи из словаря dict, вместо того, чтобы создавать и добавлять их копии.

Re: Автоматическое размещение объекта на нужном слое

Денис Перепецкий пишет:

Может база не готова к моменту когда меTод начинает работу... не знаю.
Просьба оказать посильную помощь кто может.

Я думаю ошибка в том что документ не блокируется
при изменении системных переменных
добавь LockDocument перед транзакцией

Re: Автоматическое размещение объекта на нужном слое

Андрей пишет:

Проблема была в том, что в таблицу слоёв добавлялись непосредственно записи из словаря dict, вместо того, чтобы создавать и добавлять их копии.

Проблема была в этом.
Спасибо Андрей за код.

Так выглядит поправленный код:

using Autodesk.AutoCAD.ApplicationServices;
using acad = Autodesk.AutoCAD.ApplicationServices.Application;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Colors;
using Autodesk.AutoCAD.EditorInput;
using System.Linq;
using System.Collections;
using System.Collections.Generic;
using System.Xml.Linq;
using Autodesk.AutoCAD.Runtime;
using System.IO;
using System;
using System.Text;
using System.Threading.Tasks;
using Autodesk.AutoCAD.Geometry;

namespace Bushman.AutoCAD.Samples
{
    public class Class1 : IExtensionApplication
    {
        //Словарь, в котором каждой команде сопоставлен свой слой
        Dictionary<string, LayerTableRecord> dict;
        //Объекты общих настроек и настроек слоёв
        XElement settings, layersInfo;
        //Полное имя каталога, в котором находится данная сборка
        string currentDir;
        //Имя слоя, который был текущим до того, как была выполнена команда
        string previousLayerName;

        string localization;

        public void Initialize()
        {
            //Получаем путь к каталогу, в котором находится данная сборка
            currentDir = new FileInfo(this.GetType().Assembly.Location).DirectoryName;

            //Считываем данные файла настроек
            settings = XElement.Load(Path.Combine(currentDir, "Settings.xml"));

            //Считываем информацию о локализации ресурсов, установленной пользователем.
            localization = settings.Element("CurrentResourcesLocalization").Value.Trim();

            //На основании указанной локализации, определяем, какой файл описания слоёв следует использовать в работе
            layersInfo = localization == string.Empty ? XElement.Load(Path.Combine(currentDir, "Layers.xml")) : XElement.Load(Path.Combine(currentDir, string.Format("Layers.{0}.xml", localization)));

            //Создаём словарь, в котором каждой интересующей нас команде сопоставляем нужный слой, согласно указанной локализации
            dict = new Dictionary<string, LayerTableRecord>();

            //получаем таблицу типов линий
            Document dwg = acad.DocumentManager.MdiActiveDocument;
            Database db = dwg.Database;
            LinetypeTable LineTypeTblRec;
            using (Transaction t = db.TransactionManager.StartTransaction())
            {
                //открытие таблицы типов линий для чтения                
                LineTypeTblRec = t.GetObject(db.LinetypeTableId, OpenMode.ForRead) as LinetypeTable;
                t.Commit();
            }

            //Заполняем словарь данными
            foreach (XElement layer in layersInfo.Elements("Layer"))
            {
                LayerTableRecord lt = new LayerTableRecord();
                lt.Name = layer.Element("Name").Value.Trim();
                lt.Description = layer.Element("Description").Value.Trim();
                lt.LineWeight = (LineWeight)Enum.Parse(typeof(LineWeight), layer.Element("LineWeight").Value.Trim());
                short color;
                color = Convert.ToInt16(layer.Element("Color").Value.Trim());
                lt.Color = Color.FromColorIndex(ColorMethod.ByAci, color);

                string lineTypeName;
                lineTypeName = layer.Element("LineType").Value.Trim();
                if (LineTypeTblRec.Has(lineTypeName))
                    lt.LinetypeObjectId = LineTypeTblRec[lineTypeName];

                dict.Add(layer.Attribute("Commands").Value.Trim(), lt);
            }
            //Подписываемся на событие открытия документа
            Application.DocumentManager.DocumentCreated += new DocumentCollectionEventHandler(DocumentManager_DocumentCreated);
            //Подготавливаем текущий чертёж
            MainMethod();
        }


        public void Terminate()
        {
        }

        private void MainMethod()
        {
            //ed.WriteMessage(currentDir);
            Document dwg = acad.DocumentManager.MdiActiveDocument;
            Database db = dwg.Database;

            #region Подписка на события обработки команд
            dwg.CommandWillStart += new CommandEventHandler(acDoc_CommandWillStart);
            dwg.CommandCancelled += new CommandEventHandler(acDoc_CommandEFC);
            dwg.CommandEnded += new CommandEventHandler(acDoc_CommandEFC);
            dwg.CommandFailed += new CommandEventHandler(acDoc_CommandEFC);
            #endregion

            //начало транзакции
            using (Transaction t = db.TransactionManager.StartTransaction())
            {
                //открытие таблицы слоев для чтения
                LayerTable layersTable;
                layersTable = (LayerTable)t.GetObject(db.LayerTableId, OpenMode.ForRead);

                //проверка, есть ли уже слои с таким же именем 
                foreach (KeyValuePair<string, LayerTableRecord> i in dict)
                {
                    if (!layersTable.Has(i.Value.Name))
                    {
                        layersTable.UpgradeOpen();
                        SymbolTableRecord layer = Clone(i.Value);
                        layersTable.Add(layer);
                        t.AddNewlyCreatedDBObject(layer, true);
                    }
                }
                t.Commit();
            }
        }

        //копируем слой
        private SymbolTableRecord Clone(LayerTableRecord layerTableRecord)
        {
            if (layerTableRecord == null)
                throw new ArgumentNullException("layerTableRecord");

            LayerTableRecord result = new LayerTableRecord();
            result.Name = layerTableRecord.Name;
            result.Description = layerTableRecord.Description;
            result.LineWeight = layerTableRecord.LineWeight;
            result.Color = layerTableRecord.Color;
            return result;
        }

        //создание документа
        private void DocumentManager_DocumentCreated(object sender, DocumentCollectionEventArgs e)
        {
            MainMethod();
        }

        //начало команды
        private void acDoc_CommandWillStart(object sender, CommandEventArgs e)
        {
            //установка текущего слоя для размеров, текста и т.д...
            previousLayerName = (string)acad.GetSystemVariable("CLAYER");
            foreach (KeyValuePair<string, LayerTableRecord> item in dict)
            {
                if (item.Key.Split(';').Contains(e.GlobalCommandName))
                {
                    acad.SetSystemVariable("CLAYER", dict[item.Key].Name);
                    break;
                }
            }
        }

        //конец, ошибка, отмена команды
        private void acDoc_CommandEFC(object sender, CommandEventArgs e)
        {
            //восстановление текущего слоя
            foreach (KeyValuePair<string, LayerTableRecord> item in dict)
            {
                if (item.Key.Split(';').Contains(e.GlobalCommandName))
                {
                    acad.SetSystemVariable("CLAYER", previousLayerName);
                    break;
                }
            }
        }
    }
}

Re: Автоматическое размещение объекта на нужном слое

Денис Перепецкий пишет:

Так выглядит поправленный код:

Т.е. проблема решена, как я понял? Подправленный код не смотрел.

Re: Автоматическое размещение объекта на нужном слое

да проблема решена.

добавляем

//копируем слой 
        private SymbolTableRecord Clone(LayerTableRecord layerTableRecord) 
        { 
            if (layerTableRecord == null) 
                throw new ArgumentNullException("layerTableRecord"); 

            LayerTableRecord result = new LayerTableRecord(); 
            result.Name = layerTableRecord.Name; 
            result.Description = layerTableRecord.Description; 
            result.LineWeight = layerTableRecord.LineWeight; 
            result.Color = layerTableRecord.Color; 
            return result; 
        } 

и меняем

layersTable.Add(i.Value); 
                        t.AddNewlyCreatedDBObject(i.Value, true); 

на

SymbolTableRecord layer = Clone(i.Value); 
                        layersTable.Add(layer); 
                        t.AddNewlyCreatedDBObject(layer, true); 

и все работает.

Проверено на Win7x64 + AutoCAD 2014.
Спасибо еще раз.

Re: Автоматическое размещение объекта на нужном слое

Вообще-то, в моём коде помимо Clone была добавлена и блокировка документа.

P.S. Кстати выше fixo об этом так же написал.

Re: Автоматическое размещение объекта на нужном слое

А почему нужно

SymbolTableRecord layer = Clone(i.Value); 

и писать отдельный метод Clone() если можно

SymbolTableRecord layer = i.Value.Clone() as SymbolTableRecord;

:?:

Re: Автоматическое размещение объекта на нужном слое

Изначально так и сделал, но затем подумав, всё же решил на всякий случай перестраховаться, т.к. не знаю, как он там реализован в RXObject.