Hi everyone,
I've been trying to find help on replacing Mtext in a drawing border of mine. The drawing border is a block that has constant attributes that I have a vb.net dll that can edit the constant attributes.
I'm trying to add this to that dll, I would like this to work in such a way as to not have the user involved. My thought was to have the code executed when the form was loaded to edit the values in the titleblock. The code would look to see if the Mtext existed in the border and if so replace it with the correct Mtext. If the correct Mtext is there it would exit the Sub.
I'm have a hard time figuring out how to use the Transaction concept. I've tried to modify an exisitng function in the VB.net dll to do this change.
As you can see in this example code, I'm not very good at figuring out how to modify it to my needs. I'm still not sure how some of these "BIG PICTURE" concepts work?
Do I need one function to read and another function to write? Do I need a Sub to strat to function(s)? I understand most of form code.
Any pointers in the correct direction would be appreciated....
PublicSharedFunction ReadMtextFromBTR(ByVal strName AsMText) AsMTextDim MtxtList AsList(Of Text.StringBuilder)
Dim oDwg AsDocument = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument
Dim oDB As DatabaseServices.Database= oDwg.Database
Dim oTransMgr As DatabaseServices.TransactionManager= oDB.TransactionManager
Dim oTrans As DatabaseServices.Transaction= oTransMgr.StartTransaction
UsingoTrans
Dim oDatabaseObject AsDBObject = oDB.BlockTableId.GetObject(DatabaseServices.OpenMode.ForRead)
Dim oBlockTable As DatabaseServices.BlockTable = DirectCast(oDatabaseObject, BlockTable)
Dim oBTRE As DatabaseServices.SymbolTableEnumerator= oBlockTable.GetEnumerator
WhileoBTRE.MoveNext
Dim oDBObject AsDBObject = oBTRE.Current.GetObject(DatabaseServices.OpenMode.ForRead)
Dim oBTR As DatabaseServices.BlockTableRecord = DirectCast(oDBObject, BlockTableRecord)
If oBTR.Name = strName.Text ThenForEach oObjectID AsObjectIdInoBTR
Dim oEnt AsDBObject = oTrans.GetObject(oObjectID, OpenMode.ForRead)
'If oEnt.GetType.ToString = "Autodesk.AutoCAD.DatabaseServices.AttributeDefinition" ThenIfTypeOf oEnt Is Autodesk.AutoCAD.DatabaseServices.MTextThen
oEnt.UpgradeOpen()
If oEnt.ToString >= ("FirstEnergy ") ThenSelectCaseCase"FirstEnergy GENERATION CORP."ReturnstrName
Case"FirstEnergy _"
GENERATION, LLC"
Return2
Case ElseThrowNewException("The border did not match any of the defined types.")
EndSelectEndIfEndIfNextEndIfEndWhile'oTrans.Commit()EndUsing
oTrans.Dispose()
oTransMgr.Dispose()
oDB.Dispose()
Return MtxtList
Solved! Go to Solution.
So your main question is how to better understand how to better understand and use the .NET API? If so, I recommend you work through the .NET Training Labs we have posted here - www.autodesk.com/developautocad (under the 'Learning' section. Note that there is also a 'DevTV' link to the left of the links to the material that includes a set of videos talking therough each of the training labs. If you're very new to programming, then the 'My First Plug-in' tutorial on the same page may be helpful too.
BTW In your code below, you seem to have commented out the 'trans.Commit()' line, which means your transaction will be aborted when it is Disposed and any changes you made to the drawing since you started the transaction will be rolled back. You should also look in the tutorials I mention how you can use For Each...Next instead of GetEnumerator/MoveNext - its a lot simpler.
To get all the mtext in a block, you can use selection filter. Selection filter will accumulate all entity according to your selection. Below I have create a code snip which will gather all the mtext in the model space. Please see below.
Public Shared Function GetMTextBTR(acEd As Editor) As ObjectIdCollection Dim tvs(0) As TypedValue tvs.SetValue(New TypedValue(DxfCode.Start, "MTEXT"), 0) Dim sf As New SelectionFilter(tvs) Dim psr As PromptSelectionResult = acEd.SelectAll(sf) If psr.Status = PromptStatus.OK Then Return New ObjectIdCollection(psr.Value.GetObjectIds()) Else Return New ObjectIdCollection() End If End Function <CommandMethod("tst")> Public Sub TESTMtext() Dim doc As Document = Application.DocumentManager.MdiActiveDocument Dim db As Database = doc.Database Dim ed As Editor = doc.Editor Using tx As Transaction = db.TransactionManager.StartTransaction() Dim bt As BlockTable = tx.GetObject(db.BlockTableId, OpenMode.ForRead) Dim ms As BlockTableRecord = tx.GetObject(bt(BlockTableRecord.ModelSpace), OpenMode.ForRead) Dim objIdColl As ObjectIdCollection = GetMTextBTR(ed) For Each objId As ObjectId In objIdColl 'DO SOMETHING Next tx.Commit() End Using End Sub
mzakiralam
Thank you for the Code snippet.
In your example code, I don't have a grasp on what I should be placing for code in the "DO Something" area.
I've tried to use a "If statement" to determine if the particular Mtext is what I want to change. I can't seem to unerstand how to do this part. (see below)
The "Lightbulb" is starting to glow I see the function piece in the command sub.
For some reason your supplied code wasn't liked "as is". I had to change it to this
Using tx AsTransaction= db.TransactionManager.StartTransaction()
Dim bt AsBlockTable = CType(tx.GetObject(db.BlockTableId, OpenMode.ForRead), BlockTable)
Dim ms AsBlockTableRecord = CType(tx.GetObject(bt(BlockTableRecord.ModelSpace), OpenMode.ForRead), BlockTableRecord)
Dim objIdColl AsObjectIdCollection = FirstEnergy.AutoCADFunctions.GetMTextBTR(ed)
ForEach objId AsObjectIdIn objIdColl 'DO SOMETHING Next If objId.GetObject(ms.Equals("TEST") Then
MsgBox(
"you found it")
Else
MsgBox(
"that's not it")
EndIfNext
I was not sure what you need to do. This is why after getting the objectId , I have written "Do Something" . Is it now working for you?
if not please see my below code and further explanation. Suppose I have created three MTEXT in my drawing and MTEXT are test1, test2 and test3. If test1 is found it will give a message like "found test1" and so on for others. Please see below code
<CommandMethod("tst")> Public Sub TESTMtext() Dim doc As Document = Application.DocumentManager.MdiActiveDocument Dim db As Database = doc.Database Dim ed As Editor = doc.Editor Using tx As Transaction = db.TransactionManager.StartTransaction() Dim bt As BlockTable = tx.GetObject(db.BlockTableId, OpenMode.ForRead) Dim ms As BlockTableRecord = tx.GetObject(bt(BlockTableRecord.ModelSpace), OpenMode.ForRead) Dim objIdColl As ObjectIdCollection = GetMTextBTR(ed) For Each objId As ObjectId In objIdColl 'get the Mtext from objectID Dim mtxt As MText = tx.GetObject(objId, OpenMode.ForWrite) 'check mtext content If mtxt.Contents = "test1" Then MsgBox("found test1") ElseIf mtxt.Contents = "test2" Then MsgBox("found test2") ElseIf mtxt.Contents = "test3" Then MsgBox("found test3") End If Next tx.Commit() End Using End Sub
No,
I'm still gropeing along trying to figure out what to plug into the "Do Something". I kinda understand the Function you supplied. It is collecting object Ids that are Mtext and adding them to the editor? Or is the Function looking for something specifically called "MTEXT"?
What I want to do is Find this Mtext ("FirstEnergy GENERATION CORP.") and replace with ("FirstEnergy GENERATION, LLC).
There is only one (1) of these Mtext strings in the block reference.
My function will collect all MText entities in the model space. It is not related with something Special. It will only collect objectId which DBObject are MText. After getting objectId, you can get MText . Then you can implement your purpose . According to your Need I have modified above code. Please see below. I did not try this code. But it should work
<CommandMethod("tst")> Public Sub TESTMtext() Dim doc As Document = Application.DocumentManager.MdiActiveDocument Dim db As Database = doc.Database Dim ed As Editor = doc.Editor Using tx As Transaction = db.TransactionManager.StartTransaction() Dim bt As BlockTable = tx.GetObject(db.BlockTableId, OpenMode.ForRead) Dim ms As BlockTableRecord = tx.GetObject(bt(BlockTableRecord.ModelSpace), OpenMode.ForRead) Dim objIdColl As ObjectIdCollection = GetMTextBTR(ed) For Each objId As ObjectId In objIdColl 'get the Mtext from objectID Dim mtxt As MText = tx.GetObject(objId, OpenMode.ForWrite) 'check mtext content If mtxt.Contents = "FirstEnergy GENERATION CORP." Then mtxt.contents = "FirstEnergy GENERATION, LLC" End If Next tx.Commit() End Using End Sub
OK!
Bulb getting a little brighter So I should be able to find said string, then replace it with new string and commit?
SO, if I figure out the FIND replace I can call this "Command" when the FORM loads? It will automatically search and replace if necessary?
Well, still stumbling.
I've tried to shorten what Mtext to look for (see code) but nothing is happening?
I noticed in the code you've supplied that there isn't anything that is qualifying the MText to look for, like inside a Blockreference? Does the function care? Can it see Mtext in the Block reference?
Also, I did a properties on an example Mtext = " CORP." and the properties show a bunch of formating info. Is the formating info ignored when the Function/ Sub is executed?
Public Sub TESTMtext() Dim doc As Document = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument Dim db As Database = doc.Database Dim ed As Editor = doc.Editor Using tx As Transaction = db.TransactionManager.StartTransaction() Dim bt As BlockTable = tx.GetObject(db.BlockTableId, OpenMode.ForRead) Dim ms As BlockTableRecord = tx.GetObject(bt(BlockTableRecord.ModelSpace), OpenMode.ForRead) Dim objIdColl As ObjectIdCollection = FirstEnergy.AutoCADFunctions.GetMTextBTR(ed) For Each objId As ObjectId In objIdColl 'get the Mtext from objectID Dim mtxt As MText = tx.GetObject(objId, OpenMode.ForWrite) 'check mtext content If mtxt.Contents = (" CORP.") Then mtxt.Contents = (", LLC") End If Next tx.Commit() End Using
I really do not know how you could use that function for your purposes or use Editor.SelectAll on BlockTableRecord without a layout.
Here is a thread where Norman helps someone and shows how to use a selection filter to get all BlockReferences then look inside the blocks.
http://forums.autodesk.com/t5/NET/Blocks-attributes-and-selection-sets/m-p/4331537/highlight/true
There are much better ways to streamline this and make it reusable, but just for learning purposes here is another approach.
So making part of mtext bold and and changing the last bit to red the attached drawing will print
Mtext.Contents = THIS IS {\fArial|b1|i0|c0|p34;MTEXT \C1;FORMATTED}
Mtext.Text= THIS IS MTEXT FORMATTED
Have not written VB for a long time, Sorry.
Dim blockName As String = "BlockName" <CommandMethod("LooKForMtext")> _ Public Sub LooKForMtext() Dim doc As Document = Application.DocumentManager.MdiActiveDocument Dim db As Database = doc.Database Dim ed As Editor = doc.Editor Using trx As Transaction = db.TransactionManager.StartTransaction() Dim blockTbl As BlockTable = trx.GetObject(db.BlockTableId, OpenMode.ForRead) If Not blockTbl.Has(blockName) Then ed.WriteMessage(Environment.NewLine & "Block not in drawing") Return End If Dim block As BlockTableRecord = trx.GetObject(blockTbl(blockName), OpenMode.ForRead) Dim mtxtClassptr As IntPtr = RXClass.GetClass(GetType(MText)).UnmanagedObject For Each entId As ObjectId In block If entId.ObjectClass.UnmanagedObject = mtxtClassptr Then Dim mtxt As MText = trx.GetObject(entId, OpenMode.ForRead) ed.WriteMessage(Environment.NewLine & mtxt.Contents) ed.WriteMessage(Environment.NewLine & mtxt.Text) End If Next End Using End Sub <CommandMethod("LooKForConstantAttributes")> _ Public Sub LooKForConstantAttributes() ' This method can have any name Dim doc As Document = Application.DocumentManager.MdiActiveDocument Dim db As Database = doc.Database Dim ed As Editor = doc.Editor Using trx As Transaction = db.TransactionManager.StartTransaction() Dim blockTbl As BlockTable = trx.GetObject(db.BlockTableId, OpenMode.ForRead) If Not blockTbl.Has(blockName) Then ed.WriteMessage(Environment.NewLine & "Block not in drawing") Return End If Dim block As BlockTableRecord = trx.GetObject(blockTbl(blockName), OpenMode.ForRead) If Not block.HasAttributeDefinitions Then ed.WriteMessage(Environment.NewLine & "No attributes in block") Return End If Dim attDefClassptr As IntPtr = RXClass.GetClass(GetType(AttributeDefinition)).UnmanagedObject For Each entId As ObjectId In block If entId.ObjectClass.UnmanagedObject = attDefClassptr Then Dim attDef As AttributeDefinition = trx.GetObject(entId, OpenMode.ForRead) ed.WriteMessage(Environment.NewLine & attDef.Constant) ed.WriteMessage(Environment.NewLine & attDef.Tag) If attDef.IsMTextAttributeDefinition Then ed.WriteMessage(Environment.NewLine & attDef.MTextAttributeDefinition.Contents) Else ed.WriteMessage(Environment.NewLine & attDef.TextString) End If End If Next End Using End Sub End Class
Forgot to commit transaction and should not hard code values inside fuction but this will get you started
<CommandMethod("LooKForMtext")> _ Public Sub LooKForMtext() ' This method can have any name Dim doc As Document = Application.DocumentManager.MdiActiveDocument Dim db As Database = doc.Database Dim ed As Editor = doc.Editor Using trx As Transaction = doc.TransactionManager.StartTransaction() Dim blockTbl As BlockTable = trx.GetObject(db.BlockTableId, OpenMode.ForRead) If Not blockTbl.Has(blockName) Then ed.WriteMessage(Environment.NewLine & "Block not in drawing") Return End If Dim block As BlockTableRecord = trx.GetObject(blockTbl(blockName), OpenMode.ForRead) Dim mtxtClassptr As IntPtr = RXClass.GetClass(GetType(MText)).UnmanagedObject For Each entId As ObjectId In block If entId.ObjectClass.UnmanagedObject = mtxtClassptr Then Dim mtxt As MText = trx.GetObject(entId, OpenMode.ForRead) If mtxt.Text.IndexOf("FirstEnergy GENERATION CORP", StringComparison.InvariantCultureIgnoreCase) >= 0 Then mtxt.UpgradeOpen() mtxt.Contents = mtxt.Contents.Replace("CORP", "LLC") End If End If Next trx.Commit() End Using ed.Regen() End Sub
Thank you!!!! Jeff,
I've learned more in the past few hours than all the rest of the time I've
tried to figure this out on my own, on my own I've spent weeks looking at
example code.
The code sorta made sense, but the context is what I believe was
missing. Not knowing how to apply it is very frustrating.
Please mark Jeff's answer as an Accepted Solution (and give him Kudos) if his code answered your question :-).
I'm still struggling,
I can't seem to grab the Block "that is there" to look for the Mtext. Tried to dim the TypedValue as a block but doesn't seem to effect the code?
Aslo tried the
Dim
blockname As String = "F-size" <---this is one of the Blocks that could be in the drawing.
It always says the block isn't there?
I don't understand what I'm missing?
Can you send your drawing? I am sorry that yesterday , I did not understand your requirement fully.Therefore I only mention about MText in the model space block. Do you want to change some text which is as the attribute of a block reference in the drawing?
Can't find what you're looking for? Ask the community or share your knowledge.