CONNECT C# How to update a text string in a Cell?

I am trying to updated a Text Node containing a single Text Element inside a cell in a DGN file.  Everything seems to work ok until I attempt to replace the existing cell with the modified version using the ReplaceInModel method.  At that point the program will pause for a minute or two and finally generate an error and crash MicroStation CONNECT.

Here is the code I an using to update the text element in the cell.

Can anyone see anything wrong with the code?

A fellow programmer was able to used a similar code written in C++ to update the text element and my code does almost the same thing so I don't understand why it doesn't work.

Note: This method is part of a class that has already stored the Element Ids for the main cell and the text elements located inside the cell.

//------------------------------------------------------------------------------
// setStringId [pubic]
//
// Used to set the ID value for the specifed String ID text elment in the Solar
// rack cell.
//
// Input: uiStringNo - Specifies the index value for the String ID.
// sIdValue - Specifies the ID for for the String ID text element.
//
// Return: SolarRack instance or null.
//------------------------------------------------------------------------------
public void setStringId(uint uiStringNo, string sIdValue)
{
   ElementId stringId = new ElementId();

   bool bFound = false;

   uint i = 1;
   foreach (ElementId currId in StringIdList)
   {
      if (i == uiStringNo)
      {
         stringId = currId;
         bFound = true;
         break;
      }

      i++;
   }

   if (bFound == false)
      return;

   DgnModel activeDgnModel = Session.Instance.GetActiveDgnModel();

   CellQuery rackQuery = null; 

   Element oCell = activeDgnModel.FindElementById(rackElementId);
   if (oCell != null)
   {
      rackQuery = CellQuery.GetAsCellQuery(oCell);
      if (rackQuery == null)
         return;
   }

   rackQuery.ExposeChildren(ExposeChildrenReason.Edit);

   bool bUpdated = false;

   foreach (Element oChildElm in rackQuery.GetChildren())
   {
      if (oChildElm.ElementId != stringId)  
         continue;

      using (TextEdit textEdit = TextEdit.GetAsTextEdit(oChildElm))
      {
         if (textEdit != null)
         {
            DPoint3d origin = DPoint3d.Zero;
            DMatrix3d orientation = DMatrix3d.Zero;

            textEdit.GetSnapOrigin(out origin);
            textEdit.GetOrientation(out orientation);

            TextQueryOptions textOptions = new TextQueryOptions
            {
               ShouldIncludeEmptyParts = false,
               ShouldRequireFieldSupport = false
            };

            TextPartIdCollection textParts = textEdit.GetTextPartIds(textOptions);

            TextBlockProperties textBlockProps = null;
            ParagraphProperties paragraphProps = null;
            RunProperties runProps = null;

            using (TextBlock textBlock = textEdit.GetTextPart(textParts[0]))
            {
               if (textBlock != null)
               {
                  textBlockProps = textBlock.GetProperties();
                  paragraphProps = textBlock.GetParagraphPropertiesForAdd();
                  runProps = textBlock.GetRunPropertiesForAdd();
               }
            }

            if (textBlockProps != null && paragraphProps != null && runProps != null)
            {
               using (TextBlock newTextBlock = new TextBlock(textBlockProps, paragraphProps, runProps, activeDgnModel))
               {
                  textBlockProps.Dispose();
                  paragraphProps.Dispose();
                  runProps.Dispose();

                  if (newTextBlock != null)
                  {
                     newTextBlock.AppendText("Test");
                     newTextBlock.SetUserOrigin(origin);
                     newTextBlock.SetOrientation(orientation);

                     if (textEdit.ReplaceTextPart(textParts[0], newTextBlock) == TextReplaceStatus.Success)
                        bUpdated = true;
                  }
               }
            }
         }
      }
   }

   if (bUpdated)
      rackQuery.ReplaceInModel(oCell);
}

  • Can anyone see anything wrong with the code?

    It's not formatted as code.  Consequently, it's hard to read.  Follow the Forum Guidelines.

    Use XML Comments

    //  Return: SolarRack instance or null

    The method returns void, not an instance of a SolarRack.

    setStringId [pubic]

    I have pubic hair, but no pubic strings.

    Frankly, there's no point in using comments if those comments are misleading or inaccurate.  If you're using Viz Studio or Visual Studio Code, then use Microsoft's code XML comments.  Those editors are smart enough to check your comments.  IntelliSense uses those XML comments to help you code better.

    Undeclared Variables

    Your code introduces undeclared variables.  For example, we have to guess what these do...

    • rackElementId
    • StringIdList

    Refactor

    That large, intractable method could better be broken down into smaller functions that are easier to follow.  For example, this code could be replace with an equivalent method...

    bool bFound = false;
    
       uint i = 1;
       foreach (ElementId currId in StringIdList)
       {
          if (i == uiStringNo)
          {
             stringId = currId;
             bFound = true;
             break;
          }
    
          i++;
       }
    
       if (bFound == false)
          return;

    And put into this method...

    /// <summary>Attempt to find a string ID by index.</summary>
    /// <returns>True if index is valid.</returns>
    bool StringIndexExists (uint uiStringNo, List<ElementId> StringIdList)

    That makes it more obvious what the code is expected to do.  Whoever is maintaining your code in two years (probably you) will be grateful for code that is easier to read, declares its purpose and provides useful comments.

     
    Regards, Jon Summers
    LA Solutions

  • Hi Richard,

    Can anyone see anything wrong with the code?

    As Jon wrote, many things should be improved. Now it's like "your code is wrong, so it cannot be discussed whether it's wrong" ;-)

    What makes the code unreadable and hard to be analyzed:

    • It's posted as not formatted text. Please, always used Insert > Insert code tool when you want to share a code snippet (even one line snippets).
    • Naming used in code is weird, it's like old C naming (even not C++) mixed with VBA style. Are you aware for NET code, recommended style has been defined from NET Framework 1.0?
    • It does not follow clean code rules, there is no even basic split "one function can do one thing only", so I guess it would be even not simple to debug where the problem can be.
    • How disposable instances are managed is wrong. As far as I know, the only correct ways are to encapsulate IDisposable into using (so instance will be disposed automatically) or to try/catch and to call Dispose in finally() ... in fact, these options are equal when compiled to IL.
    A fellow programmer was able to used a similar code written in C++

    Even when NET API follows C++ API concepts closely, they are not the same. Text API is very similar and often an approach used in C++ can be easily transferred to C#. But I think it's not the case of cell content. I asked in separate post what is the best approach to modify cell content in C#.

    Everything seems to work ok

    How do you know it?

    I recommend to split the code and to test independently:

    • How to modify Text Node content
    • How to modify Cell element content

    It seems to me that the code related to Text API is more complicated than necessary. I do not see any reason to create new TextBlock. It exists already and can be modified using carets (see explanation in MicroStation C++ API documentation).

    When such code will work (text element in model, not in cell, will be modified), it can be called from cell content modification code (where I do not know answer now).

    With regards,

      Jan