UWP WebView 打印显示打印页面模糊
UWP WebView Printing shows the printed page blurred
网上有很多关于 WebVeiw
网页打印的投诉(例如 this one and this 一个),其中打印的页面只显示屏幕上的当前视图,而不是整个页面。通过在线搜索的一些代码,我能够使用以下代码打印整个页面。但是打印页面已尝试在启用滚动条的情况下将其全部打印在一页中。而且打印出来的内容很模糊(如下图)
问题:如何在启用滚动条的情况下打印出整页内容而不模糊?
WebView中使用的网站:Wikipedia
参考: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
打印的 PDF:可用 here
更新:
@Faywang - MSFT
的回应似乎很有希望。我尝试如下:
- 使用来自 here
的 OnPrintButtonClick()
事件的代码创建了方法调用 PrintWebView()
- 添加了上面 post 中显示的所有其他 methods/events。
- 在我的
btnTest_Click(...)
中,我修改了用户@Faywang
的代码如下
.
List<Rectangle> allpages = await GetWebPages(wvTest, new Windows.Foundation.Size(750d, 950d));
//print these pages
foreach (Rectangle rectangle in allpages)
PrintWebView();
- 当应用程序在默认模式下运行时,它显示
allpages
的计数为 7,上面的 foreach
调用 PrintWebView()
7 次。如果我将应用程序屏幕置于最大模式,allpages
的计数为 3 并且 foreach
调用 PrintWebView()
3 次(如预期)。在这两种情况下,我都期望循环的最后一次迭代将带来打印对话框并打印所有第 7 页(或第 3 页,具体取决于应用程序屏幕的默认或最大模式)。相反,调试器转到 PrintWebView()
方法的 catch
块,如前所述,其代码取自 here
- 问题:我是否按照上述步骤正确操作?如果没有,什么是更好的方法?
将webview压缩重绘到矩形后会变得模糊,最好分页显示。首先我给打印页面的宽度和高度为750、950,然后根据比例来计算需要多少页。之后,您可以打印这些矩形。例如:
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;
}
网上有很多关于 WebVeiw
网页打印的投诉(例如 this one and this 一个),其中打印的页面只显示屏幕上的当前视图,而不是整个页面。通过在线搜索的一些代码,我能够使用以下代码打印整个页面。但是打印页面已尝试在启用滚动条的情况下将其全部打印在一页中。而且打印出来的内容很模糊(如下图)
问题:如何在启用滚动条的情况下打印出整页内容而不模糊?
WebView中使用的网站:Wikipedia
参考: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
打印的 PDF:可用 here
更新:
@Faywang - MSFT
的回应似乎很有希望。我尝试如下:
- 使用来自 here 的
- 添加了上面 post 中显示的所有其他 methods/events。
- 在我的
btnTest_Click(...)
中,我修改了用户@Faywang
的代码如下
OnPrintButtonClick()
事件的代码创建了方法调用 PrintWebView()
.
List<Rectangle> allpages = await GetWebPages(wvTest, new Windows.Foundation.Size(750d, 950d));
//print these pages
foreach (Rectangle rectangle in allpages)
PrintWebView();
- 当应用程序在默认模式下运行时,它显示
allpages
的计数为 7,上面的foreach
调用PrintWebView()
7 次。如果我将应用程序屏幕置于最大模式,allpages
的计数为 3 并且foreach
调用PrintWebView()
3 次(如预期)。在这两种情况下,我都期望循环的最后一次迭代将带来打印对话框并打印所有第 7 页(或第 3 页,具体取决于应用程序屏幕的默认或最大模式)。相反,调试器转到PrintWebView()
方法的catch
块,如前所述,其代码取自 here - 问题:我是否按照上述步骤正确操作?如果没有,什么是更好的方法?
将webview压缩重绘到矩形后会变得模糊,最好分页显示。首先我给打印页面的宽度和高度为750、950,然后根据比例来计算需要多少页。之后,您可以打印这些矩形。例如:
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;
}