Announcements
Due to scheduled maintenance, the Autodesk Community will be inaccessible from 10:00PM PDT on Oct 16th for approximately 1 hour. We appreciate your patience during this time.
.NET
cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Problems inserting a Table and Block then updating values

8 REPLIES 8
SOLVED
Reply
Message 1 of 9
nshupeFMPE3
518 Views, 8 Replies

Problems inserting a Table and Block then updating values

I have this piece of code, that takes the BlockTableRecord of a Layout, and inserts a Table that is passed in, as well as inserts a new Block and updates Attributes inside the block.

public static void UpdateLayoutBtr(BlockTableRecord btr, Transaction tran, Table table, MyDataObject data)
{
    Point3d tableInsertPoint = table.Position;
    if (btr == null) return;

    btr.AppendEntity(table);
    tran.AddNewlyCreatedDBObject(table, true);
    //inserts MText above inserted Table
    InsertNote(btr, tran, tableInsertPoint);
    //Erases old block
    RemoveOldBlock(btr, tran);
    //finds block in block table and inserts into BTR, then updates attributes
    MyBlock.InsertNewBlock(
        btr,
        tableInsertPoint.Add(new Vector3d(2.8694, .69, 0)),
        data);
}

 

public static void InsertNewBlock(BlockTableRecord btr, Point3d insertPoint, MyDataObject data)
{
    ObjectId blockId = ObjectId.Null;
    using (Transaction tr = btr.Database.TransactionManager.StartOpenCloseTransaction())
    {
        try
        {
            blockId = Active.Database.InsertIntoBlockTableRecord(
        BlockNameCustomGrooveCount,
        insertPoint,
        layerName: "My_Layer",
        modelOrPaper: btr,
        transaction: tr);

            if (blockId == ObjectId.Null) return;
            tr.Commit();
        }
        catch (Exception ex)
        {
            Active.WriteMessage($"Error in {nameof(MyBlock)}.{nameof(InsertNewBlock)}: {ex.Message}");
            tr.Abort();
        }
    }

    if(blockId == ObjectId.Null) return;
    FillAttributes(blockId, results);
}
private static void FillAttributes(ObjectId tableId, MyDataObject results)
{
    using (Transaction tr = tableId.Database.TransactionManager.StartOpenCloseTransaction())
    using(BlockReference br = tr.GetObject(tableId, OpenMode.ForWrite, false, true) as BlockReference)
    {
        if (br == null) return;
        //Format the total length of lines
        string slineLengths = Converter.DistanceToString(results.LineLength, DistanceUnitFormat.Architectural, 0);

        foreach (ObjectId attId in br.AttributeCollection)
        {
            using (AttributeReference attRef =
                   tr.GetObject(attId, OpenMode.ForWrite, false, true) as AttributeReference)
            {
                switch (attRef.Tag)
                {
                    case "Turns:": attRef.TextString = results.ArcCount.ToString(); break;
                    case "Length:": attRef.TextString = slineLengths; break;
                    default: break;
                }
            }
        }
    }
}


And this is the code that generates the Table that is passed in to UpdateLayoutBtr

public static Table Update(Transaction tr, Layout layout, MyDataCollection tableData)
{
    if (layout == null) return null;
    ObjectIdCollection tableIds = layout.GetTableIds(Title);
    if (tableIds == null || tableIds.Count == 0) return null;

    Point3d oldPosition = Point3d.Origin;
    int oldCount = 0;
    
    using (Table oldTable = tr.GetObject(tableIds[0], OpenMode.ForWrite, false, true) as Table)
    {
        if (oldTable == null) return null;
        oldPosition = oldTable.Position;
        oldCount = oldTable.Rows.Count-3; //3 rows are Title, headers, bottom row
        oldTable.Erase();
    }
    //Point3d insertPoint = scheduleInsertPoint(tableData.Count - oldCount, oldPosition);
    Point3d insertPoint = oldPosition;
    if (insertPoint == Point3d.Origin) return null;

    
    Table tb = BuildTable(Title, HEADERS, insertPoint, tableData);
    tb.Layer = LayerName;
    tb.Color = Color.FromColorIndex(ColorMethod.ByAci, 0);

    return tb;
}
public static Table BuildTable(string title, List<string> headers, Point3d insertPoint, MyDataCollection tableData)
{
    /*
         *Table gets created with default 1 row and 1 column
         * so when inserting default rows and columns, remove old row and column
         * which get pushed to the last index
         */

    Table tb = new Table();

    try
    {
        tb.Position = insertPoint;
        tb.Layer = LayerName;
        tb.TableStyle = Active.Database.Tablestyle;

       

        int titleRow = 0, headersRow = 1, totalRow = 2;


        tb.InsertColumns(0, 1.5, 4);
        tb.DeleteColumns(tb.Columns.Count - 1, 1);//deletes the default column
        tb.SetColumnWidth(1.5);
        tb.Columns[1].Width = 1.63;
        tb.Columns[2].Width = 1;


        tb.InsertRows(0, .5, 3);
        tb.DeleteRows(tb.Rows.Count - 1, 1);
        tb.SetRowHeight(.5);
        tb.Cells.TextHeight = .19;//main text height

        //Title row
        CellRange titleRange = CellRange.Create(tb, titleRow, 0, titleRow, 3);
        tb.MergeCells(titleRange);
        tb.Rows[titleRow].TextHeight = .25;//bigger title
        tb.Cells[titleRow, 0].TextString = title;

        tb.Rows[titleRow].Alignment = CellAlignment.MiddleCenter;

        //headers row
        tb.Cells[headersRow, zoneCol].TextString = "FIRST";
        tb.Cells[headersRow, manifoldCol].TextString = "SECOND";
        tb.Cells[headersRow, loopCol].TextString = "THIRD";
        tb.Cells[headersRow, lengthCol].TextString = "FOURTH";

        tb.Rows[headersRow].Alignment = CellAlignment.MiddleCenter;


        foreach (MyData data in tableData)
        {
            int row = tb.Rows.Count - 1;
            tb.InsertRows(row, .38, 1);

            tb.Cells[row, 0].SetValue(data.First, ParseOption.SetDefaultFormat);

            tb.Cells[row, 1].SetValue(data.Second, ParseOption.SetDefaultFormat);

            tb.Cells[row, 2].SetValue(data.Third, ParseOption.SetDefaultFormat);

            tb.Cells[row, 3].TextString = data.Fourth;

        }

        //total row
        int bottomRow = tb.Rows.Count - 1;

        tb.Cells[bottomRow, manifoldCol].TextString = "TOTAL";

        tb.Cells[tb.Rows.Count - 1, 3].Contents.Add();
        tb.Cells[tb.Rows.Count - 1, 3].Contents[0].Formula = "=Sum(D3:D" + (tb.Rows.Count - 1) + ")";


        tb.GenerateLayout();
    }
    catch (Exception ex)
    {
        Active.WriteMessage($"\nError in {nameof(BuildTable)}: {ex.Message}");
        return null;
    }
    return tb;
}


There is already a Table in the layout with the proper Title, which is a const string. I delete that Table and then insert the new one.

So here is the weird part. If I run this code like this the Table gets inserted, but only has the Title, Headers, and Total rows. None of the data added in the foreach loop. Though when I'm in the debugger, I DO see the rows added. I make sure that the Table getting added to the Layout is the same one built, and it is, with the proper amount of rows as I would expect from the data passed to the Table builder. But once the operation ends, like I say above, the Table does not come out as I would expect, it does not have the data rows.

The Block I insert also does not have the attributes edited.

After so much debugging and trying weird things I came across two weird situations. If I change

//Title is a readonly const string
Table tb = BuildTable(Title, HEADERS, insertPoint, tableData);

to

Table tb = BuildTable("Test", HEADERS, insertPoint, tableData);


Both the Table and Block come out updated in AutoCAD. This will delete the Table that already exists in the Layout with the proper Title, and replace it with a new one with a Title reading "Test". 

 

If I leave the Table title alone, back to the original. If I instead comment out appending appending and adding the new Table 

//btr.AppendEntity(table);
//tran.AddNewlyCreatedDBObject(table, true);

 

The old Table gets deleted as expected, and the new one does not appear, which is also expected. But the Block will now be updated??

I'm at a loss for what to do, any help would be greatly appreciated

Labels (7)
8 REPLIES 8
Message 2 of 9


@nshupeFMPE3 wrote:

I have this piece of code, that takes the BlockTableRecord of a Layout, and inserts a Table that is passed in, as well as inserts a new Block and updates Attributes inside the block.

 


And this is the code that generates the Table that is passed in to UpdateLayoutBtr

 

 

 

public static Table Update(Transaction tr, Layout layout, MyDataCollection tableData)
{
...
    
    Table tb = BuildTable(Title, HEADERS, insertPoint, tableData);

}

 

 

Your call  to BuildTable() passes the identifier 'Title' as the first argument. Where is that variable declared/assigned?

 

One other thing I noticed is that you have APIs that take a Transaction as an argument,  and within the API you commit the transaction. That is not a good design pattern and can easily become the source of bugs. If you take a transaction as an argument you should use it, but not commit or abort it, because the responsibility for doing that should rest with the calling code that starts the transaction.




Message 3 of 9

Title is declared in the class containing BuildTable

private const string Title = "My Table";


I've also made sure not to commit any transactions that are passed in as arguments, for the reasons you've mentioned.

Message 4 of 9

You say that if you replace Title with a literal string, the code works as expected?

 

That's odd. Have you looked at the value of 'Title' in the debugger at the point where the call to BuildTable() is made?

 

Message 5 of 9

I have looked at that. 

The Update() method starts by finding an existing Table in the Layout and erasing it. This is does properly, using title which is a const string set to "My Table" to find the existing Table and delete it.

Then Title is properly used again as a parameter passed to BuildTable(). And when I go into BuildTable() while debugging I see the title parameter with the proper value "My Table".

I just ran this test again by doing the following. Leaving Title in use for the find and delete portion of Update(). But replacing Title the variable with "Test" a string constant for the call to BuildTable().

BuildTable("Test", ... , ... , ... )

Running the code this way, I see the new table and the block with its attributes updated. 

I will note that I have run the same test as described above, but instead of using Title the variable or "Test" for the call to BuildTable(), I used the string that is assigned to Title the variable. When I do this I do not see the changes, I again have the problem with the table and block not showing their changes. 

Message 6 of 9
nshupeFMPE3
in reply to: nshupeFMPE3

Another oddity is that UpdateLayoutBtr() is called by other parts of my code and works properly. I have an event that is triggered when an attribute on a block is edited. The end of that event handler is a call to UpdatLayoutBtr(), and it successfully updates the Table and Block


Message 7 of 9

Did you set a breakpoint inside of your BuildTable() method and check what value is being passed into that argument (both with a literal string and the Title constant) ?

 

 

Message 8 of 9

Yes I've set a break point inside of the BuildTable() method, and the "title" parameter inside the method is properly set as expected. That "title" parameter is then used after merging the top row to set the TextString of the Table.

CellRange titleRange = CellRange.Create(tb, titleRow, zoneCol, titleRow, lengthCol);
tb.MergeCells(titleRange);
tb.Rows[titleRow].TextHeight = .25;//bigger title
tb.Cells[titleRow, 0].TextString = title;

 

Message 9 of 9
nshupeFMPE3
in reply to: nshupeFMPE3

As I was writing out a replay about further experiments I ran, I found the answer. The code that was calling the Update() method was being run multiple times that it shouldn't have been. And it was passing in DataObjects for generating the table that had no data. 

Sorry for wasting any time, but thanks for helping me narrow down what the problem was.

Can't find what you're looking for? Ask the community or share your knowledge.

Post to forums  

AutoCAD Inside the Factory


Autodesk Design & Make Report