Can anyone help me to create a C# code to delete the viewport in layouts.
I've tried a code to delete the viewport, it compiles no problem, but it doesn't delete the viewport, I am not sure what I am doing wrong here.
Thanks
public class Class1
{
[CommandMethod("haha")]
public static void CreateModelViewports()
{
Document doc = Application.DocumentManager.MdiActiveDocument;
Database db = doc.Database;
using (Transaction trans = db.TransactionManager.StartTransaction())
{
var viewportTable = (ViewportTable)trans.GetObject(db.ViewportTableId, OpenMode.ForWrite);
foreach (ObjectId id in viewportTable)
{
var viewport = (ViewportTableRecord)trans.GetObject(id, OpenMode.ForRead);
// Delete the active viewport
viewport.UpgradeOpen();
viewport.Erase();
}
trans.Commit();
}
}
}
You are erasing a ViewportTableRecord not a Viewport.
You need to get BlockTableRecordId from the Layout you want to erase its viewports. Then iterate through every ObjectId in that BlockTableRecord to see if it is a Viewport. Only then you can delete them
So if you want to delete all Viewports in the current layout you do something like this (You must be in paper space for this to work, because it doesn't check)
[CommandMethod("GOO")]
public static void test()
{
Database DB = Application.DocumentManager.MdiActiveDocument.Database;
using (Transaction trans = DB.TransactionManager.StartTransaction())
{
LayoutManager LM = LayoutManager.Current;
string currentLo = LM.CurrentLayout;
DBDictionary LayoutDict = trans.GetObject(DB.LayoutDictionaryId, OpenMode.ForRead) as DBDictionary;
Layout CurrentLo = trans.GetObject((ObjectId)LayoutDict[currentLo], OpenMode.ForRead) as Layout;
BlockTableRecord BlkTblRec = trans.GetObject(CurrentLo.BlockTableRecordId, OpenMode.ForRead) as BlockTableRecord;
foreach (ObjectId ID in BlkTblRec)
{
Viewport VP = trans.GetObject(ID, OpenMode.ForRead) as Viewport;
if (VP != null)
{
VP.UpgradeOpen();
VP.Erase();
}
}
trans.Commit();
}
}
Related
Here is some simple code for asking the user to select some LINE and / or ARC entities:
_AcDb.TypedValue[] dxfs = new _AcDb.TypedValue[]
{
new _AcDb.TypedValue((int)_AcDb.DxfCode.Operator, "<or"),
new _AcDb.TypedValue((int)_AcDb.DxfCode.Start, "LINE"),
new _AcDb.TypedValue((int)_AcDb.DxfCode.Start, "ARC"),
new _AcDb.TypedValue((int)_AcDb.DxfCode.Operator, "or>"),
};
_AcEd.SelectionFilter sFilter = new _AcEd.SelectionFilter(dxfs);
_AcEd.PromptSelectionOptions pso = new _AcEd.PromptSelectionOptions
{
MessageForAdding = "Select LINES and/or ARCS",
MessageForRemoval = "Remove LINES and/or ARCS",
AllowDuplicates = false
};
_AcEd.PromptSelectionResult res = editor.GetSelection(pso, sFilter);
if (res.Status == _AcEd.PromptStatus.OK)
Now, suppose be modify our tool so that it uses CommandFlags.UsePickSet. Now I can test for an existing selection set:
_AcEd.PromptSelectionResult res = editor.SelectImplied();
If the implied selection set result is OK, how can we easily validate that selection set against our filter? Afterall, the user might accidentally pick up a CIRCLE which we would want to ignore.
I can confirm that the answer here (Filter Pickfirst Selectionset) is still correct. To quote:
With CommandFlags.UsePickSet, the selection filter passed to the EditorGetSelection() method is automatically applied to the active selection if any.
I repeat the code snippet in case the link breaks:
[CommandMethod("Test", CommandFlags.UsePickSet)]
public void Test()
{
Document doc = AcAp.DocumentManager.MdiActiveDocument;
Database db = doc.Database;
Editor ed = doc.Editor;
TypedValue[] filter = { new TypedValue(0, "INSERT") };
PromptSelectionResult psr = ed.GetSelection(new SelectionFilter(filter));
if (psr.Status != PromptStatus.OK) return;
using (Transaction tr = db.TransactionManager.StartTransaction())
{
foreach (SelectedObject obj in psr.Value)
{
BlockReference br = (BlockReference)tr.GetObject(obj.ObjectId, OpenMode.ForWrite);
br.Color = Color.FromColorIndex(ColorMethod.ByAci, 30);
}
tr.Commit();
}
}
All we need to do is add the CommandFlags.UsePickSet and the system will take care of the rest (using your filter). Cool.
i am trying to bind xrefs in a sideload drawing database. the program is halting at this line ' if(!xNode.Database.Filename.Equals(NewDb.Filename))'. i am also receiving this error 'System.NullReferenceException: Object reference not set to an instance of an object.at XBind.RecursiveFileProcessor.ProcessFile(String path).' i've done some reaserch and found VB.NET code to attach a xref and tried to extrapolate that with no success. i'd appreciate someone pointing me in the right direction on this.
using (Database NewDb = new Database(false, true))
{
NewDb.ReadDwgFile(path, FileOpenMode.OpenForReadAndWriteNoShare, true, "");
NewDb.CloseInput(true);
using (Transaction tr = NewDb.TransactionManager.StartTransaction())
{
ObjectIdCollection xrefCollection = new ObjectIdCollection();
XrefGraph xg = NewDb.GetHostDwgXrefGraph(false);
int numOfNodes = xg.NumNodes;
for (int cnt = 0; cnt < xg.NumNodes; cnt++)
{
XrefGraphNode xNode = xg.GetXrefNode(cnt) as XrefGraphNode;
if (!xNode.Database.Filename.Equals(NewDb.Filename))
{
if (xNode.XrefStatus == XrefStatus.Resolved)
{
xrefCollection.Add(xNode.BlockTableRecordId);
}
}
}
if (xrefCollection.Count != 0)
{
NewDb.BindXrefs(xrefCollection, true);
}
tr.Commit();
}
NewDb.SaveAs(path, DwgVersion.Current);
}
Actually, this will work in-memory. Winslow North missing the following line of code after the CloseInput()...
NewDb.ResolveXrefs(true, false);
But also, you do not need the Transaction for this. It's not necessary. I created my own sample and tested it. It works. If you need me to post that, let me know. The problem was that the xNode had a null database due to the fact that the Xref was not resolved. You have to do that manually with the line above.
Don't believe this will work for in-memory database, you may try this approach, see a pice of it below:
[CommandMethod("CHX")]
public void ChangeXref()
{
var doc = Application.DocumentManager.MdiActiveDocument;
if (doc == null) return;
var ed = doc.Editor;
var db = doc.Database;
// Get the database associated with each xref in the
// drawing and change all of its circles to be dashed
using (var tr = db.TransactionManager.StartTransaction())
{
var bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead);
var ms = (BlockTableRecord)tr.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForRead);
// Loop through the contents of the modelspace
foreach (var id in ms)
{
// We only care about BlockReferences
var br = tr.GetObject(id, OpenMode.ForRead) as BlockReference;
if (br != null)
{
// Check whether the associated BlockTableRecord is
// an external reference
var bd = (BlockTableRecord)tr.GetObject(br.BlockTableRecord, OpenMode.ForRead);
if (bd.IsFromExternalReference)
{
// If so, get its Database and call the function
// to change the linetype of its Circles
var xdb = bd.GetXrefDatabase(false);
if (xdb != null)
{
using (var xf = XrefFileLock.LockFile(xdb.XrefBlockId))
{
// Make sure the original symbols are loaded
xdb.RestoreOriginalXrefSymbols();
xdb.RestoreForwardingXrefSymbols();
}
}
}
}
}
tr.Commit();
}
}
Is it possible to change the Constant property of an AttributeReference ?
(the property IsConstant is readonly)
I don't know about objectarx, but in .Net (since you stated c#), try this:
Database currentDB = Application.DocumentManager.MdiActiveDocument.Database;
using (Transaction transaction = currentDB.TransactionManager.StartTransaction())
{
BlockTable blockTable = (BlockTable)transaction.GetObject(currentDB.BlockTableId, OpenMode.ForRead);
//Create the block if it doesn't exist
if (!blockTable.Has("MyBlock"))
{
using (BlockTableRecord myBlock = new BlockTableRecord())
{
myBlock.Name = "MyBlock";
myBlock.Origin = Point3d.Origin;
//You can add geometry here, but I'm just going to create the attribute
using (AttributeDefinition attribute = new AttributeDefinition())
{
attribute.Position = Point3d.Origin;
attribute.Tag = "Constant";
attribute.Prompt = "Enter value: ";
attribute.TextString = "My value";
attribute.Height = 0.5;
attribute.Justify = AttachmentPoint.BottomLeft;
attribute.Constant = true;
myBlock.AppendEntity(attribute);
//add to the block table
blockTable.UpgradeOpen();
blockTable.Add(myBlock);
transaction.AddNewlyCreatedDBObject(myBlock, true);
}
}
transaction.Commit();
}
}
Edit: I just realized you also said AttributeReference and not AttributeDefinition. Without verifying for sure, I think the reference cannot be modified directly since the value is constant. Because of this, you'll have to update the block's definition in the BlockTableRecord, same basic process once you've got it.
Here's the code for updating:
Database currentDB = Application.DocumentManager.MdiActiveDocument.Database;
using (Transaction transaction = currentDB.TransactionManager.StartTransaction())
{
BlockTable blockTable = (BlockTable)transaction.GetObject(currentDB.BlockTableId, OpenMode.ForRead);
//Create the block if it doesn't exist
if (blockTable.Has("MyBlock"))
{
//Get the block
ObjectId myBlockID = blockTable["MyBlock"];
BlockTableRecord myBlock = (BlockTableRecord)transaction.GetObject(myBlockID, OpenMode.ForRead);
//iterate through objects and update the attribute definition
if (myBlock.HasAttributeDefinitions)
{
foreach (ObjectId oid in myBlock)
{
DBObject dbObject = transaction.GetObject(oid, OpenMode.ForRead);
if (dbObject is AttributeDefinition)
{
AttributeDefinition attribute = (AttributeDefinition)dbObject;
attribute.UpgradeOpen();
attribute.TextString = "NewValue";
attribute.Constant = true;
}
}
}
//Remember... BlockRerefences are glorified pointers to the BlockTableRecord that defines them
//so let's get all block references associated to it and update them
foreach (ObjectId oid in myBlock.GetBlockReferenceIds(false, true))
{
BlockReference blockReference = (BlockReference)transaction.GetObject(oid, OpenMode.ForWrite);
blockReference.RecordGraphicsModified(true);
}
transaction.Commit();
}
}
You need to change the AttributeDefinition in the block table record (property Constant), not on the AttributeReference. This property is shared with all the AttributeReference.
From the docs:
AutoCAD itself never creates a constant AttributeReference object.
AutoCAD creates the AttributeReference objects for each BlockReference
based on the AttributeDefinition objects within the referenced
BlockTableRecord. If a constant AttributeDefinition is encountered,
then AutoCAD uses the AttributeDefinition itself instead of creating a
matching AttributeReference.
I'm creating a windows form where a user will run a command in Autocad, it will prompt them to select an object(specifically 3d polylines). A 3d polyline can have a very wide range of Vertex's. I want each Vertex to be on/create it's own row. Each row having 5 columns(properties of each Vertex).
What's the proper container to use for this? I am expecting the user to be able to modify(change elevation for example) each and every property in each vertex. Along with actually deleting any vertices that they want to.
Table Layout Panel? Regular Panel? Here's the code of me "getting" the vertices:
using (AcDb.Transaction oTr = db.TransactionManager.StartTransaction())
{
AcDb.ObjectIdCollection ids = new AcDb.ObjectIdCollection();
AcEd.PromptEntityOptions options = new AcEd.PromptEntityOptions("\nSelect a 3DPolyline:");
options.SetRejectMessage("That is not select a 3DPolyline" + "\n");
options.AddAllowedClass(typeof(AcDb.Polyline3d), true);
AcEd.PromptEntityResult result = ed.GetEntity(options);
if (result.Status != AcEd.PromptStatus.OK) return;
AcDb.Polyline3d oEnt = oTr.GetObject(result.ObjectId, AcDb.OpenMode.ForRead) as AcDb.Polyline3d;
foreach (AcDb.ObjectId oVtId in oEnt)
{
AcDb.PolylineVertex3d oVt = oTr.GetObject(oVtId, AcDb.OpenMode.ForRead) as AcDb.PolylineVertex3d;
//now to populate...something
A DataTable would make sense when gathering the data.
using (var trans = db.TransactionManager.StartTransaction())
{
var options = new PromptEntityOptions("\nSelect a 3DPolyline:");
options.SetRejectMessage("That is not select a 3DPolyline" + "\n");
options.AddAllowedClass(typeof(Polyline3d), true);
var result = ed.GetEntity(options);
if (result.Status != PromptStatus.OK)
return;
var poly = (Polyline3d)trans.GetObject(result.ObjectId, OpenMode.ForRead);
var vertexClass = RXClass.GetClass(typeof(PolylineVertex3d));
var vertexTable = new System.Data.DataTable("Vertices");
vertexTable.Columns.Add("HandleId", typeof(long));
vertexTable.Columns.Add("PositionX", typeof(double));
vertexTable.Columns.Add("PositionY", typeof(double));
vertexTable.Columns.Add("PositionZ", typeof(double));
foreach (ObjectId vertexId in poly)
{
if (!vertexId.ObjectClass.IsDerivedFrom(vertexClass))
continue;
var vertex = (PolylineVertex3d)trans.GetObject(vertexId, OpenMode.ForRead);
vertexTable.Rows.Add(vertex.Handle.Value, vertex.Position.X, vertex.Position.Y, vertex.Position.Z);
}
trans.Commit();
}
Once you've got your vertex data in a table you can bind it to visual controls very easily.
I have been struggling with this problem for some time now and am not able to resolve this. Have read multiple posts and tried several different solutions but nothing has worked. Now I feel like I have come to a stop and really need help with this.
I am using EF 5+ with an DB first and edmx file. I have 3 different tables in my DB:
1. Settlement
2. Cost
3. Shift
Settlement has a collection of both Cost and Shift (with a link table) connected by Association in my edmx file.
I need to insert a new Settlement in my db with a reference to an already existing Cost and Shift collections.
Shifts and Costs included in my Settlement entity I am trying to insert contains all there related data and none of those are modified in any way (same as I retrieved from db).
Here in my method of inserting the entity into my db.
public bool CreateSettlement(Settlement settlement)
{
bool _success;
var _context = new EtaxiEnteties();// ObjectFactory.Get<IETaxiEntitiesContext>();
try
{
var _newSettlement = new Settlement
{
CreateDate = settlement.CreateDate,
Driver = settlement.Driver,
DriverID = settlement.DriverID,
Car = settlement.Car,
CarID = settlement.CarID,
DocPath = settlement.DocPath
};
foreach (var _shift in settlement.Shifts)
{
//var _sh = _context.Shifts.Find(_shift.ShiftID);
//_context.Entry(_sh).CurrentValues.SetValues(_shift);
_newSettlement.Shifts.Add(_shift);
}
foreach (var _cost in settlement.Costs)
{
////var _sh = _context.Costs.Find(_cost.CostID);
////_context.Entry(_sh).CurrentValues.SetValues(_cost);
_newSettlement.Costs.Add(_cost);
}
_context.Settlements.Add(_newSettlement);
_success = _context.SaveChanges() > 0;
}
catch (Exception ex)
{
throw new Exception(ex.Message);
}
return _success;
}
Any help on the issue would be MUCH appreciated.
Here is how I am adding the Cost and Shift to my collection:
I create a Settlement in page:
_settlement = new Settlement
{
CreateDate = DateTime.Now,
Driver = _driver,
DriverID = _driver.DriverID,
Car = _car,
CarID = _car.CarID,
DocPath = _path
};
then when I create a pdf file with selected rows from 2 separated grid views:
foreach (GridDataItem _selectedRow in gwShifts.MasterTableView.Items)
{
if (_selectedRow.Selected)
{
var _shift =
_diaryRepository.GetShiftByID((int) _selectedRow.GetDataKeyValue("ShiftID")).FirstOrDefault();
if (_shift != null)
{
_settlement.Shifts.Add(_shift);
_settlementData.Shifts.Add(_shift);
_settlementData.SplitPercentace = GetTemplateValue(_selectedRow, "lblSplit");
_settlementData.SettlementAmount = GetTemplateValue(_selectedRow, "lblSettlementAmount");
if (_settlementData.Shifts != null)
{
_tableShifts.AddCell(
new PdfPCell(
new Phrase(
_settlementData.Shifts.FirstOrDefault().ShiftDate.ToShortDateString(),
_bodyFont)) {Border = 0});
_tableShifts.AddCell(
new PdfPCell(
new Phrase(
string.Format("{0:c}", _settlementData.Shifts.FirstOrDefault().GrossAmount),
_bodyFont)) {Border = 0});
_tableShifts.AddCell(
new PdfPCell(
new Phrase(
string.Format("{0:c}", _settlementData.Shifts.FirstOrDefault().MoneyAmount),
_bodyFont)) {Border = 0});
_tableShifts.AddCell(new PdfPCell(new Phrase(_settlementData.SplitPercentace, _bodyFont))
{Border = 0});
_tableShifts.AddCell(
new PdfPCell(new Phrase(_settlementData.SettlementAmount, _boldTableFont))
{Border = 0});
_totalAmount.AddRange(new[]
{
Convert.ToInt32(
_settlementData.SettlementAmount.Replace(".", "").
Replace(",", "").Replace("kr", ""))
});
_settlementData.Shifts.Remove(_shift);
}
}
}
}
var _summaryCell =
new PdfPCell(new Phrase("Upphæð: " + string.Format("{0:c}", _totalAmount.Sum()), _boldTableFont))
{
Border = 0,
Colspan = 5,
HorizontalAlignment = Element.ALIGN_RIGHT,
Padding = 5,
BorderWidthTop = 1
};
_tableShifts.AddCell(_summaryCell);
if (_totalAmount.Count != 0)
_totalAmount.Clear();
}
there you see how I add the Shift to this Settlement entity:
var _shift =
_diaryRepository.GetShiftByID((int) _selectedRow.GetDataKeyValue("ShiftID")).FirstOrDefault();
if (_shift != null)
{
_settlement.Shifts.Add(_shift);
then I send this to the reporistory (see method above)
if(_driverRepository.CreateSettlement(_settlement))
{
SetMessage("Uppgjör hefur verið skapað og sent bílstjóra ef e-póstur er skráður á viðkomandi bílstjóra.", "Uppgjör skapað");
pnlSettlement.Visible = false;
pnlDocCreation.Visible = false;
pnlResult.Visible = false;
}
I also tried to simply add param settlement directly to the context but got similar error.
I think this has to do with how you're populating the Shifts and Costs collection. You're trying to add already created records (i.e. they already have their primary key values set) to be saved with the new Settlement entity, but I believe that Entity Framework isn't trying to create new ones that are linked to your new Settlement entity but rather save them to the table as is. In such a case you would indeed have a situation where multiple entities have the same primary key.
I would try the following (I'll show you using the Shifts loop only, but you should be able to apply it to the Costs loop as well):
foreach (var _shift in settlement.Shifts)
{
var newShift = new Shift { /*Copy all of the values from _shift here*/ };
_newSettlement.Shifts.Add(_shift);
context.Shifts.Add(newShift);
}
If that doesn't work I would suggest debugging Costs and Shifts to make sure that you don't have any duplicates in those collections.
If you don't want new Shifts & Costs then I can only assume you need to repoint the existing ones to the new Settlement
foreach (var shift in settlement.Shifts)
{
//either
shift.Settlement = newSettlement;
//or
shift.SettlementId = newSettlement.SettlementId;
//depending on your object model
}
I've just realised that I have misunderstood the question. There are 2 additional tables not shown in the diagram (Costs & Shifts). The problem is trying to create SettlementCost and SettlementShift entities that connect Settlement to Costs\Shifts.
OK, came up with a "ugly" solution on this..but it is resolved.
Changed the Model to be one(Settlement) -> many(Shift or Cost) relationship.
I create a new Settlement and save this one to the DB.
Retrieve each Shift and Cost from the DB update SettlementID on each and save these to the DB.
try
{
var _newSettlement = new Settlement
{
CreateDate = settlement.CreateDate,
DriverID = settlement.DriverID,
CarID = settlement.CarID,
DocPath = settlement.DocPath
};
Add(_newSettlement);
_success = SaveWithSuccess() > 0;
var _settlement = GetAll().FirstOrDefault(x => x.SettlementID == _newSettlement.SettlementID);
if (_success)
{
foreach (var _shift in settlement.Shifts)
{
var _sh = _diaryRepository.GetShiftByID(_shift.ShiftID).FirstOrDefault();
_sh.SettlementID = _settlement.SettlementID;
_diaryRepository.UpdateShift(_sh);
}
foreach (var _cost in settlement.Costs)
{
var _ch = _costRepository.GetCostByID(_cost.CostID);
_ch.SettlementID = _settlement.SettlementID;
_costRepository.UpdateCost(_ch);
}
}
}
not a pretty one, but it resolves the problem.
I am not concerned about the DB request load..it will not be that high in this case.
I would think there is a nicer solution to this but I was not able to find it at this time.
Thanks for all your efforts to help :)