Printing in WPF with XPS/PrintVisual

Oystein Bjorke hace 10 años 0
This discussion was imported from CodePlex

bitmatic wrote at 2014-02-15 20:10:

I am trying to print an OxyPlot chart in WPF.

I have made a UserControl containing the charts and a few other controls, and it looks perfectly well in the xaml designer.

When i try to print it via XPS and PrintVisual the charts do not show up on the print (the rest of the controls do).

Have any of you had any experience printing OxyPlot's from WPF, and how did you do it ??

objo wrote at 2014-02-16 14:34:

Did you try the OxyPlot.Xps library? I have not checked this recently, but it should be possible to create xps and print from the XpsExporter class

bitmatic wrote at 2014-03-15 12:28:

I found a solution. I had to call UpdateLayout() before doing the actual printing.

So. If you have a UserControl (mine is called DefaultPrintControl) with some OxyPlot's and some other stuff on them, you can print it like this:
private void ButtonPrint_OnClick(object sender, RoutedEventArgs e)
        {
            var dialog = new PrintDialog();
            if (dialog.ShowDialog() != true)
            { return; }

            var dpc = new DefaultPrintControl();

            dpc.Measure(new Size(dialog.PrintableAreaWidth, dialog.PrintableAreaHeight));
            dpc.Arrange(new Rect(new Point(0, 0), dpc.DesiredSize));
            dpc.UpdateLayout();

            dialog.PrintVisual(dpc, "some description of the print job");
        }

GuruK wrote at 2014-07-07 16:39:

Hi,

I also try to print graph as same way. But I couldn't it even if I call UpdateLayout().
When I create instance of user control on memory empty page is printed out.
But I throw showing visual object to PrintVisual() it's no problem.

Should I set any special configuration to user control(XAML/code behind)?
In my case, user control is very simple as following.
<UserControl
    x:Class="Reports.TestView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
    mc:Ignorable="d" 
    xmlns:oxy="http://oxyplot.codeplex.com"
    d:DesignHeight="800" d:DesignWidth="600"
    >

    <Grid>
        <oxy:Plot>
            <oxy:Plot.Axes>
                <oxy:LinearAxis Position="Left" Minimum="0" Maximum="100"/>
                <oxy:TimeSpanAxis Position="Bottom" Minimum="0" Maximum="86400" StringFormat="hh:mm"/>
            </oxy:Plot.Axes>
            <oxy:Plot.Series>
                <oxy:LineSeries ItemsSource="{Binding Path=DataList}" DataFieldX="Time" DataFieldY="Value" Color="Red"/>
            </oxy:Plot.Series>
        </oxy:Plot>
    </Grid>
</UserControl>

bitmatic wrote at 2014-07-08 10:38:

If you are using the latest version of OxyPlot it wont work. I updated to the latest version some time ago, and the above code stopped working.

I downgraded to version "2014.1.231.1" and it works with that version, so they must have changed something in the underlying rendering code....

GuruK wrote at 2014-07-08 16:08:

Thank you for your great information!
When I try to use 2014.1.231.1 from NuGet it works fine.

I checked changing point roughly. Cause is probably in InvalidatePlot() (Plot.cs or PlotView.cs).
From following version of source code, it has not rendered if it's not receive Loaded event.

revision : 824 / change set : ffbabf441bcea4e2871c8c86fc56dbe910b3f87f [ffbabf441bce]

This modification is for performance issue.

So... I added uncool code as following it seems be working on latest version of OxyPlot.
"graph" is name of Plot object (<oxy:Plot x:Name="graph">)
    dpc.Measure(new Size(dialog.PrintableAreaWidth, dialog.PrintableAreaHeight));
    dpc.Arrange(new Rect(new Point(0, 0), dpc.DesiredSize));
    dpc.graph.RaiseEvent(new System.Windows.RoutedEventArgs(OxyPlot.Wpf.PlotView.LoadedEvent));   
    dpc.UpdateLayout();
or
    dpc.Measure(new Size(dialog.PrintableAreaWidth, dialog.PrintableAreaHeight));
    dpc.Arrange(new Rect(new Point(0, 0), dpc.DesiredSize));
    dpc.graph.IsRendering = true;
    dpc.graph.InvalidatePlot(true);
    dpc.graph.IsRendering = false;
    dpc.UpdateLayout();
Of course this is not correct way... But currently it may be only way.
It should be modified or extended for our case.

bitmatic wrote at 2014-07-09 13:37:

Thanks GuruK. Your hack works :-)

objo wrote at 2014-07-10 13:17:

Have you seen the XpsExporter in the OxyPlot.Xps package? Can this be used? Let us know if there are bugs!

bitmatic wrote at 2014-07-10 13:52:

I looked at the XpsExporter some time ago, but couldn't really use it. It prints the actual OxyPlot - but what i need is much more complex.

What I (and GuruK apparently) need is more complex prints with both OxyPlots and other controls, such as textboxes, images, etc... So we make a UserControl with all the stuff we need to print out, and print it with the above code.

This worked fine until the latest update of the OxyPlot rendering code :-(

OxyPlot is the only control i have come across that can not be printed properly with this method. This could be a bug in OxyPlot(?) When you call Measure, Arrange & UpdateLayout on a control it is supposed to render - OxyPlot doesn't.

objo wrote at 2014-07-10 14:03:

Thanks for the explanation, now I understand. This should be corrected, I guess this also applies when rendering a UserControl to a bitmap?
I have added https://oxyplot.codeplex.com/workitem/10231