How to set the Constant property AttributeReference? - c#

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.

Related

What is the most efficient way to validate a SelectImplied against a Filter?

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.

EF Updating entity related table is inserting instead of updating

I have an object I am trying to update using Entity Framework 5.
Once I retrieve the existing object and go to update the fields, it correctly updates the base object "coach", but fails to update the Address object and instead inserts it again instead of updating it with a new primary key even though it has been passed the existing primary key to use again.
Any help is appreciated.
Below is a dumbed down version of the code:
using (AltairEntities context = new AltairEntities())
{
dtlCoach coach = context.dtlCoaches.FirstOrDefault(x => x.CoachID == coachId);
coach.Name = "Bob";
coach.Description = "sample";
coach.dtlCoachAddresses.Add(PrepareAddress(coach.dtlCoachAddresses.First().CoachAddressID));
context.Database.Connection.Open();
context.Entry(coach).State = EntityState.Modified;
context.SaveChanges();
}
public static dtlCoachAddress PrepareAddress(int existingId)
{
dtlCoachAddress newAddress = new dtlCoachAddress();
try
{
newAddress.CoachAddressID = existingId;
newAddress.AddressLine1 = "Line 1";
newAddress.AddressLine2 = "Line 2";
return newAddress;
}
catch (Exception ex)
{
throw ex;
}
}
UPDATE:
So I have found if I feed the existing dtlCoachAddress entity from inside the dtlCoach entity into the PrepareAddress function as a parameter instead of declaring the object as new, it updates correctly.
What is the difference between the dtlCoachAddress object from the entity and the dtlCoachAddress object defined from new, if I pass it all the same parameters? But the two define if the object gets inserted or updated?
I am not sure how you have arranged PKs and FKs in your entities. So this solution has a few assumptions.
Updating again to match OPs methods.
using (AltairEntities context = new AltairEntities())
{
dtlCoach coach = context.dtlCoaches.FirstOrDefault(x => x.CoachID == coachId);
coach.Name = "Bob";
coach.Description = "sample";
//coach.dtlCoachAddresses.Add(PrepareAddress(coach.dtlCoachAddresses.First().CoachAddressID));
//context.Database.Connection.Open();
//context.Entry(coach).State = EntityState.Modified;
var address = context.dtlCoachAddresses.FirstOrDefault(a => a.CoachAddressID == coachId);
if(address != null)
{
address.AddressLine1 = "Line 1";
address.AddressLine2 = "Line 2";
}
context.SaveChanges();
}
/*This function is not required
public static dtlCoachAddress PrepareAddress(int existingId)
{
using (AltairEntities context = new AltairEntities())
{
var address = context.dtlCoachAddresses.FirstOrDefault(a => a.CoachAddressID == coachId);
if(address != null)
{
address.AddressLine1 = "Line 1";
address.AddressLine2 = "Line 2";
context.SaveChanges();//update an existing address.
}
}
catch (Exception ex)
{
throw ex;
}
}*/

Insert into database using Entity Framework from C#

I am trying to insert a text file formatted in C Sharp to a Microsoft SQL server. I have 2 tables Transaction and TMatch in which I want to populate the data. 4 attributes each. I have created 2 classes for each. I am aware of how to input data manually into the database through the .Add() and .SaveChanges().
Here is what I have so far:
//Database insertions
TTransaction txn = new TTransaction();
**txn.Amount = 56; //I want a variable used below (AMOUNT) to go into amount.
txn.TRN = "sdfgsdfg";** //(TxnNo) to go into TRN
ScotiaNYAEntities context = new ScotiaNYAEntities();
context.TTransactions.Add(txn);
context.SaveChanges();
Traversing the text file using a while loop.
{
if (line.Contains("AMOUNT:")) //Look where to end for Transaction Text
{
// For Amount
IsAmount=true;
if(IsAmount)
{
Amount = line.Replace("AMOUNT:", String.Empty).Trim();
Console.WriteLine("AMOUNT: ********");
Console.WriteLine(Amount);
}
}..............................................
I am not sure how to reference a variable instead of just values.
Thank you.
leap of faith but you could have something like this
using (ScotiaNYAEntities context = new ScotiaNYAEntities())
{
foreach (string line in File.ReadLines(pathToFile))
{
if (line.Contains("AMOUNT:"))
{
if (IsAmount)
{
string amount = line.Replace("AMOUNT:", string.Empty).Trim();
TTransaction txn = new TTransaction();
txn.Amount = amount;
txn.TRN = "sdfgsdfg";
context.TTransactions.Add(txn);
}
}
}
context.SaveChanges();
}
This is what I did:
In the for loop for reading the file line by line
String TxnLOC = null;
IsTransactionLocation= false;
if (line.Contains("TRANSACTION LOC:"))
{
IsTransactionLocation = true;
if (IsTransactionLocation)
{
TxnLOC = line.Replace("TRANSACTION LOC:", String.Empty).Trim();
Console.WriteLine("The Transaction Location: ********");
Console.WriteLine(TxnLOC);
//Database insertion fot TTransaction Table
TTransaction txn = new TTransaction();
txn.TRN = txnNo;
txn.Amount = Convert.ToDecimal(Amount);
txn.TransactionLocation = TxnLOC;
context.TTransaction.Add(txn); //Adding to the database
context.SaveChanges();
IsTxnSection = false;//For 1 to many relationship
}
}

c# autocad sideload database binding xrefs

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();
}
}

Create Class instance from string value and assign to same class type in C#

private void QueryDBSchema()
{
// Schema information for the current database connection.
DataTable schema;
// Loop counter.
int loop = 0;
// Clean up the menu so the menu item does not hang while this function executes.
this.Refresh();
// Instantiate an OleDbConnection object.
using (OleDbConnection oleDbConnection = new OleDbConnection("Provider=SQLOLEDB;Password=sa123;User ID=sa;Data Source=mukesh;Initial Catalog=Medi;"))
{
try
{
// Open the connection.
oleDbConnection.Open();
// Retrieve the Table objects.
schema = oleDbConnection.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, new Object[] { null, null, null, "table" });
// Store the table names in the object collection.
for (loop = 0; loop < schema.Rows.Count; loop++)
{
objects.Add(schema.Rows[loop].ItemArray[2].ToString());
}
}
catch (Exception e)
{
// Messages.BadConnection(e);
}
}
}
This function for getting table name in objects list.
// Instantiate the objects collection.
this.objects = new Collection<string>();
QueryDBSchema();
// Gather each of the selected items (if any) into a collection object.
foreach (string item in this.objects)
{
//This should be like this
// "item" obj = new "item";
}
in foreach loop i want to create class object with string saved in item value.How this possible in c# also same name class is exits in different namespace and i want to assign the object to another same class in different namespace and call function named like save in obj1.
such as
// "item" obj1 = new "item";
// "Item" obj2 = new "item";
obj1=obj2;
obj1.Save();
Use
ObjectHandle handle = Activator.CreateInstance("YourAssemblyName_String", "Item");
Item obj = (Item)handle.Unwrap();

Categories