问题
There are many complaints online (such as this one and this one) about the printing of a webpage in WebVeiw
where the printed page only shows the current view on the screen and not the entire page. By going through some code from online searches, I was able to print the entire page using the following code. But the printed page has tried to print it all in one page with a scrollbar enabled. Moreover, the printed content is very blurred (as shown below).
Question: How can we make the printed content not blurred while still printing the entire page with scrollbar enable?
Website used in the WebView: Wikipedia
Reference: WebView to WebViewBrush
MainPage.xaml:
<Page
x:Class="UWP_WebViewPrinting.MainPage"
....>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<WebView x:Name="wvTest" Source="https://en.wikipedia.org/wiki/Universal_Windows_Platform" Grid.Row="1" Margin="0,61,0,0"/>
<Button x:Name="btnTest" Content="Test" Grid.Row="0" VerticalAlignment="Top" Click="btnTest_Click"/>
<Rectangle x:Name="RectangleToPrint" Grid.Row="1"/>
</Grid>
</Page>
MainPage.xaml.cs:
private async void btnTest_Click(object sender, RoutedEventArgs e)
{
//Step 1: use WebViewBrush to render the content of webview into the Rectangle
int width;
int height;
// get the total width and height
var widthString = await wvTest.InvokeScriptAsync("eval", new[] { "document.body.scrollWidth.toString()" });
var heightString = await wvTest.InvokeScriptAsync("eval", new[] { "document.body.scrollHeight.toString()" });
if (!int.TryParse(widthString, out width))
{
throw new Exception("Unable to get page width");
}
if (!int.TryParse(heightString, out height))
{
throw new Exception("Unable to get page height");
}
// resize the webview to the content
wvTest.Width = width;
wvTest.Height = height;
WebViewBrush b = new WebViewBrush();
b.SourceName = "wvTest";
b.Redraw();
RectangleToPrint.Fill = b;
//Step 2: Then print the rectangle
if (PrintManager.IsSupported())
{
try
{
// Show print UI
await PrintManager.ShowPrintUIAsync();
}
catch
{
// Printing cannot proceed at this time
ContentDialog noPrintingDialog = new ContentDialog()
{
Title = "Printing error",
Content = "\nSorry, printing can' t proceed at this time.",
PrimaryButtonText = "OK"
};
await noPrintingDialog.ShowAsync();
}
}
else
{
// Printing is not supported on this device
ContentDialog noPrintingDialog = new ContentDialog()
{
Title = "Printing not supported",
Content = "\nSorry, printing is not supported on this device.",
PrimaryButtonText = "OK"
};
await noPrintingDialog.ShowAsync();
}
}
#region Register for printing
protected override void OnNavigatedTo(NavigationEventArgs e)
{
// Register for PrintTaskRequested event
printMan = PrintManager.GetForCurrentView();
printMan.PrintTaskRequested += PrintTaskRequested;
// Build a PrintDocument and register for callbacks
printDoc = new PrintDocument();
printDocSource = printDoc.DocumentSource;
printDoc.Paginate += Paginate;
printDoc.GetPreviewPage += GetPreviewPage;
printDoc.AddPages += AddPages;
}
#endregion
#region Showing the print dialog
private void PrintTaskRequested(PrintManager sender, PrintTaskRequestedEventArgs args)
{
// Create the PrintTask.
// Defines the title and delegate for PrintTaskSourceRequested
var printTask = args.Request.CreatePrintTask("Print", PrintTaskSourceRequrested);
// Handle PrintTask.Completed to catch failed print jobs
printTask.Completed += PrintTaskCompleted;
}
private void PrintTaskSourceRequrested(PrintTaskSourceRequestedArgs args)
{
// Set the document source.
args.SetSource(printDocSource);
}
#endregion
#region Print preview
private void Paginate(object sender, PaginateEventArgs e)
{
// As I only want to print one Rectangle, so I set the count to 1
printDoc.SetPreviewPageCount(1, PreviewPageCountType.Final);
}
private void GetPreviewPage(object sender, GetPreviewPageEventArgs e)
{
// Provide a UIElement as the print preview.
printDoc.SetPreviewPage(e.PageNumber, this.RectangleToPrint);
}
#endregion
#region Add pages to send to the printer
private void AddPages(object sender, AddPagesEventArgs e)
{
printDoc.AddPage(this.RectangleToPrint);
// Indicate that all of the print pages have been provided
printDoc.AddPagesComplete();
}
#endregion
#region Print task completed
private async void PrintTaskCompleted(PrintTask sender, PrintTaskCompletedEventArgs args)
{
// Notify the user when the print operation fails.
if (args.Completion == PrintTaskCompletion.Failed)
{
await CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, async () =>
{
ContentDialog noPrintingDialog = new ContentDialog()
{
Title = "Printing error",
Content = "\nSorry, failed to print.",
PrimaryButtonText = "OK"
};
await noPrintingDialog.ShowAsync();
});
}
}
#endregion
Printed PDF: Available here
UPDATE:
A response from @Faywang - MSFT
seems quite promising. I tried it as follows:
- Created a method call
PrintWebView()
with the code from theOnPrintButtonClick()
event from here - Added all the other methods/events shown in my post above.
- In my
btnTest_Click(...)
, I modified the code from user@Faywang
as follows
.
List<Rectangle> allpages = await GetWebPages(wvTest, new Windows.Foundation.Size(750d, 950d));
//print these pages
foreach (Rectangle rectangle in allpages)
PrintWebView();
- When the app runs in default mode it shows count for
allpages
to be 7 and the aboveforeach
callsPrintWebView()
7 times. If I put the app screen in max mode, the count forallpages
is 3 and theforeach
callsPrintWebView()
3 times (as expected). In both cases I was expecting the last iteration of the loop will bring the print dialog and print all pages 7 (or 3 depending on default or max mode of the app's screen). Instead, the debugger went to thecatch
block of thePrintWebView()
method whose code, as stated earlier, is taken from here - Question: Did I follow the above steps correctly? If not, what would be a better way of doing it?
回答1:
When you compress and redraw the webview to the rectangle, it will become blurred, it's better to display the webview in mutiple pages. First I give the width and height of printed page as 750, 950, and then according to the scale to cacluate how many pages it needs to be. After that, you can print these Rectangles. For example:
private async void btnTest_Click(object sender, RoutedEventArgs e)
{
allPages = await GetWebPages(wvTest, new Windows.Foundation.Size(750d, 950d));
//print these pages
}
async Task<List<Windows.UI.Xaml.Shapes.Rectangle>> GetWebPages(Windows.UI.Xaml.Controls.WebView webView, Windows.Foundation.Size page)
{
// ask the content its width
var _WidthString = await webView.InvokeScriptAsync("eval",
new[] { "document.body.scrollWidth.toString()" });
int _ContentWidth;
if (!int.TryParse(_WidthString, out _ContentWidth))
throw new Exception(string.Format("failure/width:{0}", _WidthString));
webView.Width = _ContentWidth;
// ask the content its height
var _HeightString = await webView.InvokeScriptAsync("eval",
new[] { "document.body.scrollHeight.toString()" });
int _ContentHeight;
if (!int.TryParse(_HeightString, out _ContentHeight))
throw new Exception(string.Format("failure/height:{0}", _HeightString));
webView.Height = _ContentHeight;
// how many pages will there be?
var _Scale = page.Width / _ContentWidth;
var _ScaledHeight = (_ContentHeight * _Scale);
var _PageCount = (double)_ScaledHeight / page.Height;
_PageCount = _PageCount + ((_PageCount > (int)_PageCount) ? 1 : 0);
// create the pages
var _Pages = new List<Windows.UI.Xaml.Shapes.Rectangle>();
for (int i = 0; i < (int)_PageCount; i++)
{
var _TranslateY = -page.Height * i;
var _Page = new Windows.UI.Xaml.Shapes.Rectangle
{
Height = page.Height,
Width = page.Width,
Margin = new Windows.UI.Xaml.Thickness(5),
Tag = new Windows.UI.Xaml.Media.TranslateTransform { Y = _TranslateY },
};
_Page.Loaded += async (s, e) =>
{
var _Rectangle = s as Windows.UI.Xaml.Shapes.Rectangle;
var _Brush = await GetWebViewBrush(webView);
_Brush.Stretch = Windows.UI.Xaml.Media.Stretch.UniformToFill;
_Brush.AlignmentY = Windows.UI.Xaml.Media.AlignmentY.Top;
_Brush.Transform = _Rectangle.Tag as Windows.UI.Xaml.Media.TranslateTransform;
_Rectangle.Fill = _Brush;
};
_Pages.Add(_Page);
}
return _Pages;
}
async Task<WebViewBrush> GetWebViewBrush(Windows.UI.Xaml.Controls.WebView webView)
{
// resize width to content
var _OriginalWidth = webView.Width;
var _WidthString = await webView.InvokeScriptAsync("eval",
new[] { "document.body.scrollWidth.toString()" });
int _ContentWidth;
if (!int.TryParse(_WidthString, out _ContentWidth))
throw new Exception(string.Format("failure/width:{0}", _WidthString));
webView.Width = _ContentWidth;
// resize height to content
var _OriginalHeight = webView.Height;
var _HeightString = await webView.InvokeScriptAsync("eval",
new[] { "document.body.scrollHeight.toString()" });
int _ContentHeight;
if (!int.TryParse(_HeightString, out _ContentHeight))
throw new Exception(string.Format("failure/height:{0}", _HeightString));
webView.Height = _ContentHeight;
// create brush
var _OriginalVisibilty = webView.Visibility;
webView.Visibility = Windows.UI.Xaml.Visibility.Visible;
var _Brush = new WebViewBrush
{
SourceName = webView.Name,
Stretch = Windows.UI.Xaml.Media.Stretch.Uniform
};
_Brush.Redraw();
// reset, return
webView.Width = _OriginalWidth;
webView.Height = _OriginalHeight;
webView.Visibility = _OriginalVisibilty;
return _Brush;
}
来源:https://stackoverflow.com/questions/62701768/uwp-webview-printing-shows-the-printed-page-blurred