Сохранение значений параметров команд между сеансами работы AutoCAD

Дата публикации: 21.09.2010
Состояние: завершена
Дата изменения: не изменялась

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

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

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

Видео YouTube



    А теперь, собственно, посмотрим и сам исходный код (тестировалось под AutoCAD 2009 x86 Enu SP3)... Изначально файл MySettings.xml будет иметь такой вид (т.е. когда настроек в нём ещё нет):

   1:  <?xml version="1.0" encoding="utf-8" ?>
   2:  <!--Не стоит слепо копировать наименования элементов и их атрибутов - они назначены совершенно произвольно. Назначайте свои, 
   3:  какие вам больше нравятся/подходят по смыслу для ваших настроек.-->
   4:  <Settings>
   5:    <!--Блок данных, в котором храним те значения различных переменных, которые нужно сохранять между сеансами работы AutoCAD-->
   6:    <PreviousValues>    
   7:    </PreviousValues>
   8:  </Settings>

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

   1:  <?xml version="1.0" encoding="utf-8" ?>
   2:  <!--Не стоит слепо копировать наименования элементов и их атрибутов - они назначены совершенно произвольно. Назначайте свои, 
   3:  какие вам больше нравятся/подходят по смыслу для ваших настроек.-->
   4:  <Settings>
   5:    <!--Блок данных, в котором храним те значения различных переменных, которые нужно сохранять между сеансами работы AutoCAD-->
   6:    <PreviousValues>
   7:      <!--Ради примера, первые три записи будут упакованы в PreviousValue-->
   8:      <PreviousValue Name="TestInt" Value="7" Description="Некоторое целое число"/>
   9:      <PreviousValue Name="TestDouble" Value="5.55" Description="Некоторое действительное число"/>
  10:      <PreviousValue Name="TestString" Value="Мама мыла раму" Description="Некоторая строка"/>
  11:      <!--А следующий тип будет индивидуальным (я демонстрирую, что имена типов и их атрибутов можно назначать любыми). В качестве
  12:      примера использую примитив AutoCAD - Point3d (в качестве разделителя будем использовать точку, можно вообще не указывать 
  13:      десятичную часть, как это сделано для координаты Z)-->
  14:      <!--А это примитив AutoCAD (в качестве разделителя будем использовать точку. Можно вообще не указывать 
  15:      десятичную часть, как это сделано для координаты Z)-->
  16:      <MyPoint3d AcadPointName="AnyPoint" X="15.345" Y="345.1" Z="45" MyDescription="Некоторая точка"/>
  17:    </PreviousValues>
  18:  </Settings>

Далее исходный код нашего cs-файла:

   1:  //Microsoft
   2:  using System;
   3:  using System.Collections.Generic;
   4:  using System.Linq;
   5:  using System.Text;
   6:  using System.Reflection;
   7:  using System.IO;
   8:  using System.Xml.Linq;
   9:  //Autodesk
  10:  using acad = Autodesk.AutoCAD.ApplicationServices.Application;
  11:  using Autodesk.AutoCAD.ApplicationServices;
  12:  using Autodesk.AutoCAD.DatabaseServices;
  13:  using Autodesk.AutoCAD.EditorInput;
  14:  using Autodesk.AutoCAD.Runtime;
  15:  using Autodesk.AutoCAD.Geometry;
  16:   
  17:  namespace AcadCommands1
  18:  {
  19:      public class Class1
  20:      {
  21:          DocumentCollection dm = acad.DocumentManager;
  22:          Document dwg;
  23:          Database db;
  24:          Editor ed;
  25:          const string ns = "gpsm";//пространство имён команд
  26:   
  27:          /// <summary>
  28:          /// Конструктор по умолчанию
  29:          /// </summary>
  30:          public Class1()
  31:          {
  32:              Drawing = acad.DocumentManager.MdiActiveDocument;
  33:          }
  34:   
  35:          /// <summary>
  36:          /// Чертёж
  37:          /// </summary>
  38:          Document Drawing
  39:          {
  40:              get { return dwg; }
  41:              set
  42:              {
  43:                  dwg = value;
  44:                  if (dwg != null)
  45:                  {
  46:                      db = dwg.Database; ed = dwg.Editor;
  47:                  }
  48:                  else
  49:                  {
  50:                      db = null; ed = null;
  51:                  }
  52:              }
  53:          }
  54:   
  55:          [CommandMethod(ns, "cmd", CommandFlags.Modal)]
  56:          public void Cmd()
  57:          {
  58:              ///Для начала нужно определиться с тем, где будет размещаться xml-файл, хранящий в себе настройки, 
  59:              ///которые должны сохраняться между сеансами работы AutoCAD. В своём примере я исхожу из предположения,
  60:              ///что обозначенный файл хранится в том же каталоге, где и данная библиотека и имеет имя 'MySettings.xml'.
  61:              string xmlFileName = "MySettings.xml";
  62:              //Определяем полное имя каталога, в котором размещается файл 'MySettings.xml'.
  63:              string curDir = new FileInfo(Assembly.GetAssembly(this.GetType()).Location).Directory.FullName;
  64:              //Формируем полное имя xml-файла настроек.
  65:              string xmlFullName = Path.Combine(curDir, xmlFileName);            
  66:              //Проверяем наличие файла
  67:              if (!File.Exists(xmlFullName)) { ed.WriteMessage(string.Format("\nФайл '{0}' не найден.\n", xmlFullName)); return; }
  68:   
  69:              //Создаём объект, с помощью которого будем работаь с содержимым файла 'MySettings.xml' (чтение/удаление/изменение/запись).
  70:              XElement xml = XElement.Load(xmlFullName);
  71:              
  72:              //Пусть в нашем xml-файле хранятся данные разных типов, которые и будут использоваться в примере (ниже приведены наиболее часто употребляемые):
  73:              int valInt = 0;
  74:              double valDouble = 0;
  75:              string valString = string.Empty;
  76:              Point3d point = Point3d.Origin;
  77:   
  78:              try
  79:              {
  80:                  
  81:                  ///Если это первый запуск команды, то возможно, что в xml-файле ещё нет записей, хранящих нужные нам значения. В таком
  82:                  ///случае эти записи нужно создавать программно (что и будет продемонстрировано далее в коде)
  83:   
  84:                  //Извлекаем из блока 'PreviousValues' элементы 'PreviousValue' (их там у нас несколько), и выбираем из них такой, чей атрибут 'Name' имеет значение 'TestInt'.
  85:                  //Если нужного элемента нет, то переменной xInt должно присваиваться значение по умолчанию, т.е. null.
  86:                  XElement xInt = xml.Element("PreviousValues").Elements("PreviousValue").Where(n => n.Attribute("Name").Value == "TestInt").DefaultIfEmpty().First();
  87:   
  88:                  //Аналогичную операцию проделываем для переменных valDouble и valString
  89:                  XElement xDouble = xml.Element("PreviousValues").Elements("PreviousValue").Where(n => n.Attribute("Name").Value == "TestDouble").DefaultIfEmpty().First();
  90:                  XElement xString = xml.Element("PreviousValues").Elements("PreviousValue").Where(n => n.Attribute("Name").Value == "TestString").DefaultIfEmpty().First();
  91:   
  92:                  //Переменная point у нас сохранена в ином элементе, представленном в единственном виде (т.е. в блоке PreviousValues всего
  93:                  //один элемент MyPoint3d - соответственно можно обойтись без фильтрации. Следует обратить внимание, что для MyPoint3d используется не Elements, а Element.
  94:                  XElement xPoint3d = xml.Element("PreviousValues").Element("MyPoint3d");
  95:   
  96:                  //*****************************************************************************************
  97:                  //Запрашиваем у пользователя новое значение для некоторого целочисленного параметра
  98:                  PromptIntegerOptions pio = new PromptIntegerOptions("\nЗадайте новое значение целого числа:") {AllowNone = true, AllowNegative = true, 
  99:                      DefaultValue = xInt == null ? valInt : Int32.Parse(xInt.Attribute("Value").Value), UseDefaultValue = true };
 100:                  PromptIntegerResult pir = ed.GetInteger(pio);
 101:                  if (pir.Status == PromptStatus.OK)
 102:                  {
 103:                      valInt = pir.Value;
 104:                      //Если в xml-файле ещё не было сохранено значение нужной нам некоторой целочисленной переменной, то записываем в этот файл указанное пользователем
 105:                      //значение
 106:                      if (xInt == null)
 107:                      {
 108:                          xml.Element("PreviousValues").Add(new XElement("PreviousValue", new XAttribute("Name", "TestInt"), new XAttribute("Value", valInt),
 109:                              new XAttribute("Description", "Некоторое целое число")));
 110:                      }
 111:                      //Если в xml-файле уже имеется запись с сохранённым интересующим нас значением, то перезаписываем его
 112:                      else
 113:                      {
 114:                          xInt.Attribute("Value").Value = valInt.ToString();
 115:                      }
 116:                  }
 117:                  //********************************************************************************************
 118:                  //Подобно тому, как это сделано для целочисленного параметра, выполняем операцию над другими типами:
 119:   
 120:                  //Запрашиваем у пользователя новое значение для некоторого действительного числового параметра
 121:                  PromptDoubleOptions pdo = new PromptDoubleOptions("\nЗадайте новое значение действительного числа:")
 122:                  {
 123:                      AllowNone = true,
 124:                      AllowNegative = true,
 125:                      DefaultValue = xDouble == null ? valDouble : Double.Parse(xDouble.Attribute("Value").Value),
 126:                      UseDefaultValue = true
 127:                  };
 128:                  PromptDoubleResult pdr = ed.GetDouble(pdo);
 129:                  if (pdr.Status == PromptStatus.OK)
 130:                  {
 131:                      valDouble = pdr.Value;
 132:                      //Если в xml-файле ещё не было сохранено значение нужной нам некоторой действительной числовой переменной, то записываем в этот файл указанное пользователем
 133:                      //значение
 134:                      if (xDouble == null)
 135:                      {
 136:                          xml.Element("PreviousValues").Add(new XElement("PreviousValue", new XAttribute("Name", "TestDouble"), new XAttribute("Value", valDouble),
 137:                              new XAttribute("Description", "Некоторое действительное число")));
 138:                      }
 139:                      //Если в xml-файле уже имеется запись с сохранённым интересующим нас значением, то перезаписываем его
 140:                      else
 141:                      {
 142:                          xDouble.Attribute("Value").Value = valDouble.ToString();
 143:                      }
 144:                  }
 145:                  //Запрашиваем у пользователя новое значение для некоторого строкового параметра
 146:                  PromptStringOptions pso = new PromptStringOptions("\nЗадайте новое значение строковой переменной:")
 147:                  {
 148:                      DefaultValue = xString == null ? valString : xString.Attribute("Value").Value, UseDefaultValue = true};
 149:                  PromptResult pr = ed.GetString(pso);
 150:                  if (pr.Status == PromptStatus.OK)
 151:                  {
 152:                      valString = pr.StringResult;
 153:                      //Если в xml-файле ещё не было сохранено значение нужной нам некоторой строковой переменной, то записываем в этот файл указанное пользователем
 154:                      //значение
 155:                      if (xString == null)
 156:                      {
 157:                          xml.Element("PreviousValues").Add(new XElement("PreviousValue", new XAttribute("Name", "TestString"), new XAttribute("Value", valString),
 158:                              new XAttribute("Description", "Некоторая строка")));
 159:                      }
 160:                      //Если в xml-файле уже имеется запись с сохранённым интересующим нас значением, то перезаписываем его
 161:                      else
 162:                      {
 163:                          xString.Attribute("Value").Value = valString;
 164:                      }
 165:                  }
 166:                  //Запрашиваем у пользователя новое значение для некоторой точки
 167:                  PromptPointOptions ppo = new PromptPointOptions("\nЗадайте новое значение переменной, хранящей координаты точки:"){AllowNone = true}; 
 168:                  //    BasePoint = xPoint3d == null ? point : new Point3d(Double.Parse(xPoint3d.Attribute("X").Value), Double.Parse(xPoint3d.Attribute("Y").Value), 
 169:                  //        Double.Parse(xPoint3d.Attribute("Z").Value))};
 170:                  PromptPointResult ppr = ed.GetPoint(ppo);
 171:                  if (ppr.Status == PromptStatus.OK)
 172:                  {
 173:                      point = ppr.Value;
 174:                      //Если в xml-файле ещё не было сохранено значение нужной нам некоторой строковой переменной, то записываем в этот файл указанное пользователем
 175:                      //значение
 176:                      if (xPoint3d == null)
 177:                      {
 178:                          xml.Element("PreviousValues").Add(new XElement("MyPoint3d", new XAttribute("AcadPointName", "AnyPoint"), new XAttribute("X", point.X),
 179:                              new XAttribute("Y", point.Y), new XAttribute("Z", point.Z), new XAttribute("Description", "Некоторая точка")));
 180:                          ed.WriteMessage(string.Format("\nПроизошло добавление в xml-файл записи, хранящей координаты точки: '{0}'\n", point.ToString()));
 181:                      }
 182:                      //Если в xml-файле уже имеется запись с сохранённым интересующим нас значением, то перезаписываем его
 183:                      else
 184:                      {
 185:                          xPoint3d.Attribute("X").Value = point.X.ToString();
 186:                          xPoint3d.Attribute("Y").Value = point.Y.ToString();
 187:                          xPoint3d.Attribute("Z").Value = point.Z.ToString();
 188:                          ed.WriteMessage(string.Format("\nПроизошло пересохранение в xml-файл записи, хранящей координаты точки. Новое значение: {0}\n", point.ToString()));
 189:                      }
 190:                  }
 191:                  else if (ppr.Status == PromptStatus.None)
 192:                  {
 193:                      point = ppr.Value;
 194:                      //Если в xml-файле ещё не было сохранено значение нужной нам некоторой строковой переменной, то записываем в этот файл указанное пользователем
 195:                      //значение
 196:                      if (xPoint3d == null)
 197:                      {
 198:                          ed.WriteMessage("\nXml-файл не содержит записи с координатами точки, поэтому воспользоваться значением 'по умолчанию' не удастся.");
 199:                      }
 200:                      //Если в xml-файле уже имеется запись с сохранённым интересующим нас значением, то перезаписываем его
 201:                      else
 202:                      {
 203:                          point = new Point3d(Double.Parse(xPoint3d.Attribute("X").Value), Double.Parse(xPoint3d.Attribute("Y").Value), 
 204:                              Double.Parse(xPoint3d.Attribute("Z").Value));
 205:                          ed.WriteMessage(string.Format("\nСчитываем координаты точки из xml-файла: {0}\n", point.ToString()));
 206:                      }
 207:                  }
 208:   
 209:                  //Сохраняем выполненные изменения
 210:                  xml.Save(xmlFullName);                
 211:              }
 212:              catch (System.Exception ex)
 213:              {
 214:                  ed.WriteMessage(string.Format("\nОшибка: '{0}'\n", ex.Message));
 215:              }
 216:          }
 217:      }

Исходники здесь.

    P.S. В проекте используется MS .NetFramework 3.5 SP1.

Примечание:
    Дополнительно по теме можно глянуть эту статью.



Comments