2010-02-26 23 views
10

Tôi muốn tạo (và sau đó in hoặc lưu) các tài liệu XPS lớn (> 400 trang) từ ứng dụng WPF của tôi. Chúng tôi có một số lượng lớn dữ liệu trong bộ nhớ cần được ghi vào XPS.Làm thế nào để tạo và in các tài liệu XPS lớn trong WPF?

Làm cách nào để thực hiện điều này mà không cần nhận được OutOfMemoryException? Có cách nào tôi có thể viết tài liệu theo khối không? Việc này thường được thực hiện như thế nào? Tôi có nên không sử dụng XPS cho các tệp lớn ngay từ đầu không?

Nguyên nhân gốc của OutOfMemoryException dường như là việc tạo ra FlowDocument lớn. Tôi đang tạo đầy đủ FlowDocument và sau đó gửi nó đến nhà văn tài liệu XPS. Đây có phải là cách tiếp cận sai?

+0

> The OutOfMemeoryExcpetion có nhiều khả năng gây ra bởi ... Nói cách khác, bạn chưa thực sự kiểm tra. Chúng tôi cần thêm thông tin để cho bạn biết vấn đề là gì. –

+0

@bitbonk Tôi hiện đang gặp sự cố tương tự. Bạn đã bao giờ tìm thấy một giải pháp? –

Trả lời

3

Tôi có thể xác nhận rằng XPS thực hiện không ném ra khỏi bộ nhớ trên tài liệu dài. Cả về lý thuyết (vì các hoạt động trên XPS dựa trên trang, nó không cố gắng tải toàn bộ tài liệu trong bộ nhớ), và trong thực tế (tôi sử dụng báo cáo dựa trên XPS, và thấy các thông báo lỗi chạy thêm lên tới hàng nghìn trang).

Có thể vấn đề nằm trong một trang đặc biệt lớn không? Một hình ảnh lớn, ví dụ? Trang lớn có độ phân giải DPI cao? Nếu một đối tượng trong tài liệu quá lớn để được phân bổ cùng một lúc, nó sẽ dẫn đến ngoại lệ ngoài bộ nhớ.

+0

Tôi phải thiết lập các trang như thế nào? Tôi nghĩ rằng người điều hành làm điều đó cho tôi. – bitbonk

+0

Chúng tôi đã không chỉ định kích thước trang có thể là gốc của tất cả các điều xấu ... – bitbonk

+0

Tôi đã không thực hiện bất kỳ biện pháp đặc biệt nào, chỉ cần làm theo các ví dụ. Giống như những người khác đã lưu ý, bạn cung cấp thông tin quá ít. Phải có điều gì đó không bình thường về mã hoặc hệ thống của bạn, nhưng làm cách nào chúng tôi có thể đoán được nó là gì - không có ngăn xếp cuộc gọi hoặc đoạn mã? – ima

5

Bạn làm như thế nào? Bạn không hiển thị bất kỳ mã nào.

tôi sử dụng một XpsDocumentWriter viết trong khối, như thế này:

FlowDocument flowDocument = . .. ..; 

// write the XPS document 
using (XpsDocument doc = new XpsDocument(fileName, FileAccess.ReadWrite)) 
{ 
    XpsDocumentWriter writer = XpsDocument.CreateXpsDocumentWriter(doc); 
    DocumentPaginator paginator = ((IDocumentPaginatorSource)flowDocument).DocumentPaginator; 

    // Change the PageSize and PagePadding for the document 
    // to match the CanvasSize for the printer device. 
    paginator.PageSize = new Size(816, 1056); 
    copy.PagePadding = new Thickness(72); 
    copy.ColumnWidth = double.PositiveInfinity; 
    writer.Write(paginator); 
} 

Điều này không làm việc cho bạn?

+0

Đâu là khối. Bạn có nghĩa là ví dụ này có khả năng viết nhiều tài liệu XPS vào cùng một flowDocument không? Wehn tôi tái sử dụng cùng một paginator tất cả các tài liệu XPS họ sẽ kết thúc trong cùng một tập tin XPS hoặc trong cùng một tài liệu in? – bitbonk

+0

Các khối: bạn có thể sử dụng writer.Write() trong một vòng lặp. Ngay cả khi không có điều đó, tôi sẽ đoán rằng writer.Write() sử dụng một cách tiếp cận được truyền trực tiếp trên trình paginator, và không có gì cần phải được tích lũy vào bộ nhớ. Tôi không biết ý bạn là gì bằng cách "sử dụng cùng một người điều hành." bạn cần hiển thị mã của bạn. – Cheeso

+0

Điều này sẽ cho tôi tôi sẽ cần phải tạo ra nhiều trường hợp FlowDocument, chính xác? Vì trình paginator xuất phát từ cá thể FlowDocument. – bitbonk

0

Bạn đã sử dụng sos để tìm hiểu những gì đang sử dụng hết bộ nhớ?

Có thể các đối tượng được quản lý hoặc không được quản lý đang được tạo trong quá trình sản xuất tài liệu của bạn và chúng sẽ không được phát hành cho đến khi tài liệu được hoàn thành (hoặc không hoàn toàn).

Tracking down managed memory leaks bởi Rico Mariani có thể trợ giúp.

+0

OutOfMemeoryExcpetion rất có thể gây ra bằng cách tạo FlowDocument khổng lồ trong bộ nhớ. Làm cách nào để tạo XPS khi tôi không thể tạo FixedDocument cho nó? – bitbonk

+0

Nếu FlowDocument của bạn không nắm giữ nhiều đối tượng hơn mức cần thiết, thì có lẽ bạn có thể tạo DocumentPaginator tùy chỉnh để truy vấn các trang theo yêu cầu? Dường như không có nhiều thông tin về chủ đề này. – marklam

0

giống như bạn nói: có thể FixedDocument trong bộ nhớ đang tiêu thụ quá nhiều bộ nhớ.

Có thể một cách tiếp cận mà bạn viết ra các trang XPS riêng lẻ (và đảm bảo FixedDocument được phát hành mỗi lần), và sau đó sử dụng sáp nhập sau đó có thể chứng minh hiệu quả.

Bạn có thể viết riêng từng trang không?

Nick.

ps. Hãy liên hệ trực tiếp với tôi ([email protected]); chúng tôi làm rất nhiều công cụ XPS ở NiXPS và tôi rất quan tâm đến việc giúp bạn giải quyết vấn đề này.

4

Phát biểu từ sự thiếu hiểu biết hoàn hảo của hệ thống cụ thể có liên quan, tôi có thể đề xuất sử dụng kỹ thuật gỡ lỗi Wolf Fence trong kỹ thuật gỡ lỗi Alaska để xác định nguồn gốc của vấn đề không? Tôi đang đề xuất điều này vì những người phản hồi khác không báo cáo cùng một vấn đề mà bạn đang gặp phải. Khi làm việc với các lỗi dễ sinh sản, Wolf Fence chết đơn giản để làm (nó không hoạt động tốt với các điều kiện chủng tộc và tương tự).

  1. Chọn điểm giữa trong dữ liệu đầu vào của bạn và cố gắng tạo tài liệu đầu ra của bạn từ chỉ dữ liệu đó, không còn nữa.
  2. Nếu thành công, hãy chọn một điểm khoảng 75% vào đầu vào và thử lại, nếu không chọn một điểm khoảng 25% đường vào đầu vào và thử lại.
  3. Thu thập, rửa sạch, lặp lại, mỗi lần thu hẹp cửa sổ xuống vị trí của đường dây công trình/lỗi.
  4. Bạn có thể thấy rằng bạn nhanh chóng xác định một trang cụ thể - hoặc có lẽ một đối tượng cụ thể trên trang đó - đó là "vui nhộn". Lưu ý: bạn chỉ phải thực hiện log2 (N) lần này, hoặc trong trường hợp này 9 lần cho 400 trang bạn đề cập.

Bây giờ bạn có thể có thứ gì đó bạn có thể tấn công trực tiếp. Chúc may mắn.

4

Bạn không thể sử dụng một đơn FlowDocument để tạo tài liệu lớn vì bạn sẽ hết bộ nhớ. Tuy nhiên, nếu có thể tạo đầu ra của bạn dưới dạng một chuỗi là FlowDocument hoặc là cực kỳ cao ItemsControl, điều đó là có thể.

tôi đã tìm thấy cách dễ nhất để làm điều này là để phân lớp DocumentPaginator và vượt qua một thể hiện của lớp con tôi để XpsDocumentWriter.Write:

var document = new XpsDocument(...); 
var writer = XpsDocument.CreateXpsDocumentWriter(xpsDocument); 
writer.Write(new WidgetPaginator { Widget = widgetBeingPrinted, PageSize = ... }); 

Lớp WidgetPaginator chính nó là khá đơn giản:

class WidgetPaginator : DocumentPaginator, IDocumentPaginatorSource 
{ 
    Size _pageSize; 

    public Widget Widget { get; set; } 

    public override Size PageSize { get { return _pageSize; } set { _pageSize = value; } } 
    public override bool IsPageCountValid { return true; } 
    public override IDocumentPaginatorSource Source { return this; } 
    public override DocumentPaginator DocumentPaginator { return this; } 
    public override int PageCount 
    { 
    get 
    { 
     return ...; // Compute page count 
    } 
    } 
    public override DocumentPage GetPaget(int pageNumber) 
    { 
    var visual = ...; // Compute page visual 

    Rect box = new Rect(0,0,_pageSize.With, _pageSize.Height); 
    return new DocumentPage(visual, _pageSize, box, box); 
    } 

Tất nhiên bạn vẫn phải viết mã thực sự tạo ra các trang.

Nếu bạn muốn sử dụng một loạt các FlowDocuments để tạo ra tài liệu của bạn

Nếu bạn đang sử dụng một chuỗi các FlowDocuments đẻ ra tài liệu một phần của bạn tại một thời điểm thay vì tất cả cùng một lúc, tùy chỉnh của bạn người điều hành có thể làm việc theo hai lần:

  • Lần đầu tiên xảy ra khi người tạo trang được xây dựng. Nó tạo ra một FlowDocument cho mỗi phần, sau đó nhận được DocumentPaginator để truy xuất số trang. Mỗi phần của FlowDocument bị hủy sau khi các trang được tính.
  • Lần truy cập thứ hai xảy ra trong quá trình xuất tài liệu thực tế: Nếu số được chuyển đến GetPage() là trong FlowDocument được tạo gần đây nhất, GetPage() chỉ cần gọi trình thu thập tài liệu đó để nhận trang thích hợp. Nếu không, nó sẽ loại bỏ FlowDocument đó và tạo ra một FlowDocument cho phần mới, lấy người tạo trang của nó, sau đó gọi số GetPage() trên trình tạo trang.

Chiến lược này cho phép bạn tiếp tục sử dụng FlowDocuments như bạn đã từng, miễn là bạn có thể chia dữ liệu thành "phần" với từng tài liệu riêng. Trình tùy chỉnh của bạn sau đó xử lý hiệu quả tất cả các FlowDocuments riêng lẻ dưới dạng một tài liệu lớn. Điều này tương tự với tính năng "Tài liệu chính" của Word.

Nếu bạn có thể làm cho dữ liệu của bạn như một chuỗi các hình ảnh theo chiều dọc xếp chồng lên nhau

Trong trường hợp này, các kỹ thuật tương tự có thể được sử dụng.Trong lần vượt qua đầu tiên, tất cả hình ảnh được tạo theo thứ tự và được đo để xem số lượng hình ảnh sẽ phù hợp trên một trang. Cấu trúc dữ liệu được xây dựng để cho biết phạm vi của hình ảnh (theo chỉ mục hoặc bất kỳ thứ gì) được tìm thấy trên một trang nhất định. Trong quá trình này mỗi lần trang được lấp đầy, hình ảnh tiếp theo được đặt trên một trang mới. Đầu trang và chân trang sẽ được xử lý theo cách hiển nhiên.

Trong quá trình tạo tài liệu thực tế, phương pháp GetPage() được triển khai để tạo lại hình ảnh đã được quyết định trước đó trên một trang nhất định và kết hợp chúng bằng DockPanel dọc hoặc bảng điều khiển khác do bạn chọn.

Tôi đã tìm thấy kỹ thuật này linh hoạt hơn về lâu dài vì bạn không phải giải quyết các giới hạn của FlowDocument.

Các vấn đề liên quan