Hi All,
I'm a newbie with vb.net but I'm very excited about the first tutorials I did.
To get any further with VB.net I'm trying to (re)create some autocad commands (and a own features, eventually).
I'm trying a move command now.A recreation of the Autocad command.
The selection of object works. Moving the set between two points works. The only thing thats too advanced for me is the drag posibility VB offers.
I looked close to the code by Kean Walmsley. http://through-the-interface.typepad.com/through_the_interface/2010/06/allowing-interactive-dragging...
But I'm a newbie and fail to translate the C# code correctly. The dragcallback is a mystery to me.
Can anyone help me with a (simple) piece of code from which I can learn how the drag/jig system for selections works? Or has anyone a sample code of a command that's similar to the move or copy command so I can learn from those examples.
Thank you very much!!!
Jeroen
Here is Kean's code, translated to VB. In case you missed it, I explained (briefly) how to handle the callback function in VB in the comments section of Kean's original post to Someone else who was having trouble. Of course the attached code already has it taken care of.
I just wrote an article on creating own "MOVE" command with .NET API here:
http://drive-cad-with-code.blogspot.com/2011/01/creating-move-command-with-net-api.html
It also refers Kean's post, but intend to build a command behaving closely like AutoCAD built-in "MOVE" command.
The code is in C#. But it should be easily translated to VB.NET: "chiefbraincloud" has in the oter reply shown the translation of the tricky part: DragCallBack delegate.
Norman Yuan
Yuan,
Great blog post. Supper informative. I only have a problem not moving but picking points.
Perhaps you could help me.
What do i need to do.
I need the user to select multiple points in the drawing. How can i highlight the pervious selected points. And how can i draw a line like in your example from the last selected point to the next point to be selected.
I hope you understand my question and hope you can help me out.
Kind regards
Irvin
If I understand you correctly, by "select multiple points", you mean Autocad's Point entity (DBPoint class), not geometric location ((x,y,z)). That is, you want pick a sieries of entities (DBPoints, or other entity type), after each pick, the selcted entity is highlighted, when picking next one, a rubber band line is drawn dynamically with the mouse cursor move while the next entity is to pick; the rubber band line starts from the last picked point (or somewhere of the last pciked entity). After the picking you would get a collection of ObjectId, or a collection of Point3d, or both.
If this what you want, obviously, the combination of PromptPointOption, which has BasePoint property that draws a rubber band line from a BasePoint, would not help you, because you need to use PromptEntityOption with Editor.GetEntity().
But it can certainly be done fairly easy with some code. It would be an interesting topic for another small article. I'll try it and get back in a day or two.
Norman Yuan
Hi yuan,
Thanks for you reply.
With points i mean editor.getpoint. I need to get the x ,y, z values from the selected point to created a region based on those points selected by the user.
My ultimate goal is:
I want to show(HighLight) the points previous selected (from current running command) and draw a rubber line from the last point to the next.
Hope you can make an other great blog post.
Kind regards,
Irvin
Hi Guys,
Thanks for replying!!
I saw Kean's code for grab(). I used a c# to vb translator and managed to get it to work.
So I managed to select objects
Get a startpoint
fix the selection to Grab()
and get a endpoint from grab()
I added Yuan's rubber line(brilliant!!) to my code and was very proud it all worked. (I started with vb.net a week ago and had only a little autolisp an vba experience. I was going very nice, up to now)
But now I'm putting it all together and I get a unhandled exception error. What's wrong with my code?
I realize the code below is very dirty since i have to get used to the VB syntax, forgive me.
Imports Autodesk.AutoCAD.ApplicationServices
Imports Autodesk.AutoCAD.EditorInput
Imports Autodesk.AutoCAD.DatabaseServices
Imports Autodesk.AutoCAD.Runtime Imports Autodesk.AutoCAD.Geometry
Imports Autodesk.AutoCAD.GraphicsInterface
Public Class dragtest
<CommandMethod("dt")> _
Public Sub DragTest()
Dim doc As Document = Application.DocumentManager.MdiActiveDocument
Dim db As Database = doc.Database
Dim ed As Editor = doc.Editor
Using tr As Transaction = db.TransactionManager.StartTransaction()
Dim ssSet As PromptSelectionResult = ed.GetSelection()
' startpoint
Dim prPointRes As PromptPointResult
prPointRes = ed.GetPoint("Select a Start Point: ")
Dim PointSP As Point3d = New Point3d(prPointRes.Value.ToArray)
'' Open the Block table for read
Dim bt As BlockTable
bt = tr.GetObject(db.BlockTableId, OpenMode.ForRead)
'' Open the Block table record Model space for write
Dim ms As BlockTableRecord
ms = tr.GetObject(bt(BlockTableRecord.ModelSpace), OpenMode.ForWrite)
Dim rLine As Line = Nothing
Dim ppr As PromptPointResult = ed.Drag(ssSet.Value, vbLf & "Select text location: ", _
Function(pt As Point3d, ByRef mat As Matrix3d)
If PointSP = pt Then
Return SamplerStatus.NoChange
Else
If rLine = Nothing Then
rLine = New Line(PointSP, pt)
rLine.SetDatabaseDefaults(db)
Dim intCol As IntegerCollection = New IntegerCollection
TransientManager.CurrentTransientManager.AddTransient(rLine,
TransientDrawingMode.DirectShortTerm, 128, intCol)
Else
rLine.EndPoint = pt
Dim intCol As IntegerCollection = New IntegerCollection
TransientManager.CurrentTransientManager.UpdateTransient(rLine, intCol)
End If
mat = Matrix3d.Displacement(PointSP.GetVectorTo(pt))
End If
Return SamplerStatus.OK
End Function)
If ppr.Status = PromptStatus.OK Then
' Check rLine
If rLine <> Nothing Then
Dim intCol As IntegerCollection = New IntegerCollection
TransientManager.CurrentTransientManager.EraseTransient(rLine, intCol)
rLine.Dispose()
rLine = Nothing
End If
' Get the final translation
Dim acPt3d As Point3d = PointSP
Dim acVec3d As Vector3d = acPt3d.GetVectorTo(ppr.Value)
For Each acSSObj As SelectedObject In ssSet.Value
If Not IsDBNull(acSSObj) Then
Dim acEnt As Entity = tr.GetObject(acSSObj.ObjectId, OpenMode.ForWrite)
acEnt.TransformBy(Matrix3d.Displacement(acVec3d))
End If
Next
tr.Commit()
End If
End Using
End Sub
End Class
I attached also my VB file.
Please help. Thanks!!
Jeroen
Well, if you meant to use Editor.GetPoint(), the PropmtPointOption class you use in conjunction with Editor.GetPoint() has properties UseBasePoint and BasePoint. Simply set former as "True" and set the latter to a Point3d (the last picked point), then Editor.GetPoint() draws a rubber band line for you while user is picking point. However, since you are picking point (geometric location), there is no entity that would be related to the picking that you can highlight as you wanted.
So, you must have something at the previously picked location there that can be highlighted. That "something", could be an existing entity that somehow has some association with the point picked; or you can draw some temporary visual stuff (yes, you can guess it: Transient Graphics would be the candidate) at picked points during the picking.
I did worte an article over the weekend on the former (if it would be of any help to you):
http://drive-cad-with-code.blogspot.com/2011/01/picking-entity-like-picking-point-with.html
For the latter, I could post something latter.
Also, since your goal is to pick points to form a region (e.g. to get a closed polyline visually), it might be better to not just showing the rubber band line from the last picked point. You might want to draw a polyline, starting from the first picked point, and extend it with each point added.
Norman Yuan
OK, I just compiled a quick blog post that might be what you needed here:
http://drive-cad-with-code.blogspot.com/2011/01/picking-points-with-visual-aid.html
BTW, to all possible readers:
I am not trying to promote my blog here. I just feel sometimes an appropriate answer to a question posted here might be too long, too complicated to post all code in a reply. In this case, I'd redirect the answer to my blog. Otherwise I'd just write short code snippet directly in the reply.
Norman Yuan
Yuan,
I don't mind you promote your blog.
The more info/tutorials which make sense the better 🙂
Keep on the good work!
Jeroen
Keep up the good work!
Your posts and blogs are very helpful and appreciated - here and in Map 3D.
Hi Yuan,
That's exactly what i needed. Great blog again.
This is gonna help me out a lot.
Kudos for Yuan.
But i've got an other brain theaser. And it's not something i need but i think it supper cool how it works. And im verry intrested in how it works. But i havend got a clue how to create it.
Is it possible to create a command that works like the area command in AutoCAD. Here you also specify points. And also draw perimiter of the selected points but also fils the area with a transparent color. Like in the picture.
Yuan if you got some spare time i think you can help us out. These past articles you wrote are stuff where many beginning AutoCAD .net programmers can learn a lot from so are you up for the challenge?
Kind regards,
Irvin
The Netherlands
Yuan,
Could you tell me why your code is crashing AutoCAD when you invoke the command and the commandline is asking to select te first point and you then hit the Escape Button. I cant debug it because i'm kicked out of AutoCAD.
Kind regards,
Irvin
Thamks for your prompt of the bug: it is i the "try...finally{...}" block. I have correct lt in my blog, but here it is:
public void DoPick(bool drawTraceLine) { try { ... } catch { } finally { //Clear Transient graphics if (_circles != null) { if (_circles.Count > 0) { foreach (Circle c in _circles) { TransientManager.CurrentTransientManager. EraseTransient(c, new IntegerCollection()); c.Dispose(); } } _circles.Clear(); _circles = null; } ClearTraceLine(); } }
Norman Yuan
Well, the "Area" commands in Acad2009 and Acad2011 (I skipped Acad2010) are slightly different: in Acad2009, it allows you get total area with only 2 points picked (obviously area=0), while in Acad2011, one must pick at least 3 points.
To fully mimic the "Area" command would be quite some work (with the command options of [Arc/Length...], especially the suboptions for the "Arc". But if only do points picking, it should be fairly easy to do.
I might be able to have some code for it, if I could manage a bit time.
Norman Yuan
Yuan, That's great,
[edit]
Oops just watched your video. Now read the full article. Hope you can figure it out when you got more time.
But the MyArea Works like a charm. An other great sample.
But can you fill the area you select with a back color like in the picture.
Hi Yuan,
Looking at this code again.
http://drive-cad-with-code.blogspot.com/2011/01/picking-points-with-visual-aid.html
I ran to an other glitch. If you selected 1 or more points. And you hit the Esc button. The user gets prompted on the commandline the command was canceld. This is how AutoCAD works. But in your code you check if the array is not emtpy, if not the commandline will still prompt the number of selected points.
Where in your code will be the best place to check is promptstatus is canceld en then clear the array of points.
Kind regards,
Irvin
Upated the code to show background color:
using System.Collections.Generic; using Autodesk.AutoCAD.ApplicationServices; using Autodesk.AutoCAD.EditorInput; using Autodesk.AutoCAD.Geometry; using Autodesk.AutoCAD.GraphicsInterface; using Autodesk.AutoCAD.DatabaseServices; namespace AreaCommand { public class MyAreaCmd { private Document _dwg; private Editor _editor; private double _area = 0.0; private double _perimeter = 0.0; private Autodesk.AutoCAD.DatabaseServices.Polyline _pline = null; private Autodesk.AutoCAD.DatabaseServices.Hatch _hatch = null; private List _points; private bool _pickDone; private Autodesk.AutoCAD.DatabaseServices.Transaction _tran = null; private BlockTableRecord _model = null; public MyAreaCmd(Document dwg) { _dwg = dwg; _editor = _dwg.Editor; } public double Area { get { return _area; } } public double Perimeter { get { return _perimeter; } } public bool GetArea() { _pline=null; //Pick first point Point3d pt1; if (!GetFirstPoint(out pt1)) return false; //Pick second point Point3d pt2; if (!GetSecondPoint(pt1, out pt2)) return false; _pickDone = false; _points = new List(); _points.Add(new Point2d(pt1.X,pt1.Y)); _points.Add(new Point2d(pt2.X, pt2.Y)); try { _tran = _dwg.Database.TransactionManager.StartTransaction(); BlockTable bt = (BlockTable)_tran.GetObject(_dwg.Database.BlockTableId, OpenMode.ForRead); _model = (BlockTableRecord)_tran.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForWrite); //Handling mouse cursor moving during picking _editor.PointMonitor += new PointMonitorEventHandler(_editor_PointMonitor); while (true) { if (!PickNextPoint()) break; } if (_pline != null && _pickDone) { _area = _pline.Area; _perimeter = _pline.Length; } } catch { throw; } finally { ClearTransientGraphics(); //Remove PointMonitor handler _editor.PointMonitor -= new PointMonitorEventHandler(_editor_PointMonitor); _tran.Abort(); _tran.Dispose(); _model = null; } return _pickDone; } #region private methods private bool GetFirstPoint(out Point3d pt) { pt = new Point3d(); PromptPointOptions opt = new PromptPointOptions("\nPick first corner: "); PromptPointResult res = _editor.GetPoint(opt); if (res.Status == PromptStatus.OK) { pt = res.Value; return true; } else { return false; } } private bool GetSecondPoint(Point3d basePt, out Point3d pt) { pt = new Point3d(); PromptPointOptions opt = new PromptPointOptions("\nPick next corner: "); opt.UseBasePoint = true; opt.BasePoint = basePt; PromptPointResult res = _editor.GetPoint(opt); if (res.Status == PromptStatus.OK) { pt = res.Value; return true; } else { return false; } } private bool PickNextPoint() { PromptPointOptions opt = new PromptPointOptions("\nPick next corner: "); if (_points.Count > 2) { opt.Keywords.Add("Undo"); opt.Keywords.Add("Total"); opt.AppendKeywordsToMessage = true; } PromptPointResult res = _editor.GetPoint(opt); if (res.Status == PromptStatus.OK) { _points.Add(new Point2d(res.Value.X,res.Value.Y)); return true; } else if (res.Status == PromptStatus.Keyword) { if (res.StringResult == "Undo") { if (_points.Count > 2) { _points.RemoveAt(_points.Count - 1); } return true; } else { _pickDone = true; return false; } } else { _pickDone = false; return false; } } private void ClearTransientGraphics() { if (_pline != null || _hatch!=null) { TransientManager.CurrentTransientManager.EraseTransients( TransientDrawingMode.DirectTopmost, 128, new IntegerCollection()); if (_pline != null) { if (!_pline.IsErased) _pline.Erase(); _pline.Dispose(); _pline = null; } if (_hatch != null) { if (!_hatch.IsErased) _hatch.Erase(); _hatch.Dispose(); _hatch = null; } } } private void _editor_PointMonitor(object sender, PointMonitorEventArgs e) { ClearTransientGraphics(); //Draw polyline Point2d pt = new Point2d(e.Context.RawPoint.X, e.Context.RawPoint.Y); _pline = new Autodesk.AutoCAD.DatabaseServices.Polyline(_points.Count + 1); for (int i = 0; i < _points.Count; i++) { _pline.AddVertexAt(i, _points[i], 0.0, 0.0, 0.0); } _pline.AddVertexAt(_points.Count, pt, 0.0, 0.0, 0.0); _pline.Closed = true; _model.AppendEntity(_pline); _tran.AddNewlyCreatedDBObject(_pline, true); TransientManager.CurrentTransientManager.AddTransient( _pline, TransientDrawingMode.DirectTopmost, 128, new IntegerCollection()); _hatch = new Hatch(); _hatch.ColorIndex = 1; //set color to RED _model.AppendEntity(_hatch); _tran.AddNewlyCreatedDBObject(_hatch, true); ObjectIdCollection loops = new ObjectIdCollection(); loops.Add(_pline.ObjectId); _hatch.SetHatchPattern(HatchPatternType.PreDefined, "Solid"); _hatch.AppendLoop(HatchLoopTypes.Outermost, loops); _hatch.EvaluateHatch(true); TransientManager.CurrentTransientManager.AddTransient( _hatch, TransientDrawingMode.DirectTopmost, 128, new IntegerCollection()); } #endregion } }
Or you can go to the blog post, where the changed lines of code are highlighted in red:
http://drive-cad-with-code.blogspot.com/2011/01/mimicking-autocads-area-command-with_20.html
Norman Yuan