2012-03-26 33 views
8

Tôi đang cố gắng xóa đoạn văn bản (Tôi đang sử dụng một số văn bản giữ chỗ để tạo thế hệ từ tệp giống như mẫu docx) từ tệp .docx bằng OpenXML, nhưng bất cứ khi nào tôi xóa đoạn, nó phá vỡ vòng lặp foreach Tôi đang sử dụng để lặp lại máng.C# openxml remove paragraph

MainDocumentPart mainpart = doc.MainDocumentPart; 
IEnumerable<OpenXmlElement> elems = mainPart.Document.Body.Descendants(); 

foreach(OpenXmlElement elem in elems){ 
    if(elem is Text && elem.InnerText == "##MY_PLACE_HOLDER##") 
    { 
     Run run = (Run)elem.Parent; 
     Paragraph p = (Paragraph)run.Parent; 
     p.RemoveAllChildren(); 
     p.Remove(); 
    } 
} 

này hoạt động, loại bỏ giữ chỗ tôi và đoạn đó là trong, nhưng vòng lặp foreach ngừng lặp. Và tôi cần nhiều thứ hơn để làm trong vòng lặp foreach của tôi.

Đây có phải là ok cách để loại bỏ đoạn trong C# sử dụng OpenXML và tại sao foreach vòng lặp dừng của tôi hay làm thế nào để làm cho nó không dừng lại? Cảm ơn.

Trả lời

10

Đây là "Halloween vấn đề", gọi như vậy vì nó được chú ý bởi một số nhà phát triển trên Halloween, và nó trông ghê rợn đối với họ. Đó là vấn đề của việc sử dụng mã khai báo (truy vấn) với mã bắt buộc (xóa các nút) cùng một lúc. Nếu bạn suy nghĩ về nó, bạn đang lặp đi lặp lại mặc dù một danh sách liên kết, và nếu bạn bắt đầu xóa các nút trong danh sách liên kết, bạn hoàn toàn mess up the iterator. Cách đơn giản hơn để tránh vấn đề này là "hiện thực hóa" kết quả của truy vấn trong Danh sách, và sau đó bạn có thể lặp qua danh sách và xóa các nút theo ý muốn. Sự khác biệt duy nhất trong đoạn mã sau là nó gọi ToList sau khi gọi trục Descendants.

MainDocumentPart mainpart = doc.MainDocumentPart; 
IEnumerable<OpenXmlElement> elems = mainPart.Document.Body.Descendants().ToList(); 

foreach(OpenXmlElement elem in elems){ 
    if(elem is Text && elem.InnerText == "##MY_PLACE_HOLDER##") 
    { 
     Run run = (Run)elem.Parent; 
     Paragraph p = (Paragraph)run.Parent; 
     p.RemoveAllChildren(); 
     p.Remove(); 
    } 
} 

Tuy nhiên, tôi phải lưu ý rằng tôi thấy một lỗi khác trong mã của bạn. Không có gì để ngăn chặn Word tách nút văn bản đó thành nhiều phần tử văn bản từ nhiều lần chạy. Mặc dù trong hầu hết các trường hợp, mã của bạn sẽ hoạt động tốt, sớm hay muộn, bạn hoặc người dùng sẽ thực hiện một số hành động (như chọn ký tự và vô tình nhấn nút in đậm trên ruy-băng) và sau đó mã của bạn sẽ không hoạt động nữa.

Nếu bạn thực sự muốn làm việc ở cấp văn bản, thì bạn cần phải sử dụng mã như những gì tôi giới thiệu trong này màn hình-cast: http://openxmldeveloper.org/blog/b/openxmldeveloper/archive/2011/08/04/introducing-textreplacer-a-new-class-for-powertools-for-open-xml.aspx

Trong thực tế, bạn có thể có thể sử dụng mã đúng nguyên văn để xử lý của bạn trường hợp sử dụng, tôi tin.

cách tiếp cận khác, linh hoạt hơn và mạnh mẽ, được nêu chi tiết trong:

http://openxmldeveloper.org/blog/b/openxmldeveloper/archive/2011/06/13/open-xml-presentation-generation-using-a-template-presentation.aspx

Trong khi đó màn hình-cast khoảng PresentationML, cùng một nguyên tắc áp dụng cho WordprocessingML.

Nhưng thậm chí tốt hơn, với điều kiện bạn đang sử dụng WordprocessingML, là sử dụng các điều khiển nội dung.Đối với một cách tiếp cận tài liệu thế hệ, xem:

http://ericwhite.com/blog/map/generating-open-xml-wordprocessingml-documents-blog-post-series/

Và đối với rất nhiều thông tin về việc sử dụng các điều khiển nội dung nói chung, xem:

http://www.ericwhite.com/blog/content-controls-expanded

-Eric

+0

Thực ra tôi đã thực hiện .ToList(), bởi vì một số biến chứng khác xuất hiện bằng cách sử dụng trước đó dung dịch. Ngoài ra, tôi biết từ tách nó thành nhiều lần chạy (đây, đây là ví dụ xấu), vì vậy trình giữ chỗ của tôi không có '_'. Và trình giữ chỗ của tôi được mã hóa cứng, vì vậy mặc dù tôi biết về các lợi thế của Kiểm soát nội dung, tôi không sử dụng chúng vì tôi không biết chúng đủ tốt và có lịch trình dự án ngắn (nhỏ). Cảm ơn câu trả lời, nó rất sâu sắc, đầy đủ hơn. –

1

Trước tiên, bạn phải sử dụng hai chu kỳ lưu trữ các mục bạn muốn xóa và thứ hai xóa các mục. một cái gì đó như thế này:

List<Paragraph> paragraphsToDelete = new List<Paragraph>(); 
foreach(OpenXmlElement elem in elems){ 
    if(elem is Text && elem.InnerText == "##MY_PLACE_HOLDER##") 
    { 
     Run run = (Run)elem.Parent; 
     Paragraph p = (Paragraph)run.Parent; 
     paragraphsToDelete.Add(p); 
    } 
} 

foreach (var p in paragraphsToDelete) 
{ 
     p.RemoveAllChildren(); 
     p.Remove(); 
} 
+1

Thiên Chúa , Tôi thật ngu ngốc. Cảm ơn. Nhưng tại sao địa ngục nó bị phá vỡ từ vòng lặp ở nơi đầu tiên? (nếu ai đó biết, vì vậy tôi sẽ để nó một thời gian để chấp nhận câu trả lời; sry không thể bỏ phiếu, đại diện quá thấp) –

+0

http://stackoverflow.com/questions/2545027/exception-during-iteration-on-collection-and- remove-items-from-that-collection –

+0

Cảm ơn. Tìm thấy một tốt nhất: http://stackoverflow.com/questions/604831/collection-was-modified-enumeration-operation-may-not-execute –

0
Dim elems As IEnumerable(Of OpenXmlElement) = MainPart.Document.Body.Descendants().ToList() 
     For Each elem As OpenXmlElement In elems 
      If elem.InnerText.IndexOf("fullname") > 0 Then 
       elem.RemoveAllChildren() 
      End If 

     Next