将C# Windows窗体控件转换为图像?

4

我正在尝试创建一个面板的图像并将其保存到文件夹中。问题在于该面板具有滚动条,生成的图像仅针对面板的可见部分。

我使用的代码类似于Panel.DrawToImage。是否有任何帮助可以保存整个面板作为图片而不仅仅是可见部分?

7个回答

4

我认为WM_PRINT消息会很有帮助。这里有一个几乎可以工作的示例(它仍然打印滚动条本身,并且背景在“超出滚动”部分丢失)。也许您可以尝试调整一下并使其正常工作,或者有更多WinForms经验的人可以将其提升到下一个级别?

在您的类中声明以下内容:

[DllImport("user32.dll")]
public static extern int SendMessage(IntPtr hWnd, int Msg, int wParam, int lParam);

private const int WM_PRINT = 791;

/// <summary>
/// The WM_PRINT drawing options
/// </summary>
[Flags]
private enum DrawingOptions
{
    /// <summary>
    /// Draws the window only if it is visible.
    /// </summary>
    PRF_CHECKVISIBLE = 1,

    /// <summary>
    /// Draws the nonclient area of the window.
    /// </summary>
    PRF_NONCLIENT = 2,
    /// <summary>

    /// Draws the client area of the window.
    /// </summary>
    PRF_CLIENT = 4,

    /// <summary>
    /// Erases the background before drawing the window.
    /// </summary>
    PRF_ERASEBKGND = 8,

    /// <summary>
    /// Draws all visible children windows.
    /// </summary>
    PRF_CHILDREN = 16,

    /// <summary>
    /// Draws all owned windows.
    /// </summary>
    PRF_OWNED = 32
}

然后,将控件绘制到位图上:

using (Bitmap screenshot = new Bitmap(this.panel1.DisplayRectangle.Width, this.panel1.DisplayRectangle.Height))
using (Graphics g = Graphics.FromImage(screenshot))
{
    try
    {
        SendMessage(this.panel1.Handle, WM_PRINT, g.GetHdc().ToInt32(), (int)(DrawingOptions.PRF_CHILDREN | DrawingOptions.PRF_CLIENT | DrawingOptions.PRF_NONCLIENT | DrawingOptions.PRF_OWNED));
    }
    finally
    {
        g.ReleaseHdc();
    }
    screenshot.Save("temp.bmp");
}

编辑:这里有一种替代的绘制策略,可能可以帮助你达到目标。我做了一些假设,但也许它会起作用。首先在位图上绘制虚拟背景,并且去除滚动条:

using (Bitmap screenshot = new Bitmap(this.panel1.DisplayRectangle.Width, this.panel1.DisplayRectangle.Height))
using (Graphics g = Graphics.FromImage(screenshot))
{
    g.FillRectangle(SystemBrushes.Control, 0, 0, screenshot.Width, screenshot.Height);
    try
    {
        SendMessage(this.panel1.Handle, WM_PRINT, g.GetHdc().ToInt32(), (int)(DrawingOptions.PRF_CHILDREN | DrawingOptions.PRF_CLIENT | DrawingOptions.PRF_OWNED));
    }
    finally
    {
        g.ReleaseHdc();
    }
    screenshot.Save("temp.bmp");
}

2
没有什么好的解决方案,DrawToBitmap只能绘制用户可见部分,诸如事先改变面板位置和大小的技巧往往由于父控件所施加的限制而很快失效。窗体本身永远不能大于屏幕,现在您还需要考虑目标计算机上视频适配器的分辨率。
一种丑陋的处理方法是多次运行DrawToBitmap,在每次调用之间更改Panel的AutoScrollPosition属性。然后您需要将生成的图像拼接在一起。当然特别要注意最后一个图像,因为它不会像其他图像那样大。
一旦您开始考虑这样的代码,您真的应该考虑使用PrintDocument或报表生成器。其中一个好处是打印输出看起来会更干净,因为由于视频和打印机分辨率之间的巨大差异,打印屏幕截图总是难看的。

谢谢!目前似乎这是唯一的方法!虽然需要一些工作!再次感谢! - Shri
嘿,大家好。这是一个更新...一种解决方法是在内存中创建相同的控件,并定义自定义高度和宽度,然后调用DrawToBitmap函数!!非常成功!希望能帮到其他人! - Shri

1

考虑暂时增加面板的大小,调用 p.Layout(),然后 p.DrawToImage()


控件比屏幕的显示尺寸要大,因此滚动条根本无法消除! - Shri

1
也许您可以将控件克隆到另一个(隐藏的)窗体上,使其具有所需的大小(以便不显示滚动条),然后以这种方式调用DrawToImage()函数?

0

嗯...听起来(因为你在原始问题中没有这么说),你是否有一个作为容器的picturebox,位于面板容器内,也就是嵌套容器?

您考虑过这样做吗:

// 假设 pic 是 PictureBox 类型,并且已经分配了图像属性
PictureBox pic;
// 并且该 picturebox 嵌入在面板变量 p 中。 p.Controls.Add(pic);
// ...
// 那么为什么不这样做呢? pic.Image.Save(...);

Image 类的 Save 方法有 5 种重载形式,可以根据自己的需求选择。

希望对您有所帮助, 此致, Tom。


2
我认为他想要在窗体上“截屏”一个容器控件。 - hometoast
@hometoast:哦...现在我明白了...嗯...Alt+PrtScrn,然后粘贴到mspaint中怎么样?或者使用其他屏幕截图工具?简而言之,答案是否定的,因为屏幕截图会捕捉整个屏幕,无论图像是否被裁剪! - t0mm13b

0
我将创建一个大小正确的位图(并用正确的颜色清除它或者用面板的背景图像绘制到位图上),然后循环遍历所有子控件,并.DrawToImage将它们中的每一个放置在正确的位置。

0

你需要将你的面板设置为 AutoZize=TrueAutoScroll=False,然后将其放入另一个容器中,并将该容器设置为 AutoScroll=true


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接