Bijington Bijington - 18 days ago 6
C# Question

WPF printing/xps issue

I have written the following chunk of code that prints my ListBox perfectly when being sent to a physical printer, however when trying to send it to the XPS printer driver or using the XpsDocumentWriter class (I assume they use the same code under the hood) I receive the following exception:

System.ArgumentException was unhandled by user code
Message=Width and Height must be non-negative.
Source=ReachFramework
StackTrace:
at System.Windows.Xps.Serialization.VisualSerializer.WriteTileBrush(String element, TileBrush brush, Rect bounds)

The exception obviously points to an item not having a correct width/height however I have debugged the code when sending it to the different printers (physical and XPS driver) and I haven't been able to find any differences.

Below is how I create the visual to send to the printer:

private ScrollViewer GeneratePrintableView()
{
ScrollViewer scrollView = new ScrollViewer();

Grid grid = new Grid { Background = Brushes.White, Width = this.myListBox.ActualWidth, Height = this.myListBox.ActualHeight };

grid.RowDefinitions.Add(new RowDefinition());
grid.RowDefinitions[0].Height = new GridLength(0, GridUnitType.Auto);
grid.RowDefinitions.Add(new RowDefinition());
grid.RowDefinitions[1].Height = new GridLength(0, GridUnitType.Auto);

// Add the title and icon to the top
VisualBrush titleClone = new VisualBrush(this.TitleBar);
var titleRectangle = new Rectangle { Fill = titleClone, Width = this.TitleBar.ActualWidth, Height = this.TitleBar.ActualHeight };
grid.Children.Add(titleRectangle);
Grid.SetRow(titleRectangle, 0);

this.myListBox.Width = this.myListBox.ActualWidth;
this.myListBox.Height = this.myListBox.ActualHeight;

VisualBrush clone = new VisualBrush(this.myListBox) { Stretch = Stretch.None, AutoLayoutContent = true };
var rectangle = new Rectangle { Fill = clone, Width = this.myListBox.ActualWidth, Height = this.myListBox.ActualHeight };
Border border = new Border { Background = Brushes.White, Width = this.myListBox.ActualWidth, Height = this.myListBox.ActualHeight };
border.Child = rectangle;
grid.Children.Add(border);
Grid.SetRow(border, 1);

scrollView.Width = this.myListBox.ActualWidth;
scrollView.Height = this.myListBox.ActualHeight;
scrollView.Content = grid;
scrollView.VerticalScrollBarVisibility = ScrollBarVisibility.Hidden;

return scrollView;
}


Here is the GetPage override in my DocumentPaginator implementation:

public override DocumentPage GetPage(int pageNumber)
{
Page page = new Page();
double z = 0.0;

this.grid = new Grid();
this.grid.RowDefinitions.Add(new RowDefinition());
this.grid.RowDefinitions[0].Height = new GridLength(0, GridUnitType.Auto);

this.grid.Children.Add(this.printViewer);
Grid.SetRow(this.printViewer, 0);

//Adjusting the vertical scroll offset depending on the page number
if (pageNumber + 1 == 1) //if First Page
{
this.printViewer.ScrollToVerticalOffset(0);
this.printViewer.UpdateLayout();
}
else if (pageNumber + 1 == _verticalPageCount) //if Last Page
{
if (this.printViewer.ScrollableHeight == 0) //If printing only single page and the contents fits only on one page
{
this.printViewer.ScrollToVerticalOffset(0);
this.printViewer.UpdateLayout();

}
else if (this.printViewer.ScrollableHeight <= this.printViewer.Height) //If scrollconentheight is less or equal than scrollheight
{
this.printViewer.Height = this.printViewer.ScrollableHeight;
this.printViewer.ScrollToEnd();
this.printViewer.UpdateLayout();
}
else //if the scrollcontentheight is greater than scrollheight then set the scrollviewer height to be the remainder between scrollcontentheight and scrollheight
{
this.printViewer.Height = (this.printViewer.ScrollableHeight % this.printViewer.Height) + 5;
this.printViewer.ScrollToEnd();
this.printViewer.UpdateLayout();
}
}
else //Other Pages
{
z = z + this.printViewer.Height;
this.printViewer.ScrollToVerticalOffset(z);
this.printViewer.UpdateLayout();
}

page.Content = this.grid; //put the grid into the page
page.Arrange(new Rect(this.originalMargin.Left, this.originalMargin.Top, this.ContentSize.Width, this.ContentSize.Height));
page.UpdateLayout();

return new DocumentPage(page);
}


Interestingly if I change the Fill of rectangle to a Brush instead of clone then I do not receive the exception and the outputted file is the correct size.

I have spent over a day trying to debug why this isn't working and I am hoping that someone out there has either seen a similar issue or is able to point out any mistakes I am making.

Thanks for any responses.

Answer

I was unable to rectify the problem however using this link Paginated printing of WPF visuals I was able to find a suitable solution to allow printing of complicated visuals within my WPF application.