TheSwamp
Code Red => .NET => Topic started by: David Hall on January 03, 2008, 01:30:00 PM
-
What Im looking for is suggestions on how to code this better, or am I doing pretty well. I went back to some really old code from when C# first started working with acad, and tried to improve what I did back then. Anyway, comments are welcome
namespace LayerCode
{
public class LayerCode
{
[CommandMethod("CL1")]
public void CreateLayer1()
{
Database DB = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument.Database;
using (Transaction tr = DB.TransactionManager.StartTransaction())
{
LayerTable LT = (LayerTable)DB.LayerTableId.GetObject(Autodesk.AutoCAD.DatabaseServices.OpenMode.ForWrite);
if (LT.Has("DSH"))
{
tr.Abort();
}
else
{
LayerTableRecord LTR = new Autodesk.AutoCAD.DatabaseServices.LayerTableRecord();
LTR.Name = "DSH";
LT.Add(LTR);
tr.AddNewlyCreatedDBObject(LTR, true);
tr.Commit();
}
}
}
}
}
-
I think it looks good except for one thing. Tony T. has pointed out that the Has() method will return items that are marked to be erased, meaning that they don't really exist in the drawing, and will be lost when the drawing is closed. So what he did was use a sub function to search the whole table checking the name and the IsErased (I think) property, and if both were correct, it would return that item. I will see if I can find the post, as it was posted here.
Edit: Here you go. (http://www.theswamp.org/index.php?topic=12123.msg150999#msg150999)
-
Here's how I'd do it:
[CommandMethod("CreateDuhLayer")]
static public void createduhlayer()
{
Document doc = acadApp.DocumentManager.MdiActiveDocument;
Database db = doc.Database;
Editor ed = doc.Editor;
using (Transaction tr = db.TransactionManager.StartTransaction()) {
LayerTable lt = tr.GetObject(db.LayerTableId, OpenMode.ForRead, false) as LayerTable;
if (lt.Has("Some LayerNameGoesHere"))
return;
LayerTableRecord newLTR = new LayerTableRecord();
newLTR.Name = "SomeLayerNameGoesHere";
// set any other properties
lt.UpgradeOpen();
lt.Add(newLTR);
tr.AddNewlyCreatedDBObject(newLTR, true);
tr.Commit();
}
}
Cheers,
Glenn.
-
and another.. http://www.theswamp.org/index.php?topic=7918.msg100511#msg100511
-
Thanks Kerry. I saw that thread, and I have to admit, you had so much going on, I was lost :-(
Glenn, In your IF statement, did you forget the {} or are they not needed? Also, when you opened the Layertable for read, what is the false for? I'm going to see if I can find that answer, on my own, just curious if you had a quick answer
-
the LayerTable.UpgradeOpen is b/c you first accessed it ForRead, now you want to ForWrite - correct?
-
Also, Glenn, the code you posted is being used as a sub, and the return; sends it back to another Sub/Funcion? If not, im confused on what the return; is doing. I didn't see you passing a "Name" to this function to check for existence, so I guess i'm just confused
-
Duh,
Regarding the IF - you don't need {} if you're only executing one line - more than one and you need the {} (rather like lisp nad it's if statements and progn's)
The 'false' is "don't open erased records". Most code I've seen doesn't use the third argument whereas I always use it (habits from ARX I suppose).
WRT UpGradeOpen, yes you're right. The reason you do this is to minimise the time you have things open for write - other things will want to open for write as well, so always keep it as short as possible.
Dan mentioned something I was going to and what he said happens to ALL the SymbolTables and it occurs using the default indexer as well eg. lt["SomeLayerNameGoesHere"]
Cheers,
Glenn.
-
Also, Glenn, the code you posted is being used as a sub, and the return; sends it back to another Sub/Funcion? If not, im confused on what the return; is doing. I didn't see you passing a "Name" to this function to check for existence, so I guess i'm just confused
You'll notice the method has been decorated with the CommandMethod attribute, so this is the command definition. The return will just return out of this method/function and hand control back to AutoCAD.
-
One other thing: This is C#, so stop thinking of them as Sub's and forget that VB drivel :evil: :-D
Curly braces rule :-D
-
BTW, happy birthday as well...get some code books did we ;)
Cheers,
Glenn.
-
You'll notice the method has been decorated with the CommandMethod attribute, so this is the command definition. The return will just return out of this method/function and hand control back to AutoCAD.
COOL!! I learned something new today!!! Dont read into this more than what Im saying, but its like an internal ESC key for the function. Its there so quit the Command(CommandMethod). VERY nice. I love .Net
-
The 'false' is "don't open erased records". Most code I've seen doesn't use the third argument whereas I always use it (habits from ARX I suppose).
Hi,
I am curious about something. By setting the third argument to false, does that negate what Tim stated about the "Has" method will return items that are marked to be erased?
-
You're welcome Duh - learning something is always good in my book.
I suppose, thinking about it, that the VB equivalent of return; (a blank return returning nothing) would be Exit Sub...
Cheers,
Glenn.
-
The 'false' is "don't open erased records". Most code I've seen doesn't use the third argument whereas I always use it (habits from ARX I suppose).
Hi,
I am curious about something. By setting the third argument to false, does that negate what Tim stated about the "Has" method will return items that are marked to be erased?
Nick,
No it won't. To tell the truth, it was pretty superfluous in this case because if the LayerTable ITSELF was erased, your drawing would be cactus.
You would mainly use the construct I showed when, for instance, you were looping over the layertablerecords, which is totally different to just calling the LayerTable.Has() function, which internally does a loop, but will return erased records...it's a nasty little trap which they still haven't fixed.
Cheers,
Glenn.
-
In my testing, LayerTable.Has() returns the correct result. It's just that if you then do LayerTable[layerName], you may get an erased version of the layer, and trying to use that layer will cause you problems.
-
You'll notice the method has been decorated with the CommandMethod attribute, so this is the command definition. The return will just return out of this method/function and hand control back to AutoCAD.
COOL!! I learned something new today!!! Dont read into this more than what Im saying, but its like an internal ESC key for the function. Its there so quit the Command(CommandMethod). VERY nice. I love .Net
There is a school of thought that it is bad form to exit out of the middle of a method in this fashion. There is a whole class of potential bugs that can created by doing that.
However, the alternative is usually to use a conditional statement, which adds a layer of braces, and makes the code a bit messier and harder to read. A better solution also involves using a conditional statement, but then breaking the rest of the logic out into other methods. Sometimes this provides a much-better and more-robust solution with more-generic applicability, but sometimes it's just more work for little gain. So some people like to have the return in the middle of the method.
I tend to agree with the hardliners that it is "bad form" to return from the middle of a method, and usually try to return from the ends of methods. But I'm not strict about it, and I occasionally return from the middle of a method, for one of the reasons I just mentioned.
-
One other thing: This is C#, so stop thinking of them as Sub's and forget that VB drivel :evil: :-D
Curly braces rule :-D
So think of them as methods right?
..get some code books did we
Kinda, I got a book at AU which I'm using/abusing to try and help me learn this stuff. I am finding that while it does give me an example of "working" code, you guys are showing me better and faster ways of doing things.
-
Methods or functions - I tend to think of them as functions, but that's just personal preference.
Glad you're liking it and getting into the 'heavier' end - you can definately do more with .NET.
-
And while I'm asking tons of questions, why the AS ?
LayerTable lt = tr.GetObject(db.LayerTableId, OpenMode.ForRead, false) as LayerTable;
vs
LayerTable LT = (LayerTable)DB.LayerTableId.GetObject(Autodesk.AutoCAD.DatabaseServices.OpenMode.ForWrite);
Is it b/c your using the .GetObject of the transaction instead of the database.layertableid.getobject? I saw a thread on Through-the-Interface talking about the AS, but I didn't understand what they said.
-
This line will throw an exception if objId is the id for something that is not a layer table:
LayerTable lt = (LayerTable)tr.GetObject(objId, OpenMode.ForRead, false);
This line will result in lt == null if objId is the id for something that is not a layer table:
LayerTable lt = tr.GetObject(db.LayerTableId, OpenMode.ForRead, false) as LayerTable;
-
Methods or functions - I tend to think of them as functions, but that's just personal preference.
Glad you're liking it and getting into the 'heavier' end - you can definately do more with .NET.
Well, you might say Methods belong to an object, while Functions do not.
-
And while I'm asking tons of questions, why the AS ?
LayerTable lt = tr.GetObject(db.LayerTableId, OpenMode.ForRead, false) as LayerTable;
vs
LayerTable LT = (LayerTable)DB.LayerTableId.GetObject(Autodesk.AutoCAD.DatabaseServices.OpenMode.ForWrite);
Is it b/c your using the .GetObject of the transaction instead of the database.layertableid.getobject? I saw a thread on Through-the-Interface talking about the AS, but I didn't understand what they said.
Well, there's IS and AS. Is will test if an object IS some type of object, whereas AS will try and cast some object to another object.
When you do this:
(LayerTable)DB.LayerTableId blah blah
you are actually doing what's called a direct cast - casting directly from the return type of DbObject, in this example, to a LayerTable.
When you use AS, it will do the cast for you and either return a valid object or null and, here's the most important bit, AS will not throw an exception whereas a direct cast will.
Also, I would recommend not using the GetObject() method off an ObjectId and use GetObject() off the transaction you've started because the intent will be much clearer, but that's just my opinion.
Cheers,
Glenn.
-
Well, there's IS and AS. Is will test if an object IS some type of object, whereas AS will try and cast some object to another object.
When you do this:
(LayerTable)DB.LayerTableId blah blah
you are actually doing what's called a direct cast - casting directly from the return type of DbObject, in this example, to a LayerTable.
OK, good, I knew I was casting. I didn't know this was "direct" cast which could fail.
When you use AS, it will do the cast for you and either return a valid object or null and, here's the most important bit, AS will not throw an exception whereas a direct cast will.
So we need to test for Null? How else would we know it worked?
Also, I would recommend not using the GetObject() method off an ObjectId and use GetObject() off the transaction you've started because the intent will be much clearer, but that's just my opinion.
Cheers,
Glenn.
Noted, and appreciated! I have a hard enough time reading my own drivel, let alone someone else trying to figure out what Im thinking. On a side note, I had someone call me and ask what 'Spit the Dummy' meant b/c it popped up on their screen one day
-
So we need to test for Null? How else would we know it worked?
That's exactly right. So, revising what I posted earlier in this thread, you would do this:
[CommandMethod("CreateDuhLayer")]
static public void createduhlayer()
{
Document doc = acadApp.DocumentManager.MdiActiveDocument;
Database db = doc.Database;
Editor ed = doc.Editor;
using (Transaction tr = db.TransactionManager.StartTransaction()) {
LayerTable lt = tr.GetObject(db.LayerTableId, OpenMode.ForRead, false) as LayerTable;
if (lt == null)
return;
if (lt.Has("Some LayerNameGoesHere"))
return;
LayerTableRecord newLTR = new LayerTableRecord();
newLTR.Name = "SomeLayerNameGoesHere";
// set any other properties
lt.UpgradeOpen();
lt.Add(newLTR);
tr.AddNewlyCreatedDBObject(newLTR, true);
tr.Commit();
}
}
Cheers,
Glenn.
-
On a side note, I had someone call me and ask what 'Spit the Dummy' meant b/c it popped up on their screen one day
:lmao:
-
One last question and I'll stop beating this dead horse for today. If in the new version you posted, if the return; is called, the transaction is aborted/disposed of properly because the function loses scope? Should we use tr.Abort() or not, and if not, when would we use tr.Abort() ?
-
I found this thread on direct casting from Through the Interface:
http://through-the-interface.typepad.com/through_the_interface/2006/09/working_with_sp.html
Some thoughts from the Autodesk labs. All the code below is taken from Lab 5.
Direct casting is used through out for layertable, blocktable and blocktablerecord in the Labs. With these is there ever a chance it could not return these objects?
In the labs direct casting is used with try catch statements where the exception is used in similar way to if-else statement.
They use entity within a block here:
Entity ent = (Entity)trans.GetObject(id, OpenMode.ForRead, false);
if (ent is AttributeDefinition)
Below code is called on items selected by the user: Is Gettype() and typeof() interchangeable with IS as shown above?
Entity ent = (Entity)trans.GetObject(employeeId, OpenMode.ForRead, false); // Use it to open the current object!
if (ent.GetType() == typeof(BlockReference)) // We use .NET's RTTI to establish type.
This is their layer create:
private ObjectId CreateLayer()
{
ObjectId layerId;
Database db = HostApplicationServices.WorkingDatabase;
using (Transaction trans = db.TransactionManager.StartTransaction())
{
//Get the layer table first...
LayerTable lt = (LayerTable)trans.GetObject(db.LayerTableId, OpenMode.ForRead);
//Check if EmployeeLayer exists...
if (lt.Has("EmployeeLayer"))
{
layerId = lt["EmployeeLayer"];
}
else
{
//If not, create the layer here.
LayerTableRecord ltr = new LayerTableRecord();
ltr.Name = "EmployeeLayer"; // Set the layer name
ltr.Color = Color.FromColorIndex(ColorMethod.ByAci, 2);
// upgrade the open from read to write
lt.UpgradeOpen();
layerId = lt.Add(ltr);
trans.AddNewlyCreatedDBObject(ltr, true);
}
trans.Commit();
}
return layerId;
}
******************EDIT***********************
I thought I would run a test on Lab 5
Ran the command and then purged and then ran command again :evil:
It threw an exception here:
BlockTableRecord empBtr = (BlockTableRecord)trans.GetObject(bt["EmployeeBlock"], OpenMode.ForRead);
It detected that the block and layer had been deleted and created them again so not sure what is happening here.
-
One last question and I'll stop beating this dead horse for today. If in the new version you posted, if the return; is called, the transaction is aborted/disposed of properly because the function loses scope? Should we use tr.Abort() or not, and if not, when would we use tr.Abort() ?
Well, strictly speaking, the 'using' scope is exited first and that in turn calls Dispose() on whatever was being 'used'...in this case, a transaction. Now, if Dispose() is called on a transaction and it hasn't been commited, then an implicit Abort() is called on the transaction and then it's memory is cleaned up.
Then of course it bails out of the function.
Cheers,
Glenn.
-
I've been able to fix lab 5 by adding true on the end - letting it open deleted objects:
BlockTableRecord empBtr = (BlockTableRecord)trans.GetObject(bt["EmployeeBlock"], OpenMode.ForRead, true);
I find this strange - the block and layer are created and trans.commit() is called. :| Its a nested transaction though.
My guess here is that since the block and layer are created with in a nested transaction the database has not been updated and still registers it deleted until the very final trans.commit() is called? Nope I denested them and same problem but at least now the layer and block appear in drawing even though it throws the exception.
THis is probably a better fix - use the objectID instead of block name - it works to and don't need to let it use deleted objects:
BlockTableRecord empBtr = (BlockTableRecord)trans.GetObject(blockId, OpenMode.ForRead);
LOL well after all that you can purge layers/blocks and layertable.has("layername") and blocktable.has("blockname") will work to say they don't exisit
But don't use block names to get the block from blocktable because even if you create it again after purging it still says its erased until you open and close drawing. Weird.
-
I would really recommend you don't do that. If you try to use an erased layer for something, you might get some strange results.
It is far better to use the PInvoke solution Tony T. posted here (http://www.theswamp.org/index.php?topic=12123.msg151231#msg151231).
Alternatively, you could use the Com object. This works fine, as well:
public static ObjectId GetLayerObjectId(LayerTable table, string name)
{
AcadLayers layerTable = table.AcadObject as AcadLayers;
AcadLayer layer = layerTable.Item(name);
if (layer == null)
return ObjectId.Null;
else
return DBObject.FromAcadObject(layer);
}
-
Not to ask a dumb question, but here goes. I thought we were trying to NOT use com? Isn't it slower blah blah blah and stuff?
I never learned it b/c I have heard horror stories and the managed api is better.
-
I thought we were trying to NOT use com? Isn't it slower blah blah blah and stuff?
Correct :)
-
Not to ask a dumb question, but here goes. I thought we were trying to NOT use com? Isn't it slower blah blah blah and stuff?
I never learned it b/c I have heard horror stories and the managed api is better.
Yeah, but this is to get around a bug in the managed API.
The REAL solution is for AUTODESK TO FIX THE API!!!! But it's been like this for more than three years now, and it's a known problem, and they still haven't fixed it... :ugly:
-
Isn't it slower blah blah blah and stuff?
And this isn't right. COM is actually faster.
The .NET API is not a real framework. Maybe someday. But right now, it is simply a managed wrapper around the COM code. So it's actually SLOWER. Underneath, it amounts to using COM indirectly. It's just much easier to program in, and the performance hit isn't noticeable for most things. Or for areas where the performance hit of the managed API is not acceptable, you can write something in C++ and call it from your managed code, getting "the best of both worlds".
-
Isn't it slower blah blah blah and stuff?
it is simply a managed wrapper around the COM code. So it's actually SLOWER. ...
This is not a correct statement. Managed code wraps native code directly,
without the use of COM, I am fairly certain that well written .NET is faster than COM.
-
So what we can do is drop down into C++/CLI and write our own managed wrapper for AcDbSymbolTable::GetAt()
ObjectId Utilities::SymbolTableGetAt(String ^name,SymbolTable ^table, bool getErased)
{
if(name == nullptr)
throw gcnew System::ArgumentNullException("SymbolTableGetAt");
if(table == nullptr)
throw gcnew System::ArgumentNullException("SymbolTableGetAt");
AcDbObjectId recordId;
ObjectId id;
pin_ptr<const ACHAR> _name = PtrToStringChars(name);
pin_ptr<AcDbSymbolTable>_table = (AcDbSymbolTable*)table->UnmanagedObject.ToPointer();
//Call the ObjectARX AcDbSymbolTable::GetAt();
if(_table->getAt(_name,recordId,getErased)== Acad::eOk)
id = (ObjectId)gcnew ObjectId(recordId.asOldId());
else
id = (ObjectId)gcnew ObjectId();
return id;
}
Then add an Extension Method to the SymbolTable class via C#
public static ObjectId GetAt(this SymbolTable m_this, string name, bool getErased)
{
return AcMgdWrprs.Utilities.SymbolTableGetAt(name, m_this, getErased);//edited
}
Now we can use this new method in our C# function.
[CommandMethod("doit")]
static public void OpenThePodBayDoorsHal()
{
Editor ed = AcAp.Application.DocumentManager.MdiActiveDocument.Editor;
Database db = AcDb.HostApplicationServices.WorkingDatabase;
string MyNewLayerName = "OhDannyBoy";
LayerTableRecord newLTR = new LayerTableRecord();
newLTR.Name = MyNewLayerName;
using (Transaction tr = db.TransactionManager.StartTransaction())
{
try
{
DBObject dbObject = tr.GetObject(db.LayerTableId, OpenMode.ForRead, false);
if (dbObject != null && dbObject is LayerTable)
{
LayerTable lt = (LayerTable)dbObject;
if (lt.GetAt(MyNewLayerName, false).IsNull) //<---------
{
lt.UpgradeOpen();
lt.Add(newLTR);
tr.AddNewlyCreatedDBObject(newLTR, true);
tr.Commit();
}
else
{
ed.WriteMessage("Sorry Dave, I can't do that");
}
}
}
catch (System.Exception ex)
{
ed.WriteMessage(ex.Message);
}
}
}
w00ty w00ty w00ty w00ty :lol:
-
Heh...nice one Dan.
-
Isn't it slower blah blah blah and stuff?
it is simply a managed wrapper around the COM code. So it's actually SLOWER. ...
This is not a correct statement. Managed code wraps native code directly,
without the use of COM, I am fairly certain that well written .NET is faster than COM.
Hmmm..... Maybe I'm just getting thrown again by the fact that there is no .NET API for Civil-3D, so everything I do is a horrible mish-mash of .NET and COM....
-
So what we can do is drop down into C++/CLI and write our own managed wrapper for AcDbSymbolTable::GetAt()
You've seen that PInvoke solution from Tony T, right?
It's basically the same thing, but the whole thing is done in C#.NET, with no need to compile a C++ class.
-
Hi Sinc,
Yes, I have seen Tony’s code. I just prefer to write wrappers in C++/CLI as it helps me learn what’s going on behind the scenes.
-
Next part of the question on creating layers. What would be the best way to keep up with the list of layers and their respective properties? As I see it, there are 2 ways I can do this.
- Hard code all the layer information into the .Net DLL
- Keep an external file full of the information.
If I go with option 2, what is the best type of file to maintain? I'm thinking XML file or TXT file. XML has its advantages in that when editing, all the properties are delineated by the XML tags. If I use a TXT file, should I use comma delimited or tab delimited file? Ideas? Other pros or cons I haven't thought of?
Edit:
Hard coding has obvious disadvantages in that I have to redistribute the DLL everytime there is an update. Are there other disadvantages you can think of?
-
And to add to my previous post, I have some routines that freeze and thaw layers. I currently use a TXT file to tell computer what layers to thaw. The routine sets 0 current, freezes everything else, and thaws the layers on the TXT file list. Ideas?
-
XML - no question.
-
David, I'd use XML as well.
The 'Linq' technology provided by .NET3.5 is worth spending a couple of days playing with.
-
Glenn, do you have an example of reading or writing XML?
-
Kerry thanks. I'll start looking into that. MSDN for info?
-
a simplistic starter
http://www.theswamp.org/index.php?topic=20033.0
.. and yes, MSDN !
there are heaps of articles and samples.
-
Glenn, do you have an example of reading or writing XML?
Apart from Kerry's excellent link, you might try searching for XmlReader and XmlWriter, until you get accustomed to serialising your classes to and from XML.
-
I'm reading Kerry's post now. Thanks guys
-
David, have a play ...
don't let the using statements scare you, they are not ALL needed :-)
// CodeHimBelongaKwb © Dec 2008
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.Linq;
using System.Runtime.InteropServices;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.Runtime;
using AcadApp = Autodesk.AutoCAD.ApplicationServices.Application;
using AcAp = Autodesk.AutoCAD.ApplicationServices;
using AcDb = Autodesk.AutoCAD.DatabaseServices;
using AcEd = Autodesk.AutoCAD.EditorInput;
using AcGe = Autodesk.AutoCAD.Geometry;
using AcRx = Autodesk.AutoCAD.Runtime;
//
using AcCm = Autodesk.AutoCAD.Colors;
using AcGi = Autodesk.AutoCAD.GraphicsInterface;
using AcLy = Autodesk.AutoCAD.LayerManager;
using AcPl = Autodesk.AutoCAD.PlottingServices;
using AcUi = Autodesk.AutoCAD.Windows;
using WinForms = System.Windows.Forms;
[assembly: AcRx.CommandClass(typeof(LayersFromXML.TestCommands))]
namespace LayersFromXML
{
public class TestCommands
{
[AcRx.CommandMethod("Cmd2")]
static public void test2()
{
Util.GetLayerXMLData();
}
//==========================================================
public class Util
{
//---------------------------------------------------------
public static void GetLayerXMLData()
{
Editor ed = AcadApp.DocumentManager.MdiActiveDocument.Editor;
PromptStringOptions ops = new PromptStringOptions("\nLayer name: ");
ops.AllowSpaces = true;
PromptResult res = ed.GetString(ops);
if (!(res.Status == PromptStatus.OK && res.StringResult != string.Empty))
{
return;
}
string layerName = res.StringResult;
DataSet dsLayerData = new DataSet();
dsLayerData.ReadXml(@"c:\LayerDefinitions.xml");
DataView dsLayerDefinition = new DataView(dsLayerData.Tables["Layer"]);
dsLayerDefinition.Sort = "LayerName";
int rowIndex = dsLayerDefinition.Find(layerName);
if (rowIndex == -1)
{
// The LayerName was not found in our LayerData
ed.WriteMessage("\nSpecified layer not found in the file.");
return;
}
ed.WriteMessage("\nLayerName : {0}", dsLayerDefinition[rowIndex]["LayerName"]);
ed.WriteMessage("\nDesc : {0}", dsLayerDefinition[rowIndex]["Description"]);
ed.WriteMessage("\nColor : {0}", dsLayerDefinition[rowIndex]["Color"]);
ed.WriteMessage("\nLineType : {0}", dsLayerDefinition[rowIndex]["LineType"]);
ed.WriteMessage("\nAlias : {0}", dsLayerDefinition[rowIndex]["Alias"]);
}
}
}
and the XML file c:\LayerDefinitions.xml
<?xml version="1.0" encoding="utf-8"?>
<LayerData>
<Layer>
<Description>Outlines 0.90mm</Description>
<LayerName>ST90</LayerName>
<Color>5</Color>
<LineType>CONTINUOUS</LineType>
<Alias>ST90</Alias>
</Layer>
<Layer>
<LayerName>ST70</LayerName>
<Description>Outlines 0.70mm</Description>
<Color>3</Color>
<LineType>CONTINUOUS</LineType>
<Alias>ST70</Alias>
</Layer>
<Layer>
<LayerName>ST50</LayerName>
<Description>Outlines 0.50mm</Description>
<Color>5</Color>
<LineType>CONTINUOUS</LineType>
<Alias>ST50</Alias>
</Layer>
<Layer>
<LayerName>ST35</LayerName>
<Description>Outlines 0.35mm</Description>
<Color>2</Color>
<LineType>CONTINUOUS</LineType>
<Alias>ST35</Alias>
</Layer>
<Layer>
<LayerName>ST25</LayerName>
<Description>Outlines 0.25mm</Description>
<Color>1</Color>
<LineType>CONTINUOUS</LineType>
<Alias>ST25</Alias>
</Layer>
<Layer>
<LayerName>ST18</LayerName>
<Description>Outlines 0.18mm</Description>
<Color>4</Color>
<LineType>CONTINUOUS</LineType>
<Alias>ST18</Alias>
</Layer>
</LayerData>
-
Nicely done Kerry - DataView's can be very handy.
Cheers,
Glenn.
-
How about using a DWG file to hold your layer info? Don’t get me wrong, XML is a great idea. I just wanted to see if I could … ^-^
-
I thought about that, but I was trying to come up with files that nobody would want to edit (read screw up) except me, and a dwg is too easy to open and tweak by some of my wanna-be power users.
-
.. too easy to open and tweak by some of my wanna-be power users.
Oh God, wanna-be power users, may I recommend a 1024 bit encryption then :-D
-
yyyeeesss please!!!
-
How about using a DWG file to hold your layer info? Dont get me wrong, XML is a great idea. I just wanted to see if I could
^-^
I use a DWS file myself which has the added bonus of being able to be used for cad standards checking.
-
Glenn R, could this have been done with Clone() or DeepClone()?
-
Dan,
I would have to look up what I did exactly, but I think I used WblockClone from memory.
Cheers,
Glenn.
-
I thought about that, but I was trying to come up with files that nobody would want to edit (read screw up) except me, and a dwg is too easy to open and tweak by some of my wanna-be power users.
If you open a file using db.ReadDwgFile like Daniel did, the file does not have to have a .dwg extension. I just tested if (briefly, but I did test it) and it works.
You could 'hide' your dwg so no one would poke at it.
-
Done that before also SCMD (hide a dwg from casual scrutiny) and you can do it with VBA DBX as well too :)
-
Glenn,
Do you know of any drawbacks to reading, via .NET, a database from a non-.dwg file?
Just for my general edification. :)
Thanks
-
WOW, I have hidden stuff in files by changing the extension, but those were just txt files. I didn't know you could change the extension of "database" files and still have it work. I guess it makes sense, just never thought of it that way
-
Glenn,
Do you know of any drawbacks to reading, via .NET, a database from a non-.dwg file?
Just for my general edification. :)
Thanks
Just to clarify, it STILL is a dwg file, it just doesn't have the DWG extension...so in answer to your question, no, I haven't experienced any drawbacks as such.
-
Just make sure you destroy it.
-
Just make sure you destroy it.
???
You mean when Im done "using" it in code right? The word destroy is what threw me. I thought you would have said dispose or unload or close.
-
Sorry - my C++ coming out again. Yes, you're right - 'use it and dispose it'.
The golden rule in C++ - if you 'new' it, delete/destroy it...good rule to live by even in the managed world.
-
thats OK, just asking for claritys sake. Now that you mention it, RR taught me a little C++ back in the day, and we had to destroy our objects we created. I just forgot the term.
-
Thanks guys for the pointers. I now have 3 methods for creating layers, ea building on the last. I am now working on loading linetypes. Kerry your LibTools are definetly helping point me in the direction.
-
Not that this is anywhere near done, but for those following along, here is where Im at
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.Colors;
using Autodesk.AutoCAD.EditorInput;
namespace LayerCode
{
public class LayerCode
{
[CommandMethod("CD3")]
static public void createduhlayer()
{
Document doc = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument;
Database db = doc.Database;
Editor ed = doc.Editor;
using (Transaction tr = db.TransactionManager.StartTransaction())
{
LayerTable lt = tr.GetObject(db.LayerTableId, OpenMode.ForRead, false) as LayerTable;
if (lt == null)
return;
if (lt.Has("DSH"))
return;
LayerTableRecord newLTR = new LayerTableRecord();
newLTR.Name = "DSH";
newLTR.Color = Color.FromColorIndex(ColorMethod.ByAci, 2);
// set any other properties
lt.UpgradeOpen();
lt.Add(newLTR);
tr.AddNewlyCreatedDBObject(newLTR, true);
tr.Commit();
}
//createLayer("Duh2");
createLayerXML("Duh3");
}
static public void createLayer(string Lname)
{
Document doc = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument;
Database db = doc.Database;
Editor ed = doc.Editor;
using (Transaction tr = db.TransactionManager.StartTransaction())
{
LayerTable lt = tr.GetObject(db.LayerTableId, OpenMode.ForRead, false) as LayerTable;
if (lt == null)
return;
if (lt.Has(Lname))
return;
LayerTableRecord newLTR = new LayerTableRecord();
newLTR.Name = Lname;
newLTR.Color = Color.FromColorIndex(ColorMethod.ByAci, 2);
// set any other properties
lt.UpgradeOpen();
lt.Add(newLTR);
tr.AddNewlyCreatedDBObject(newLTR, true);
tr.Commit();
}
}
static public void createLayerXML(string Lname)
{
Document doc = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument;
Database db = doc.Database;
Editor ed = doc.Editor;
using (Transaction tr = db.TransactionManager.StartTransaction())
{
LayerTable lt = tr.GetObject(db.LayerTableId, OpenMode.ForRead, false) as LayerTable;
if (lt == null)
return;
if (lt.Has(Lname))
{
return;
}
else
{
DataSet dsLayerData = new DataSet();
dsLayerData.ReadXml(@"c:\LayerDefinitions.xml");
DataView dsLayerDefinition = new DataView(dsLayerData.Tables["Layer"]);
dsLayerDefinition.Sort = "LayerName";
int rowIndex = dsLayerDefinition.Find(Lname);
if (rowIndex == -1)
{
ed.WriteMessage("Bogus Layer Name");
}
else
{
short lC = Convert.ToInt16(dsLayerDefinition[rowIndex]["Color"]);
string lnT = dsLayerDefinition[rowIndex]["LineType"].ToString();
LinetypeTable LTT = tr.GetObject(db.LinetypeTableId, OpenMode.ForRead, false) as LinetypeTable;
if (LTT == null)
return;
if (LTT.Has(lnT))
return;
else
{
LinetypeTableRecord newLTTR = new LinetypeTableRecord();
newLTTR.Name = lnT;
LTT.UpgradeOpen();
LTT.Add(newLTTR);
tr.AddNewlyCreatedDBObject(newLTTR, true);
}
//string layerColor = dsLayerDefinition[rowIndex]["Color"].ToString();
//short lC = Convert.ToInt16(layerColor);
LayerTableRecord newLTR = new LayerTableRecord();
newLTR.Name = Lname;
newLTR.Color = Color.FromColorIndex(ColorMethod.ByAci, lC);
newLTR.LinetypeObjectId = newLTTR;
lt.UpgradeOpen();
lt.Add(newLTR);
tr.AddNewlyCreatedDBObject(newLTR, true);
}
}
// set any other properties
tr.Commit();
}
}}
-
The linetype piece doesn't work yet so thats what i will work on next.
-
this was all going so well, when I came across a new problem. What if you need to rename a layer? Is it better to create a new layer and move all objects from the old layer to the new and delete the old layer or can you just rename a layer in the LayerTable?
-
AFAIK, you can rename, as long as the new name DOES not exist in the dbase already...obviously :-)
-
Thanks Glenn. I meant to come back and update this. I was making it harder than it needed to be, and I was banging my head against the wall tring to find the "updateDB" option. Finally i said, lets just see what blows up, and it worked
-
...lets just see what blows up,
Words to live by! :)
-
Yep, that's how I handle most projects :D
(the sad thing is there's always a hint of truth in comedy :|)
-
ok, time for an update. With help from Glenn and Kerry, I now have code that will move objects from a layer that is named incorrectly to a correctly named layer. I am still having problems with my linetype, specifically its losing scope
[CommandMethod("LTree")]
static public void createLayerXML(string Lname)
{
Document doc = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument;
Database db = doc.Database;
Editor ed = doc.Editor;
using (Transaction tr = db.TransactionManager.StartTransaction())
{
LayerTable lt = tr.GetObject(db.LayerTableId, OpenMode.ForRead, false) as LayerTable;
if (lt == null)
return;
if (lt.Has(Lname))
{
return;
}
else
{
DataSet dsLayerData = new DataSet();
dsLayerData.ReadXml(@"c:\CS_TEPLayers.xml");
DataView dsLayerDefinition = new DataView(dsLayerData.Tables["Layer"]);
dsLayerDefinition.Sort = "LayerName";
int rowIndex = dsLayerDefinition.Find(Lname);
if (rowIndex == -1)
{
ed.WriteMessage("Bogus Layer Name");
}
else
{
short lC = Convert.ToInt16(dsLayerDefinition[rowIndex]["Color"]);
string lnT = dsLayerDefinition[rowIndex]["LineType"].ToString();
LinetypeTable LTT = tr.GetObject(db.LinetypeTableId, OpenMode.ForRead, false) as LinetypeTable;
if (LTT == null)
return;
if (LTT.Has(lnT))
return;
else
{
LinetypeTableRecord newLTTR = new LinetypeTableRecord();
newLTTR.Name = lnT;
LTT.UpgradeOpen();
LTT.Add(newLTTR);
tr.AddNewlyCreatedDBObject(newLTTR, true);
}
//string layerColor = dsLayerDefinition[rowIndex]["Color"].ToString();
//short lC = Convert.ToInt16(layerColor);
LayerTableRecord newLTR = new LayerTableRecord();
newLTR.Name = Lname;
newLTR.Color = Color.FromColorIndex(ColorMethod.ByAci, lC);
//newLTR.LinetypeObjectId = newLTTR; // Lost scope here from else above
lt.UpgradeOpen();
lt.Add(newLTR);
tr.AddNewlyCreatedDBObject(newLTR, true);
}
}
// set any other properties
tr.Commit();
}
}
-
BTW, the above code doesn't work as a command, I'm still working that part out. This came from another piece of code further up in the thread.
-
This is a classic example of why I don't like nested if,then,else statements.
Duh, remember, that like C/C++, curly braces denote a scope (although not as extensive as C/C++) and that is why your variable for newLTTR is going out of scope - it's DECLARED and INITIALISED in the above 'else' clause, which as luck would have it, has....you guessed it - curly braces.
In this sort of situation, at the begining of the function do this:
LinetypeTableRecord newLTTR = null;
That way, it is accessible for the rest of the 'nested' scopes defined by the curly brace code blocks.
-
Thanks Glenn, I knew I would have to declare it outside of the else, I just wanted to make it the best possible way I could.
-
I am rewriting all that code to be more user friendly. Classic example of the drivel I put together, and come back to later and say What was I thinking. I am going to have to do some reading up on XML and Datasets and maybe LINQ because I have no clue what the rowIndex of my table is doing.
-
Here is what I have so far on the re-write
[CommandMethod("LTree")]
static public void createXMLLayer()
{
Document doc = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument;
Database db = doc.Database;
Editor ed = doc.Editor;
DataSet dsLayerData = new DataSet();
dsLayerData.ReadXml(@"c:\CS_TEPLayers.xml");
DataView dsLayerDefinition = new DataView(dsLayerData.Tables["Layer"]);
dsLayerDefinition.Sort = "LayerName";
using (Transaction tr = db.TransactionManager.StartTransaction())
{
LayerTable lt = tr.GetObject(db.LayerTableId, OpenMode.ForRead, false) as LayerTable;
string LayerName = null;
if (lt == null)
return;
int rowIndex = 1;
LayerName = (string)dsLayerDefinition[rowIndex]["LayerName"];
++rowIndex;
LayerName = (string)dsLayerDefinition[rowIndex]["LayerName"];
I am trying to see how it reads the file, and how to use my rowindex. I hard coded the index to 1 and then pulled the LayerName for that row. It did not do what I thought it would do, It pulled from row 90ish. Then the second time through, it pulled from 85ish. Bottem line is I have no clue what Im doing, so I am going to have to read up on this tonight
-
That's how you learn mate - an avenue you decide to go down to learn and you emerge a week later going 'WOW'....now what was I doing... :)
-
Well, the lightbulb lit up for a second, but quickly went back off. I thought that because I was doing a sort, that was why the rowindex gave me a result I didn't expect. Turns out, if it did the sort it claims, I am still way off
-
huh, what were we talking about? :wink:
-
<snip> an avenue you decide to go down to learn and you emerge .... <snip>
I wish I could make the time to take a trip down that road .... I'm overdue for an excursion ... :)