WPF中的可视化渲染技术

在Windows Presentation Foundation (WPF)中,经常需要将用户界面元素(如控件和布局)渲染为图像文件,或者直接打印到打印机上。Microsoft为WPF的表示模型构建了一个非常结构化的类层次结构,这为添加功能提供了坚实的基础。在WPF中,放置的每个元素都继承自Visual类,而基础类库(BCL)为Visual添加了内置功能,使其能够渲染为BitmapSource或直接打印到打印机。本文将讨论如何轻松地将Visual渲染为BitmapSource,并将其用作Image控件的源。

示例应用程序

RenderTargetBitmap类主要用于将Visual渲染为位图对象。使用InkCanvas来演示这个过程。InkCanvas是一个特殊的画布元素,允许用户在屏幕上绘制图像。在继续代码之前,让先讨论一下示例应用程序。

在上图中,可以看到放置了一个InkCanvas,它允许在屏幕上动态地书写。按钮将从Canvas渲染位图图像,并将其添加到右侧的ListBox中。可以看到,当点击“Render as Bitmap”按钮后,它实际上将相同的视觉元素作为BitmapSource放置在右侧的ListBox中。

类似于之前所做的,更改了内容并再次拍摄,它确实做了同样的事情。最后,当点击“Render the Grid as Bitmap”时,它实际上渲染了整个Grid,包括ListBox和所有内容。还有一些其他按钮,如“Save Selection as JPEG / Print as Visual”,每个按钮都有自己的功能。

使用代码

现在,已经看到了基本功能,让讨论代码。

<InkCanvas EditingMode="Ink" Grid.Row="1" x:Name="inkCanvas" Background="AliceBlue" /> <ListBox x:Name="lstImages" Grid.Row="1" Grid.Column="1" ItemsSource="{Binding ImageCollection}"> <ListBox.ItemTemplate> <DataTemplate> <Image Source="{Binding}" Stretch="UniformToFill" MaxWidth="150" MaxHeight="150" Margin="10" /> </DataTemplate> </ListBox.ItemTemplate> <ListBox.ItemsPanel> <ItemsPanelTemplate> <WrapPanel Orientation="Horizontal" Width="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ScrollContentPresenter}}, Path=ActualWidth}" /> </ItemsPanelTemplate> </ListBox.ItemsPanel> </ListBox> <StackPanel Grid.Row="2" Grid.ColumnSpan="2" Orientation="Horizontal"> <Button x:Name="btnRenderBitmap" Content="Render as BitMap" Click="btnRenderBitmap_Click" /> <Button x:Name="btnRenderwhole" Content="Render the Grid as Bitmap" Click="btnRenderwhole_Click" /> <Button x:Name="btnRenderJPEG" Content="Save Selecteditem as Jpeg" Click="btnRenderJPEG_Click" /> <Button x:Name="btnPrintVisual" Content="Print the Visual" Click="btnPrintVisual_Click" /> </StackPanel>

在这里,放置了一个InkCanvas元素和一个ListBox,以及一些按钮。XAML非常直接,当按钮被点击时,ListBox将更新自身。

private ObservableCollection<BitmapSource> imagecollection;

public ObservableCollection<BitmapSource> ImageCollection

get { this.imagecollection = this.imagecollection ?? new ObservableCollection<BitmapSource>(); return this.imagecollection; }

public RenderTargetBitmap RenderVisaulToBitmap(Visual vsual, int width, int height)

{ RenderTargetBitmap rtb = new RenderTargetBitmap(width, height, 96, 96, PixelFormats.Default); rtb.Render(vsual); BitmapSource bsource = rtb; this.ImageCollection.Add(bsource); return rtb; }

使用了一个模型来使用RenderTargetBitmap。基本上,模型将逻辑与表示分离。创建了一个BitmapSource对象的ObservableCollection。现在当Button1被点击时,使用RenderTargetBitmap来渲染Visual。RenderTargetBitmap接受width、height、dpiX和dpiY作为参数,并从Visual渲染BitmapSource对象。rtb.Render(visual)获取BitmapSource对象,已经将其添加到ImageCollection中。由于它直接绑定到ListBox,ListBox会立即更新图像。

从BitmapSource对象渲染图像实际上并不困难。让看一下下面的代码:

public MemoryStream GenerateImage(Visual vsual, int width, int height, ImageFormat format) { BitmapEncoder encoder = null; switch (format) { case ImageFormat.JPG: encoder = new JpegBitmapEncoder(); break; case ImageFormat.PNG: encoder = new PngBitmapEncoder(); break; case ImageFormat.BMP: encoder = new BmpBitmapEncoder(); break; case ImageFormat.GIF: encoder = new GifBitmapEncoder(); break; case ImageFormat.TIF: encoder = new TiffBitmapEncoder(); break; } if (encoder == null) return null; RenderTargetBitmap rtb = this.RenderVisaulToBitmap(vsual, width, height); MemoryStream file = new MemoryStream(); encoder.Frames.Add(BitmapFrame.Create(rtb)); encoder.Save(file); return file; }

在这里,使用了一个枚举来决定图像的格式。在方法中,当传递JPEG时,它实际上会创建一个JpegBitmapEncoder对象,让将BitmapSource编码为压缩的JPEG格式。encoder.Frames允许添加一个BitmapFrame,稍后将被渲染为Image。可以使用JpegBitmapDecoder来解码JPEG图像。一旦调用encoder.Save,它将图像保存到Stream中。

WPF打印Visual是整个过程中最简单的,如果想直接将inkCanvas打印到打印机上,那么不需要创建PrintDocument。稍后将讨论如何打印FlowDocument。要打印Visual,需要使用PrintDialog。

PrintDialog dlg = new PrintDialog(); dlg.PrintVisual(this.inkCanvas, "The picture is drawn dynamically");
沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485