EvotecIT / OfficeIMO

Fast and easy to use cross-platform .NET library that creates or modifies Microsoft Word (DocX) and later also Excel (XLSX) files without installing any software. Library is based on Open XML SDK
MIT License
280 stars 50 forks source link

ContentControl #80

Open hxhgxy opened 1 year ago

hxhgxy commented 1 year ago

How can I insert ContentControl or input text into an existing contentcontrol?

Thanks a lot!

PrzemyslawKlys commented 1 year ago

At the moment we don't support ContentControl (I even didn't knew it existed).

Some docs fo future implementation:

If someone/you want to tackle this it would be great :-) If not, probably if I will be bored enough I'll take a look what Content Control is and will decide.

Quick look for a custom form based on:

Added a single element, and it looks like this

image

using DocumentFormat.OpenXml.Wordprocessing;
using DocumentFormat.OpenXml;

namespace GeneratedCode
{
    public class GeneratedClass
    {
        // Creates an SdtBlock instance and adds its children.
        public SdtBlock GenerateSdtBlock()
        {
            SdtBlock sdtBlock1 = new SdtBlock();

            SdtProperties sdtProperties1 = new SdtProperties();
            SdtId sdtId1 = new SdtId(){ Val = 734359063 };

            SdtPlaceholder sdtPlaceholder1 = new SdtPlaceholder();
            DocPartReference docPartReference1 = new DocPartReference(){ Val = "DefaultPlaceholder_-1854013440" };

            sdtPlaceholder1.Append(docPartReference1);
            SdtContentText sdtContentText1 = new SdtContentText();

            sdtProperties1.Append(sdtId1);
            sdtProperties1.Append(sdtPlaceholder1);
            sdtProperties1.Append(sdtContentText1);

            SdtContentBlock sdtContentBlock1 = new SdtContentBlock();

            Paragraph paragraph1 = new Paragraph(){ RsidParagraphAddition = "00A7755C", RsidRunAdditionDefault = "00A7755C", ParagraphId = "21E9D7DC", TextId = "7A60725A" };

            Run run1 = new Run();
            Text text1 = new Text();
            text1.Text = "Test";

            run1.Append(text1);

            paragraph1.Append(run1);

            sdtContentBlock1.Append(paragraph1);

            sdtBlock1.Append(sdtProperties1);
            sdtBlock1.Append(sdtContentBlock1);
            return sdtBlock1;
        }

    }
}

It's similar to Watermark and a few other elements we already kind of support.

I assume this is Content Control you mean right?

image

Test Controls Diff.docx

image

hxhgxy commented 1 year ago

Here is the way i figure out to get all contentcontrol (richtextbox) in a document and find one whose tag is what I am looking for:

            /// <summary>
    /// 查找并返回指定Word文档里的所有内容控件集合
    /// </summary>
    /// <param name="document">指定文档</param>
    /// <returns></returns>
    public List<SdtElement> ContentControls(WordprocessingDocument document)
    {
        List<SdtElement> contentControls = new List<SdtElement>();
        var allElements = document.MainDocumentPart.Document.Body.ChildElements;
        foreach (var element in allElements)
        {
            if (element is SdtElement)
            {
                contentControls.Add((SdtElement)element);
            }
            else
            {
                getChildren(element, contentControls);
            }
        }
        return contentControls;
    }

    /// <summary>
    /// 查找指定元素的所有层级的子元素
    /// </summary>
    /// <param name="element">指定的元素</param>
    /// <param name="contentControls">符合要求的元素的集合</param>
    private void getChildren(OpenXmlElement element, List<SdtElement> contentControls)
    {
        if (element.HasChildren == true)
        {
            foreach (OpenXmlElement child in element.ChildElements)
            {
                if (child is SdtElement)
                {
                    contentControls.Add((SdtElement)child);
                }
                else
                {
                    getChildren(child, contentControls);
                }
            }
        }
    }

    /// <summary>
    /// 从内容控件集合里找到指定Tag的内容控件
    /// </summary>
    /// <param name="contentControls">内容控件集合</param>
    /// <param name="tagName">指定的Tag</param>
    /// <returns></returns>
    public SdtElement ContentControl(List<SdtElement> contentControls, string tagName)
    {
        foreach (SdtElement item in contentControls)
        {
            var tag = item.SdtProperties?.GetFirstChild<Tag>();
            if (tag != null && tag.Val.HasValue)
            {
                if (tag.Val == tagName)
                {
                    return item;
                }
            }
        }
        return null;
    }
hxhgxy commented 1 year ago

By the way, by using your codes or the codes from OpenXML tools, it is quite easy to create a document. But i didn't figure out how to open an existing document to find some parts to edit.

I made a word template (.docx) in a folder. Everytime, I open the document and then save as a new file. i want to make edition on the new one. however, if i open the original one with iseditable=true, all revision will be saved to the original one.

I tried to open the original document with no editable, and save as a new one. when I try to open the new document right after, it is occupied.

What should i do?

Thanks.

PrzemyslawKlys commented 1 year ago

It's a bug I think:

It seems to be a bug in the Open-XML-SDK. I guess workaround would be to copy document before opening it up, and use Load that way. It's a different issue, so lets no hijack this thread here.