I need to insert an external DWG into an AutoCAD drawing via C# plugin.
I need to "ask" to the user the insertion point and rotation of the inserted block.
Until now I've always used a lisp function that calls the command "._-insert" which gives a thumbnail of the block under the mouse, allows the user to click into the drawing to set the insertion point and from that point allows the user to click one more time to set the rotation.
Now I want to avoid the use of Lisp or the use of low level API of AutoCAD because I need a solution that runs over various CAD environments.
What I found is something like this:
public static void InsertDwg(string dwgName)
{
CADAPI.ApplicationServices.Document doc = CADAPI.ApplicationServices.Application.DocumentManager.MdiActiveDocument;
CADDB.Database db = doc.Database;
CADAPI.EditorInput.Editor ed = doc.Editor;
CADDB.ObjectId ObjId;
using (CADDB.Transaction trx = db.TransactionManager.StartTransaction())
{
CADDB.BlockTable bt = db.BlockTableId.GetObject(CADDB.OpenMode.ForRead) as CADDB.BlockTable;
CADDB.BlockTableRecord btrMs = bt[CADDB.BlockTableRecord.ModelSpace].GetObject(CADDB.OpenMode.ForWrite) as CADDB.BlockTableRecord;
using (CADDB.Database dbInsert = new CADDB.Database(false, true))
{
dbInsert.ReadDwgFile(dwgName, CADDB.FileOpenMode.OpenForReadAndAllShare, true, string.Empty);
ObjId = db.Insert(Path.GetFileNameWithoutExtension(dwgName), dbInsert, true);
}
CADAPI.EditorInput.PromptPointOptions ppo = new CADAPI.EditorInput.PromptPointOptions("\nInsertion Point");
CADAPI.EditorInput.PromptAngleOptions ppa = new CADAPI.EditorInput.PromptAngleOptions("\nInsert Rotation");
CADAPI.EditorInput.PromptPointResult ppr;
ppr = ed.GetPoint(ppo);
CADAPI.EditorInput.PromptDoubleResult ppd = ed.GetAngle(ppa);
if (ppr.Status == CADAPI.EditorInput.PromptStatus.OK)
{
CADGEOM.Point3d insertPt = ppr.Value;
CADDB.BlockReference bref = new CADDB.BlockReference(insertPt, ObjId);
btrMs.AppendEntity(bref);
trx.AddNewlyCreatedDBObject(bref, true);
trx.Commit();
}
}
}
But here I have two problems:
The main one is that there is no preview under the mouse.
The second is that the user needs to click 3 times instead of 2 to set both the insertion point and the rotation.
Is there any way that doesn't use some kind of SendCommand and does all of this stuff?
TIA
It seems Jigging is the way to go to allow the preview. I have three links for you.
Jigging multiple entities with the DrawJig
Using a jig to rotate an AutoCAD entity via .NET
Using transient graphics to simulate AutoCAD’s MOVE command using .NET
The first is an example of creating a simple jig with polylines - you could extend this to a block.
The second link is similar but applies rotation to the mix. This is applied to a rectangle but again could be modified to accomodate a block.
The third link describes a different method - AutoCADs transient graphics interface. You must be using AutoCAD 2009 or later to use this method.
The last two links are from the Through the Interface blog, where you may find some more examples and is a very good starting point if you have problems, especially for coding C#.
You will want to use the AcEdJig class. It provides the preview. You will have to write the code to collect the insert point and rotation and to transform the block accordingly.
Here is the first link from my google search for example usage code.
Related
I have a simple SharpMap Map object (WinForms) which contains a TileAsyncLayer (OpenStreetMap using BruTile) within the BackgroundLayers collection.
I would like to have the map either wrap at the edges or repeat so that the user can fan continuously east/west without ever reaching the "end" of the map, but there doesn't seem to be a built in option to do this. What would be the simplest way to enable infinite scrolling?
My code:
BruTile.Web.HttpTileSource source = KnownTileSources.Create();
TileAsyncLayer background = new TileAsyncLayer(source, "OSM");
mapBox1.Map.BackgroundLayer.Add(background);
mapBox1.Map.ZoomToExtents();
mapBox1.Refresh();
mapBox1.ActiveTool = MapBox.Tools.Pan;
Software Versions:
SharpMap: 1.2.0-pre.182830673
BruTile: 2.1.2
I'm trying to update XAxis on Active CATIA document using C#. Anyone knows how to do that? somehowGetActiveAxisSysObject() is placeholder in pseudo code:
Array xMatrix = Array.CreateInstance(typeof(double), 3);
xMatrix.SetValue(5.0, 0);
xMatrix.SetValue(0.0, 1);
xMatrix.SetValue(0.0, 2);
MECMOD.AxisSystem targetAxisSys = **somehowGetActiveAxisSysObject();**
targetAxisSys.PutXAxis(xMatrix);
THANK YOU!
It's been a while since I worked with Catia in C# so the code below may not run properly.
First, I will assume you are working in a part document and that you have created an axis system from the menu: Insert->AxisSystems->AxisSystem (or named something similar). Next, I will also assume that you went into this items properties and renamed it to "Larry".
MECMOD.AxisSystems thisPartsAxisSysCollection = (MECMOD.AxisSystems)Part.AxisSystems;
MECMOD.AxisSystem oneAxisSys = thisPartsAxisSysCollection(1); //Index is name unknown
//or
MECMOD.AxisSystem oneAxisSys = thisPartsAxisSysCollection("Larry");
oneAxisSys.PutXAxis("your data");
If this is not what you are trying to do, but instead are trying to move the part's origin then you will need to access Part.OriginElements instead and use that object's PlaneXY, PlaneYZ, and PlaneZX. Unfortunately those are read only if I remember correctly.
On a GoogleMap I am drawing a polyline to show a route. The lat/long-data is requested via Google Directions API and I receive correct data, usually for two different cities, from which I extract the lat/long-values and add it to an Polyoptions-object, which is added to the Polyline of the Map. Whatever the route is long, how much positions/latLng are available, the route never has more than around 8 straight lines. I have read a lot of stuff, and I can be sure, that the route is created correctly, I can even delete one to create another one. I have also tried "geodesic()" with true an false. No change.
Can anybody tell how I can get out of this?
Some code snippet
polylineOptions = new PolylineOptions();
foreach (var position in routeCoordinates)
{
polylineOptions.Add(new LatLng(position.Latitude, position.Longitude));
}
gMap.AddPolyline(polylineOptions);
Added on Sept 09, 2016
I started by using the points for the legs-steps, lots of points but wrong route, so I finally used only the final overview for the route this way
if (createRouteStep == true && prop.Equals("overview_polyline"))
{
i++;
line = getPropAndValue(lines[i]);
prop = getProp(line);
propValue = getPropValue(line);
string point = propValue.Replace(com, space).Trim();//= "yhoyHg~ol#nvgBgbjAbcbByxlDr_iArqG";
startRS = new RouteStep();
startRS.RouteID = routeCounter;
startRS.StepPoints = point;
sql.insertRouteStep(startRS);
i++;
}
The code is stored in an sqlite database, from which is later retrieved and decoded. Decoding works correct, I have tried a sample (points-result in the comment above) from the mentioned Google-test-site for this purpose and the route was the same as on the site. The points from my own request for the route have the correct direction, but don't follow any road or reach the destination at all.
I am working from the sample project here: http://www.codeproject.com/Articles/8086/Extending-the-save-file-dialog-class-in-NET
I have hidden the address/location bar at the top and made other modifications but I can't for the life of me manage to disable the button that lets you go up to the parent folder. Ist is in the ToolbarWindow32 class which is the problem. This is what I have at the moment but it is not working:
int parentFolderWindow = GetDlgItem(parent, 0x440);
//Doesn't work
//ShowWindow((IntPtr)parentFolderWindow, SW_HIDE);
//40961 gathered from Spy++ watching messages when clicking on the control
// doesn't work
//SendMessage(parentFolderWindow, TB_ENABLEBUTTON, 40961, 0);
// doesn't work
//SendMessage(parentFolderWindow, TB_SETSTATE, 40961, 0);
//Comes back as '{static}', am I working with the wrong control maybe?
GetClassName((IntPtr)parentFolderWindow, lpClassName, (int)nLength);
Alternatively, if they do use the parent folder button and go where I don't want them to, I'm able to look at the new directory they land in, is there a way I can force the navigation to go back?
Edit: Added screenshot
//Comes back as '{static}', am I working with the wrong control maybe?
You know you are using the wrong control, you expected to see "ToolbarWindow32" back. A very significant problem, a common one for Codeproject.com code, is that this code cannot work anymore as posted. Windows has changed too much since 2004. Vista was the first version since then that added a completely new set of shell dialogs, they are based on IFileDialog. Much improved over its predecessor, in particular customizing the dialog is a lot cleaner through the IFileDialogCustomize interface. Not actually what you want to do, and customizations do not include tinkering with the navigation bar.
The IFileDialogEvents interface delivers events, the one you are looking for is the OnFolderChanging event. Designed to stop the user from navigating away from the current folder, the thing you really want to do.
While this looks good on paper, I should caution you about actually trying to use these interfaces. A common problem with anything related to the Windows shell is that they only made it easy to use from C++. The COM interfaces are the "unfriendly" kind, interfaces based on IUnknown without a type library you can use the easily add a reference to your C# or VB.NET project. Microsoft published the "Vista bridge" to make these interfaces usable from C# as well, it looks like this. Yes, yuck. Double yuck when you discover you have to do this twice, this only works on later Windows versions and there's a strong hint that you are trying to do this on XP (judging from the control ID you found).
This is simply not something you want to have to support. Since the alternative is so simple, use the supported .NET FileOk event instead. A Winforms example:
private void SaveButton_Click(object sender, EventArgs e) {
string requiredDir = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
using (var dlg = new SaveFileDialog()) {
dlg.InitialDirectory = requiredDir;
dlg.FileOk += (s, cea) => {
string selectedDir = System.IO.Path.GetDirectoryName(dlg.FileName);
if (string.Compare(requiredDir, selectedDir, StringComparison.OrdinalIgnoreCase) != 0) {
string msg = string.Format("Sorry, you cannot save to this directory.\r\nPlease select '{0}' instead", requiredDir);
MessageBox.Show(msg, "Invalid folder selection");
cea.Cancel = true;
}
};
if (dlg.ShowDialog() == DialogResult.OK) {
// etc...
}
}
}
I don't this is going to work. Even if you disable the button they can type ..\ and click save and it will take them up one level. You can't exactly disable the file name text box and maintain the functionality of the dialog.
You'd be better off either using the FolderBrowserDialog and setting it's RootFolder property and asking the user to type the filename in or auto generating it.
If the folder you are wanting to restrict the users to isn't an Environment.SpecialFolder Then you'll need to do some work to make the call to SHBrowseForFolder Manually using ILCreateFromPath to get a PIDLIST_ABSOLUTE for your path to pass to the BROWSEINFO.pidlRoot
You can reflect FolderBrowserDialog.RunDialog to see how to make that call.
Since you want such custom behaviors instead of developing low level code (that is likely yo break in the next versions of windows) you can try to develop your file picker form.
Basically it is a simple treeview + list view. Microsoft has a walk-through .
It will take you half a day but once you have your custom form you can define all behaviors you need without tricks and limits.
In a Microsoft Surface 1.0 SDK project based on WPF, I'd like to transform contacts captured in a small part of the screen to match the whole screen (like a virtual touchpad).
After capturing a contact and transforming it's position and orientation I would like to send it back to the event queue. I already figured out that there seems to be no way to create a "new Contact()" or to change anything in the "ReadOnlyContactCollection" (like it's name already says).
Here's what I was trying to do:
private void OnContactDown(object sender, ContactEventArgs e)
{
base.OnContactDown(e);
e.Contact.Capture(this);
// transform the contact's center and orientation
// and write them back into e.Contact via own private method
// e.Contact = transformContact(e.Contact);
// keep transformed contact in the event queue
// so it can be processed at it's new position
e.Handled = false;
}
My next idea was to make use of the simulator and automation to create SimulatedContacts, but sadly this doesn't work on the surface table itself, only in the simulator.
Is there any way to send out "virtual" contacts (that don't exist in the raw image) so that they will be recognized by the surface (without the use of the simulator)? How does the SurfaceInput.exe send out the recognized contacts?
Surface v1 doesn't officially support WPF 4.0, but others have figured out how to take Surface v1 input and route it into the standardized & extensible touch APIs that come with WPF 4.0. Take a look at http://nui.joshland.org/2010/07/how-to-write-surface-applications-with.html for creating a custom "touch device" that transforms Surface input into WPF 4.0 input events. Following that same approach, you can create another "touch device" on your own in order to pass in your fake touches.