如何使用图形对象渲染 WPF Hwnd
How to render a WPF Hwnd with Graphics Object
RenderTargetBitmap 之后太慢了。我尝试了一种不同的方法。不幸的是我的尝试没有成功。希望你们中的一些人能弄清楚为什么这段代码不起作用。
var myPopup = new Popup();
var child = new Grid() { Background = new VisualBrush(myVisual)};
myPopup.StaysOpen = false;
myPopup.Child = child;
myPopup.IsOpen = false;
myPopup.Opened += (sender, args) =>
{
var source = ((HwndSource)PresentationSource.FromVisual(myPopup.Child));
var image = new Bitmap(1000,1000);
using (Graphics gr = Graphics.FromHwnd(source.Handle))
{
var ptr = gr.GetHdc();
using (Graphics g = Graphics.FromHdc(ptr))
{
g.DrawImage(image, new System.Drawing.Point(0, 0));
}
gr.ReleaseHdc(ptr);
}
//The image is just black...
image.Save("test.png");
myPopup.IsOpen = false;
};
myPopup.IsOpen = true;
恐怕您的代码无法运行。实际上,您正在将一个空图像复制到 Graphics
,然后将该图像保存到一个文件中。所以文件是空的。
如果您不想使用RenderTargetBitmap, then you need to use BitBlt功能。
让我们看看如何去做。首先让我们创建一个助手 class 来管理从 Visual 到 Bitmap 的转换:
public static class VisualToBitmapConverter
{
private enum TernaryRasterOperations : uint
{
SRCCOPY = 0x00CC0020,
SRCPAINT = 0x00EE0086,
SRCAND = 0x008800C6,
SRCINVERT = 0x00660046,
SRCERASE = 0x00440328,
NOTSRCCOPY = 0x00330008,
NOTSRCERASE = 0x001100A6,
MERGECOPY = 0x00C000CA,
MERGEPAINT = 0x00BB0226,
PATCOPY = 0x00F00021,
PATPAINT = 0x00FB0A09,
PATINVERT = 0x005A0049,
DSTINVERT = 0x00550009,
BLACKNESS = 0x00000042,
WHITENESS = 0x00FF0062,
CAPTUREBLT = 0x40000000
}
[DllImport("gdi32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool BitBlt(IntPtr hdc, int nXDest, int nYDest, int nWidth, int nHeight, IntPtr hdcSrc, int nXSrc, int nYSrc, TernaryRasterOperations dwRop);
public static Bitmap GetBitmap(Visual visual, int width, int height)
{
IntPtr source;
IntPtr destination;
Bitmap bitmap = new Bitmap(width, height);
HwndSource hwndSource = (HwndSource)PresentationSource.FromVisual(visual);
using (Graphics graphicsFromVisual = Graphics.FromHwnd(hwndSource.Handle))
{
using (Graphics graphicsFromImage = Graphics.FromImage(bitmap))
{
source = graphicsFromVisual.GetHdc();
destination = graphicsFromImage.GetHdc();
BitBlt(destination, 0, 0, bitmap.Width, bitmap.Height, source, 0, 0, TernaryRasterOperations.SRCCOPY);
graphicsFromVisual.ReleaseHdc(source);
graphicsFromImage.ReleaseHdc(destination);
}
}
return bitmap;
}
}
现在我们可以编写一个简单的 XAML 来测试助手 class:
<Window x:Class="WpfApplication1.MainWindow" Name="win"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="400">
<StackPanel>
<Border BorderBrush="DarkGray" BorderThickness="4" CornerRadius="4"
Background="LightGray" Padding="6" Name="border">
<Label Content="Copy me to a bitmap file, please" FontSize="20" Foreground="Green"
FontStyle="Italic" />
</Border>
<Button Content="Save to file" Margin="20" HorizontalAlignment="Center"
Click="Button_Click" />
</StackPanel>
</Window>
其代码隐藏:
namespace WpfApplication1
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
Bitmap image = VisualToBitmapConverter.GetBitmap(border,
(int)border.ActualWidth, (int)border.ActualHeight);
image.Save(@"C:\YourPath\test.png");
}
}
}
如果单击该按钮,您会发现边框及其内容已复制到位图文件中。希望这个方法的速度适合你的目的。
RenderTargetBitmap 之后太慢了。我尝试了一种不同的方法。不幸的是我的尝试没有成功。希望你们中的一些人能弄清楚为什么这段代码不起作用。
var myPopup = new Popup();
var child = new Grid() { Background = new VisualBrush(myVisual)};
myPopup.StaysOpen = false;
myPopup.Child = child;
myPopup.IsOpen = false;
myPopup.Opened += (sender, args) =>
{
var source = ((HwndSource)PresentationSource.FromVisual(myPopup.Child));
var image = new Bitmap(1000,1000);
using (Graphics gr = Graphics.FromHwnd(source.Handle))
{
var ptr = gr.GetHdc();
using (Graphics g = Graphics.FromHdc(ptr))
{
g.DrawImage(image, new System.Drawing.Point(0, 0));
}
gr.ReleaseHdc(ptr);
}
//The image is just black...
image.Save("test.png");
myPopup.IsOpen = false;
};
myPopup.IsOpen = true;
恐怕您的代码无法运行。实际上,您正在将一个空图像复制到 Graphics
,然后将该图像保存到一个文件中。所以文件是空的。
如果您不想使用RenderTargetBitmap, then you need to use BitBlt功能。
让我们看看如何去做。首先让我们创建一个助手 class 来管理从 Visual 到 Bitmap 的转换:
public static class VisualToBitmapConverter
{
private enum TernaryRasterOperations : uint
{
SRCCOPY = 0x00CC0020,
SRCPAINT = 0x00EE0086,
SRCAND = 0x008800C6,
SRCINVERT = 0x00660046,
SRCERASE = 0x00440328,
NOTSRCCOPY = 0x00330008,
NOTSRCERASE = 0x001100A6,
MERGECOPY = 0x00C000CA,
MERGEPAINT = 0x00BB0226,
PATCOPY = 0x00F00021,
PATPAINT = 0x00FB0A09,
PATINVERT = 0x005A0049,
DSTINVERT = 0x00550009,
BLACKNESS = 0x00000042,
WHITENESS = 0x00FF0062,
CAPTUREBLT = 0x40000000
}
[DllImport("gdi32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool BitBlt(IntPtr hdc, int nXDest, int nYDest, int nWidth, int nHeight, IntPtr hdcSrc, int nXSrc, int nYSrc, TernaryRasterOperations dwRop);
public static Bitmap GetBitmap(Visual visual, int width, int height)
{
IntPtr source;
IntPtr destination;
Bitmap bitmap = new Bitmap(width, height);
HwndSource hwndSource = (HwndSource)PresentationSource.FromVisual(visual);
using (Graphics graphicsFromVisual = Graphics.FromHwnd(hwndSource.Handle))
{
using (Graphics graphicsFromImage = Graphics.FromImage(bitmap))
{
source = graphicsFromVisual.GetHdc();
destination = graphicsFromImage.GetHdc();
BitBlt(destination, 0, 0, bitmap.Width, bitmap.Height, source, 0, 0, TernaryRasterOperations.SRCCOPY);
graphicsFromVisual.ReleaseHdc(source);
graphicsFromImage.ReleaseHdc(destination);
}
}
return bitmap;
}
}
现在我们可以编写一个简单的 XAML 来测试助手 class:
<Window x:Class="WpfApplication1.MainWindow" Name="win"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="400">
<StackPanel>
<Border BorderBrush="DarkGray" BorderThickness="4" CornerRadius="4"
Background="LightGray" Padding="6" Name="border">
<Label Content="Copy me to a bitmap file, please" FontSize="20" Foreground="Green"
FontStyle="Italic" />
</Border>
<Button Content="Save to file" Margin="20" HorizontalAlignment="Center"
Click="Button_Click" />
</StackPanel>
</Window>
其代码隐藏:
namespace WpfApplication1
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
Bitmap image = VisualToBitmapConverter.GetBitmap(border,
(int)border.ActualWidth, (int)border.ActualHeight);
image.Save(@"C:\YourPath\test.png");
}
}
}
如果单击该按钮,您会发现边框及其内容已复制到位图文件中。希望这个方法的速度适合你的目的。