[CONNECT C#] - Issue with TagElement.ReplaceInModel(null)

I'm trying to update the URL for Engineering Links in a collection of DGN files. The problem is that I seam to be creating duplicates of the TAG when writing the updated element back into the model.

After processing a file, following the engineering link still takes you to the old address and not the new one.

/// <summary>
/// Scans model looking for tag elements processing them withScans model looking for tag elements, processing them with 
/// <see cref="TagElementProcessor(Element, DgnModelRef)"/>
/// </summary>
/// <param name="model">The model to scan</param>
private void UpdateEngineeringLinks(DgnModel model) {
    var criteria = new ScanCriteria();
    var elementTypes = new MSElementType[] { MSElementType.Tag };
    criteria.AddElementTypes(elementTypes);
    criteria.SetModelRef(model);
    var status = criteria.Scan(TagElementProcessor);
}

/// <summary>
/// Call back method for UpdateEngineeringLinks model scan. <see cref="Bentley.DgnPlatformNET.ScanDelegate"/>
/// <para>
/// If the element is an internet url tag, get the current value and then use <see cref="UpdateValue(string, out bool)"/>
/// to perform a replace of the matching part. Then if updated, set as the new value for the tag.
/// </para>
/// </summary>
/// <param name="el">The current element that matches the scan criteria</param>
/// <param name="modelRef">The model</param>
/// <returns></returns>
public StatusInt TagElementProcessor(Element el, DgnModelRef modelRef) {
    if(el.ElementType == MSElementType.Tag) {
        var model = modelRef.GetDgnModel();

        if (model.IsReadOnly) {
            return StatusInt.Error;
        }

        TagElement tagElement = (TagElement)el;
        if(tagElement.TagSetName.Equals( "internet", StringComparison.InvariantCultureIgnoreCase)
            && tagElement.TagName.Equals("url", StringComparison.InvariantCultureIgnoreCase)) {

            var oldValue = tagElement.GetTagValue().ToString();
            var newValue = UpdateValue(oldValue, out bool updated);

            if (updated) {
                LogDetails.AppendLine($"Updating Engineering link {tagElement.TagName} from '{oldValue}' to '{newValue}'");
                tagElement.SetTagValue(newValue);
                tagElement.ReplaceInModel(null);
            } else {
                LogDetails.AppendLine($"Skipping Engineering link {tagElement.TagName} with value '{oldValue}'");
            }

            return StatusInt.Success;
        }
    }

    return StatusInt.Error;
}

Am I missing something? (easily done given the poor quality of the API documentation).

  • // Replace tag text in model
    string chStr = ""; //assign string
    TextEdit textEdit = tagEl.AsTextEdit();
    TextPartIdCollection textParts = textEdit.GetTextPartIds(new TextQueryOptions());
    
    if (textParts.Count >= 1)
    {
    TextBlock textBlock = textEdit.GetTextPart(textParts[0]); //Go thru textParts array to look for text you want to replace
    
    textBlock.ReplaceText(chStr, textBlock.CreateStartCaret(), textBlock.CreateEndCaret());
    if (TextReplaceStatus.Success == textEdit.ReplaceTextPart(textParts[0], textBlock))
    textEdit.ReplaceInModel(tagEl);
    
    textBlock.Dispose();
    }
    textParts.Dispose();
    textEdit.Dispose();

    I hope helps!

    Code is C# - you can add code to get the text you want to replace and add to the Text Block of the tag. 

    I had to dig through the API's, but I did figure it out.

    Donna Rodrick

  • I was wondering if you ever figured out how to replace a tag's value in your design file.  I am can update the value of the tag but get an error when using the ReplaceInModel API - I am writing in C#.

    MessageBox1.Text = MessageBox1.Text + "current tag value: " + tagEl.GetTagValue().ToString() + "\n";
    
    tagEl.SetTagValue(chStr);
    TextQueryOptions textQueryOptions = new TextQueryOptions
    {
    ShouldIncludeEmptyParts = false,
    ShouldRequireFieldSupport = false
    };
    TextPartIdCollection textParts = tagEl.GetTextPartIds(textQueryOptions);
    TextPartId textPart = textParts[0];
    TextBlock txtBlock = tagEl.GetTextPart(textPart);
    //Check for change via a text block, it shows the value was changed, so I thought 
    //that maybe I could just use the ReplaceText for the text block
    MessageBox1.Text = MessageBox1.Text + "txtBlock value: " + txtBlock.ToString() + "\n";
                                
    Caret startCaret = txtBlock.CreateStartCaret();
    Caret endCaret = txtBlock.CreateEndCaret();
    txtBlock.ReplaceText(chStr, startCaret, endCaret);
    tagEl.ReplaceTextPart(textPart, txtBlock);

    I would love to know how you got around this issue!!

    Thank you,

    Donna

  • Note that Engineering links are essentially stored as Tags

    The relevant information is provided in C++ header file ..\SDK\include\Mstn\MdlApi\tagdata.h.

    I'll include the option to convert the Engineering links to Design links

    Design Links are provided by an ECSchema.  The DgnECHostType Enumeration declares and defines value DgnECHostType.DesignLinkHowever, AFAIK there is no public API, in any language, that deals with Design Links.  Only the C++ MicroStationAPI provides a Design Link API: here's an example.

    Please make a new post for questions about Design Links.

     
    Regards, Jon Summers
    LA Solutions

  • LinkUpdateTest.dgn

    This DGN file contains two squares, the square on the left has an engineering link while the one on the right has a design link. I'm attempting to create is a find and replace URL tool that will update part or all of the URL in Design and Engineering Links.

    I have no problem updating the engineering links using a MicroStation VBA macro and will us that approche for the current job, but would like to complete the tool sometime in the near future at which point I'll include the option to convert the Engineering links to Design links.

    I also have another project coming up that will require Tag values to be updated so am keen to get the code required for editing tags sorted.

    Note that Engineering links are essentially stored as Tags (tag set name:- internet). 

  • Which managed .NET code example shows how to modify a component of a complex element

    I don't know if there is such an example.  The idiomatic way to modify a complex element is...

    • Read the element into memory
    • Find the component to modify
    • Replace the component with a new element
    • Rewrite the entire complex element

    However, a tag element is not part of its host element.  The host element is not affected when you attach a tag.  A tag is associated with its host through the Dependency Engine using the host's Element ID.  The tag's host Element ID is stored in the tag element.  More here.

    I don't think there's any guidance on this, but I assume that it's best to attach a tag to the outermost header of a complex element.

     
    Regards, Jon Summers
    LA Solutions

  • Hi Dave,

    can you post some simple DGN as example to ensure it would be clear how your complex element is structured and to what element the tag is attached to?

    With regards,

      Jan

  • I have modified the code as follows:

    if (tagElement.SetTagValue(newValue) == BentleyStatus.Success) { 
        try { 
            tagElement.ReplaceInModel(tagElement);
            LogDetails.AppendLine($"Updating Engineering link {tagElement.TagName} from '{oldValue}' to '{newValue}'");
        } catch (DgnPlatformNETException) {
            LogDetails.AppendLine($"Failed to save updated Engineering link {tagElement.TagName} from '{oldValue}' to '{newValue}'");
        }
    } else {
        LogDetails.AppendLine($"Failed to updated Engineering link {tagElement.TagName} from '{oldValue}' to '{newValue}'");
    }

    It is now throwing a generic exception originating from StatusHandler.HandelStatus(num) as num is a non zero value (BentleyStatus.Error, 0x8000). I suspect that it is due to it being a component of a complex element as the Model should be read/write.

    So where to from here?

    Which managed .NET code example shows how to modify a component of a complex element and which elements constitute the complex element in the case?

  • I was obviously miss lead by the following documentation

    Remarks

    If toReplace is null or equal to this , the current state of the element replaces the persistent state in the model.

    If toReplace is not currently a persistent element, then ReplaceInModel has the same effect as calling AddInModel.

    If toReplace is from a different model, ReplaceInModel does whatever is necessary to use this element in the target model.

    If the toReplace Element is not this or null , that Element becomes a nonpersistent element.

    ReplaceInModel throws an exception if the original element is in a read-only model or is a component of a complex element.