ASK Field and Rendered content

We recently upgraded to the latest version of ASPOSE for Word. Subsequently ASK fields seem to behave differently. Formerly when this code ran:

FieldsHelper.ConvertFieldsToStaticText(doc, FieldType.FieldAsk);

It would remove the ASK field (and input value) from the output stream. However, since the upgrade the following occurs:

{ASK NUM1 “Pick a number 1, 2, 3” /d “1” \o}

You picked {REF NUM1}.

RENDERS AS:

1

You picked 1.

Whereas before it RENDERED AS:

You picked 1.

Is this the expected behavior for the latest version or is there something I can do to “hide” the ASK value?

Thanks so much for any help!

Hi Linda,
Thanks for your query. Would you please share some sample document/code. So we will take a closer look into the issue and suggest you solution accordingly.
Regards,

Thank you for any advice!

One more file I missed!

Hi Linda,
Thanks’ for sharing additional information. The issue you highlighted is default behavior of ConvertFieldsToStaticText(() method. As the process of converting fields to static text involves extracting the field result and retaining this value while removing the field objects around it i.e. FiledStart, Field code, FieldSeparator and FieldEnd. Would you please mention the old version, in which ASK field was behaving differently. So we have a closer look into the issue and suggestion you accordingly.
Sorry for the inconvenience faced.
Best Regards

Hi,

Sorry to reopen an old case, however I’ve encountered the same undesirable result. With ConvertFieldsToStaticText(), Aspose Words is replacing the ASK field with the value of the result. I don’t think this is the correct way to unlink the and remove the field.

As is Linda’s OP, The ASK field has 2 components, the question (with prompt/default) and the Bookmark where the answer is stored and placed in the document with REF.

Generally speaking, when converting to static text, the answer should not be in the ASK field. MS Word does not show the answer there. It should only be shown in the REF fields that point to the bookmarks.

I think there should have a parameter on whether to add the value or just remove the field.

Alternatively, how can we modify the Document Visitor so that it replaces the node with empty text instead of the stored value for the ASK field?

@GaryO You are right the topic is very old. Currently Aspose.Words has a built-in method to unlink fields in the document. Please see Document.UnlinkFields method. Also, you can unlink individual fields in the document using Field.Unlink method. Also, you can simply remove the field if it is required to replace it with empty string, see Field.Remove.

I understand that, but if i call ConvertFieldsToStaticText() as I want to flatten it, then it will replace the ASK with the value (unwanted behaviour) unless I have removed the field before.

If I change the ConvertFieldsToStaticText() DocumentVisitor to skip ASK, then the ASK remains. So how to modify and get the ConvertFieldsToStaticText() and DocumentVisitor to replace the field with an empty string?

If you look in Word, it never prints the ASK value where the ASK field is. Only in the REF field associated with the Bookmark linked to the ASK.

As per MS Word documentation (Field codes: Ask field):

The Ask field code causes Word to prompt you to enter information, which it then assigns to a named variable, called a bookmark.

To display the response in the content of the document, you must insert a Ref field after the Ask field.

The ASK should never show the value of the field when converted to static text.

@GaryO I do not have sources of FieldsHelper.ConvertFieldsToStaticText. Why don’t you simply use the built-in methods to unlink the fields?

We already have custom code in the DocumentVisitor for flattening checkboxes and dropdowns as the unlink was sub optimal visually.

I can add more custom code for the ASK, but I don’t know the best way to change the CompositeNode in this case. Do I just change the Accept(DocumentVisitor helper) to Remove(), or do I need to do more?

@GaryO Unfortunately, without the code and sample document I cannot analyze the problem and answer the question. Also, if the goal is to unlink the fields, I would suggest you to use the built-in method to avoid further problems.

Sure, here’s the code that was originally applied from examples given years ago:

/// <summary>
/// Converts any fields of the specified type found in the descendants of the node into static text.
/// </summary>
/// <param name="compositeNode">The node in which all descendants of the specified FieldType will be converted to static text.</param>
/// <param name="targetFieldType">The FieldType of the field to convert to static text.</param>
public static void ConvertFieldsToStaticText(CompositeNode compositeNode, Aspose.Words.Fields.FieldType targetFieldType)
{
    String originalNodeText = compositeNode.ToString(Aspose.Words.SaveFormat.Text); //ExSkip
    DocumentVisitorFields helper = new DocumentVisitorFields(targetFieldType);
    compositeNode.Accept(helper);

    Debug.Assert(originalNodeText.Equals(compositeNode.ToString(Aspose.Words.SaveFormat.Text)), "Error: Text of the node converted differs from the original"); //ExSkip
    foreach (Node node in compositeNode.GetChildNodes(NodeType.Any, true)) //ExSkip
        Debug.Assert(!(node is Aspose.Words.Fields.FieldChar && ((Aspose.Words.Fields.FieldChar)node).FieldType.Equals(targetFieldType)), "Error: A field node that should be removed still remains."); //ExSkip         
}

Are you saying this code is no longer the preferred way to flatten field? Does unlink do this internally?

@GaryO Thank you for additional information. But the provided code does not include the actual DocumentVisitor implementation, which converts fields to static text.

Yes, there is no longer need in custom code to unlink fields. Aspose.Words provides built-in method to do this for you.

You mean this code?

		private DocumentVisitorFields(Aspose.Words.Fields.FieldType targetFieldType)
		{
			mTargetFieldType = targetFieldType;
		}

		public override VisitorAction VisitFieldStart(Aspose.Words.Fields.FieldStart fieldStart)
		{
			// We must keep track of the starts and ends of fields incase of any nested fields.
			if (fieldStart.FieldType.Equals(mTargetFieldType))
			{
				mFieldDepth++;
				fieldStart.Remove();
			}
			else
			{
				// This removes the field start if it's inside a field that is being converted.
				CheckDepthAndRemoveNode(fieldStart);
			}

			return VisitorAction.Continue;
		}

		public override VisitorAction VisitFieldSeparator(Aspose.Words.Fields.FieldSeparator fieldSeparator)
		{
			// When visiting a field separator we should decrease the depth level.
			if (fieldSeparator.FieldType.Equals(mTargetFieldType))
			{
				mFieldDepth--;
				fieldSeparator.Remove();
			}
			else
			{
				// This removes the field separator if it's inside a field that is being converted.
				CheckDepthAndRemoveNode(fieldSeparator);
			}

			return VisitorAction.Continue;
		}

		public override VisitorAction VisitFieldEnd(Aspose.Words.Fields.FieldEnd fieldEnd)
		{
			if (fieldEnd.FieldType.Equals(mTargetFieldType))
				fieldEnd.Remove();
			else
				CheckDepthAndRemoveNode(fieldEnd); // This removes the field end if it's inside a field that is being converted.

			return VisitorAction.Continue;
		}

		public override VisitorAction VisitRun(Run run)
		{
			// Remove the run if it is between the FieldStart and FieldSeparator of the field being converted.
			CheckDepthAndRemoveNode(run);

			return VisitorAction.Continue;
		}

		public override VisitorAction VisitParagraphEnd(Paragraph paragraph)
		{
			if (mFieldDepth > 0)
			{
				// The field code that is being converted continues onto another paragraph. We 
				// need to copy the remaining content from this paragraph onto the next paragraph.
				Node nextParagraph = paragraph.NextSibling;

				// Skip ahead to the next available paragraph.
				while (nextParagraph != null && nextParagraph.NodeType != NodeType.Paragraph)
					nextParagraph = nextParagraph.NextSibling;

				// Copy all of the nodes over. Keep a list of these nodes so we know not to remove them.
				while (paragraph.HasChildNodes)
				{
					mNodesToSkip.Add(paragraph.LastChild);
					((Paragraph)nextParagraph).PrependChild(paragraph.LastChild);
				}

				paragraph.Remove();
			}

			return VisitorAction.Continue;
		}

		public override VisitorAction VisitTableStart(Aspose.Words.Tables.Table table)
		{
			CheckDepthAndRemoveNode(table);

			return VisitorAction.Continue;
		}

		/// <summary>
		/// Checks whether the node is inside a field or should be skipped and then removes it if necessary.
		/// </summary>
		private void CheckDepthAndRemoveNode(Node node)
		{
			if (mFieldDepth > 0 && !mNodesToSkip.Contains(node))
				node.Remove();
		}

		public override VisitorAction VisitFormField(Aspose.Words.Fields.FormField formfield)
		{
			if (formfield.Type == mTargetFieldType)
				formfield.Remove();

			return VisitorAction.Continue;
		}

		private int mFieldDepth = 0;
		private ArrayList mNodesToSkip = new ArrayList();
		private Aspose.Words.Fields.FieldType mTargetFieldType;

These functions were all from Aspose examples. Is this what Aspose is now using internally for unlinking fields? What are the differences if any?

@GaryO Aspose.Words uses more advanced code to unlink fields, since internal Aspose.Words API is more advanced and complex then public API. So I would recommend to use built-in methods.
You can use the following simple code to unlink ASK fields in the document:

Document doc = new Document(@"C:\Temp\in.docx");

doc.Range.Fields.Where(f => f.Type == FieldType.FieldAsk).ToList()
    .ForEach(f => f.Unlink());
            
doc.Save(@"C:\Temp\out.docx");