Wrong attribute collection retrieve

May 18, 2016 at 4:12 PM
Hello everyone, i write here because i've got a problem collecting attributes from blocks.
Basically the program i'm writing use an excel to modify an existing drawing block attributes.
The flow of the program should be:
  1. Load the existing drawing
  2. Load the excel file that contain attributes name and values
  3. Loop through all block in the drawings and for each block loop through the attribute that the block have
  4. If the current attribute tag is equal to the attribute name in the excel it will be filled with the relative value and move to the next attribute.
    So far so good and here comes the problems:
    when i try to retrieve the attribute collection only the first block retrieve is correct, all other attempt got a result of empty attribute, debug says that attribute collection count is 0.
    Here's my code with some comment:
public void ModifyDrawing(string savepath, string drawingpath, string Excelpath)
        {
            //Load drawing and excel
            DxfDocument Doc = DxfDocument.Load(drawingpath);
            SLDocument ExImp = new SLDocument(Excelpath);
            //Check if the header of the Row is correnct
            if (ExImp.GetCellValueAsString(1, 1) == "Attributes Name" && ExImp.GetCellValueAsString(1, 2) == "Attributes Value")
            {
                //Retrieve excel details
                SLWorksheetStatistics stat = ExImp.GetWorksheetStatistics();
                int Rows = stat.EndRowIndex;
                int i = 2;
                int inserted = 0;
                string AttribName = null;
                string AttribValue = null;
                //Loop through all inserted blocks
                //retrieve correctly 5 blocks (i'm using at the moment a drawing with 5 blocks)
                foreach (Insert Block in Doc.Inserts)
                {
                    //The first block of the series retrieve correctly the attribute collection but from the second the attribute collection is empty (count property report 0 attributes)
                    // so off course only the first block will be correctly filled with the data
                    netDxf.Collections.AttributeCollection BlockAttrib = Block.Attributes;
                    i = 2 + inserted;

                    //Loop the blocks attribute
                    foreach (netDxf.Entities.Attribute attrib in BlockAttrib)
                    {
                        //loop throught the excel file rows
                        for (i = i + inserted; i < Rows; i++)
                        {
                            //Set the values
                            AttribName = ExImp.GetCellValueAsString(i, 1);
                            AttribValue = ExImp.GetCellValueAsString(i, 2);

                            //check attribute tag name and excel attribute name value, if they are equal new value will be assigned
                            if (attrib.Tag == AttribName)
                            {
                                //Assign new attribute value correctly in the first block, in the other due to the fact that the attribute collection is empty this will be skipped
                                attrib.Value = AttribValue;
                                //take note of the inserted elemts in order to avoid to insert the same value
                                inserted = inserted + 1;
                                //add 1 to the counter
                                i = i + 1;
                                //exit the for loop and go to the next attribute
                                break;
                            }
                        }
                    }
                }
May 18, 2016 at 4:13 PM
Thnks in advice :)
Coordinator
May 19, 2016 at 6:08 PM
A couple nomenclature points:
  • A Block is a block definition.
  • An Insert is a block reference. Multiples instances a block can appear in a drawing and they are called inserts or block references.
A recommendation:
  • In the dxf Attribute tags are case insensitive, like layer, linetype, text style,... names. Take this into account when making string comparisons like in your line: "if (attrib.Tag == AttribName)"
Having said that, if your block reference is returning an attribute collection with 0 items is because there are no attributes associated with that Insert. Check that the block references of your original drawing actually contains the attributes you seek, and that they have been loaded correctly. If they exist in the drawing but not in the loaded DxfDocument perhaps there is a bug, and I will have to take a look at your dxf.

In AutoCad when an insert is created from a block that contains attribute definitions, a copy of those definitions will be created as attributes in your insert. But, if the attribute definitions are modified after the insert creation, those changes will not be automatically applied. So, you might end up with an insert with no attributes that references a block with attribute definitions.

Daniel
May 23, 2016 at 7:13 AM
Edited May 23, 2016 at 7:13 AM
Thanks for the clarification regardings the nomenclature, i've done some test with a drawings with multiple block reference of the same block definition but the problem is not solved, so unfortunately i think there might be a bug, if needed i can send the drawing that i'm using for test.
Coordinator
May 23, 2016 at 6:07 PM
Open a new bug report in the Issues section, it will allow you to upload your dxf file. I will take a look at it to see what is the problem.

Daniel
May 24, 2016 at 7:44 AM
As you suggest i've opened an issue, i uploaded an archieve with both dxf files and example excel that i'm using.
Thanks :)
Coordinator
May 25, 2016 at 5:32 PM
I have not found any problems reading your files and I closed the bug report. The inserts are reporting the correct number of attributes with the correct values read from the dxf. You have sent me two dxf files "Debut DXF.dxf" and "Method Output.dxf", if the second file is, as the name implies, the output of your program, the saving process is also done correctly. It contains 5 inserts with 5 attributes each, but looks like that you only have applied changes to the attribute values of the first insert. So, the error must be there, in the way you are assigning the new values, not in the way you are accessing the attributes.

Check the variables you are using as counters specially "i" and "inserted". There are a few problem with your variable "i" that probably are messing with how you are accessing the values of your spreadsheet.

At the beginning of your code you are initializing the variable with
int i=2;
then inside the first loop
foreach (Insert Block in Doc.Inserts)
it is assigned with a new value
i = 2 + inserted;
There is no need to initialize "i" to two since it will be overridden, also every time a new insert is read it will be assigned the same value. You decide if this is intentional or not.

Later that same variable is being used as an iterator for your last loop,
for (i = i + inserted; i < Rows; i++)
also that index "i" is not only being incremented by one in the for loop "i++", but also in "i=i+1;". Clean the way you iterate through the values in your spreadsheet, because I do not think is doing what you think is doing.

Daniel
May 26, 2016 at 7:01 AM
At this point i will review the algorithm of the method.
Thanks for your time.
May 26, 2016 at 10:31 AM
After a review with a little work i finally got what i need. Here's for everyone the final working code:
/// <summary>
        /// This method modify the attribute value of an existing drawing with an excel file
        /// This method use the following libraries:
        /// netDXF and ClosedXML
        /// </summary>
        /// <param name="savepath">String that contain the file name and path of the output</param>
        /// <param name="drawingpath">String that retrive the drawing that has to be modified</param>
        /// <param name="Excelpath">Path to the excel file used for fill the opened drawings</param>
        /// <param name="ExImp">A ClodedXML Workbook created based on the excel file</param>
    public void ModifyDrawing(string savepath, string drawingpath, string Excelpath, ClosedXML.Excel.XLWorkbook ExImp)
        {
            //Load drawing
            DxfDocument Doc = DxfDocument.Load(drawingpath);
                        
            //The first Row of the excel must contain Attribute Name, and Attribute Value filters (optionals if needed just delete the if statement)
            if (ExImp.Worksheet(1).Cell("A1").Value.ToString() == "Attributes Name" && ExImp.Worksheet(1).Cell("B1").Value.ToString() == "Attributes Value")
            {
                //Retrieve excel details
                int Rows = ExImp.Worksheet(1).RowCount();
                int i;
                string AttribName;
                string AttribValue;
                //Start a loop through the Excel rows
                 for (i = 2; i < Rows;)
                {
                    //Loop through all inserts
                    for (int k = 0; k < Doc.Inserts.Count;)
                    {
                            //Loop Through the attribute in the insert
                           for (int x = 0; x < Doc.Inserts[k].Attributes.Count;)
                            {
                                //Retrieve attribute Tag from excel and relative value
                                AttribName = ExImp.Worksheet(1).Column(1).Cell(i).Value.ToString();
                                AttribValue = ExImp.Worksheet(1).Column(2).Cell(i).Value.ToString();
                                //If the Insert contains an attribute definition that match the excel it will be modified
                                if (Doc.Inserts[k].Block.AttributeDefinitions.ContainsTag(AttribName))
                                    {
                                        Doc.Inserts[k].Attributes[x].Value = AttribValue;
                                        //Move to next excel row and next attribute
                                        x++;
                                        i++;
                                    }
                                    else
                                    {
                                       //Move to the next attribute
                                        x++;
                                    }
                            }
                      //Move to the next Insert
                      k++;
                    }
                    //Once the Inserts are finished there's no need to continue loop through the excel rows, so we can break the for loop
                    break;
                }
                //Save the modified DXF
                Doc.Save(savepath);
                MessageBox.Show("Modification completed");
                return;
            }
            else
            {
                MessageBox.Show("Wrong excel file setup, please use the wizard to create a new one.");
                return;
            }
        }
Feel free to use it :)