1 回答

TA貢獻(xiàn)1848條經(jīng)驗(yàn) 獲得超6個(gè)贊
這些方法集允許將ScrollableControl的內(nèi)容打印到位圖。
程序描述:
控件首先滾動(dòng)回原點(diǎn)(
control.AutoScrollPosition = new Point(0, 0);
(否則會(huì)引發(fā)異常:位圖大小錯(cuò)誤。您可能需要存儲(chǔ)當(dāng)前滾動(dòng)位置并在之后恢復(fù))。驗(yàn)證并存儲(chǔ)由PreferredSize或DisplayRectangle屬性返回的 Container 的實(shí)際大小(取決于方法參數(shù)設(shè)置的條件和打印的容器類型)。此屬性考慮容器的完整范圍。
這將是位圖的大小。使用容器的背景顏色清除位圖。
迭代
ScrollableControl.Controls
集合并按其相對(duì)位置打印所有第一級(jí)子控件(子控件的Bounds
矩形相對(duì)于容器 ClientArea。)如果第一級(jí)控件有子控件,則調(diào)用
DrawNestedControls
遞歸方法,該方法將枚舉并繪制所有嵌套的子容器/控件,并保留內(nèi)部剪輯邊界。
包括對(duì) RichTextBox 控件的支持。
該類RichEditPrinter
包含打印 RichTextBox/RichEdit 控件內(nèi)容所需的邏輯。該類EM_FORMATRANGE
使用正在打印控件的位圖的設(shè)備上下文向 RichTextBox 發(fā)送消息。
該ScrollableControlToBitmap()
方法僅采用ScrollableControl
類型作為參數(shù):您不能傳遞 TextBox 控件,即使它使用 ScrollBars。
? 將fullSize
參數(shù)設(shè)置為true
或false
以包含容器內(nèi)的所有子控件或僅包含可見(jiàn)的子控件。如果設(shè)置為true
,則容器ClientRectangle
將展開(kāi)以包含并打印其所有子控件。
? 將includeHidden
參數(shù)設(shè)置為true
或false
以包含或排除隱藏控件(如果有)。
注意:此代碼使用Control.DeviceDpi屬性來(lái)評(píng)估容器設(shè)備上下文的當(dāng)前 Dpi。此屬性需要 .Net Framework 4.7+。如果此版本不可用,您可以刪除:
bitmap.SetResolution(canvas.DeviceDpi,?canvas.DeviceDpi);
可能的話,更新項(xiàng)目的框架版本:)
// Prints the content of the current Form instance,?
// include all child controls and also those that are not visible
var bitmap = ControlPrinter.ScrollableControlToBitmap(this, true, true);
// Prints the content of a ScrollableControl inside a Form
// include all child controls except those that are not visible
var bitmap = ControlPrinter.ScrollableControlToBitmap(this.panel1, true, false);
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using System.Windows.Forms;
public class ControlPrinter
{
? ? public static Bitmap ScrollableControlToBitmap(ScrollableControl canvas, bool fullSize, bool includeHidden)
? ? {
? ? ? ? canvas.AutoScrollPosition = new Point(0, 0);
? ? ? ? if (includeHidden) {
? ? ? ? ? ? canvas.SuspendLayout();
? ? ? ? ? ? foreach (Control child in canvas.Controls) {
? ? ? ? ? ? ? ? child.Visible = true;
? ? ? ? ? ? }
? ? ? ? ? ? canvas.ResumeLayout(true);
? ? ? ? }
? ? ? ? canvas.PerformLayout();
? ? ? ? Size containerSize = canvas.DisplayRectangle.Size;
? ? ? ? if (fullSize) {
? ? ? ? ? ? containerSize.Width = Math.Max(containerSize.Width, canvas.ClientSize.Width);
? ? ? ? ? ? containerSize.Height = Math.Max(containerSize.Height, canvas.ClientSize.Height);
? ? ? ? }
? ? ? ? else {
? ? ? ? ? ? containerSize = canvas.ClientSize;;
? ? ? ? }
? ? ? ? ?
? ? ? ? var bitmap = new Bitmap(containerSize.Width, containerSize.Height, PixelFormat.Format32bppArgb);
? ? ? ? bitmap.SetResolution(canvas.DeviceDpi, canvas.DeviceDpi);
? ? ? ? var graphics = Graphics.FromImage(bitmap);
? ? ? ? if (canvas.BackgroundImage != null) {
? ? ? ? ? ? graphics.DrawImage(canvas.BackgroundImage, new Rectangle(Point.Empty, containerSize));
? ? ? ? }
? ? ? ? else {
? ? ? ? ? ? graphics.Clear(canvas.BackColor);
? ? ? ? }
? ? ? ? var rtfPrinter = new RichEditPrinter(graphics);
? ? ? ? try {
? ? ? ? ? ? DrawNestedControls(canvas, canvas, new Rectangle(Point.Empty, containerSize), bitmap, rtfPrinter);
? ? ? ? ? ? return bitmap;
? ? ? ? }
? ? ? ? finally {
? ? ? ? ? ? rtfPrinter.Dispose();
? ? ? ? ? ? graphics.Dispose();
? ? ? ? }
? ? }
? ? private static void DrawNestedControls(Control outerContainer, Control parent, Rectangle parentBounds, Bitmap bitmap, RichEditPrinter rtfPrinter)
? ? {
? ? ? ? for (int i = parent.Controls.Count - 1; i >= 0; i--) {
? ? ? ? ? ? var ctl = parent.Controls[i];
? ? ? ? ? ? if (!ctl.Visible || (ctl.Width < 1 || ctl.Height < 1)) continue;
? ? ? ? ? ? var clipBounds = Rectangle.Empty;
? ? ? ? ? ? if (parent.Equals(outerContainer)) { clipBounds = ctl.Bounds; }
? ? ? ? ? ? else {
? ? ? ? ? ? ? ? Size scrContainerSize = parentBounds.Size;
? ? ? ? ? ? ? ? if ((parent != ctl) && parent is ScrollableControl scrctl) {
? ? ? ? ? ? ? ? ? ? if (scrctl.VerticalScroll.Visible) scrContainerSize.Width -= (SystemInformation.VerticalScrollBarWidth + 1);
? ? ? ? ? ? ? ? ? ? if (scrctl.HorizontalScroll.Visible) scrContainerSize.Height -= (SystemInformation.HorizontalScrollBarHeight + 1);
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? clipBounds = Rectangle.Intersect(new Rectangle(Point.Empty, scrContainerSize), ctl.Bounds);
? ? ? ? ? ? }
? ? ? ? ? ? if (clipBounds.Width < 1 || clipBounds.Height < 1) continue;
? ? ? ? ? ? var bounds = outerContainer.RectangleToClient(parent.RectangleToScreen(clipBounds));
? ? ? ? ? ? if (ctl is RichTextBox rtb) {
? ? ? ? ? ? ? ? rtfPrinter.DrawRtf(rtb.Rtf, outerContainer.Bounds, bounds, ctl.BackColor);
? ? ? ? ? ? }
? ? ? ? ? ? else {
? ? ? ? ? ? ? ? ctl.DrawToBitmap(bitmap, bounds);
? ? ? ? ? ? }
? ? ? ? ? ? if (ctl.HasChildren) {
? ? ? ? ? ? ? ? DrawNestedControls(outerContainer, ctl, clipBounds, bitmap, rtfPrinter);
? ? ? ? ? ? }
? ? ? ? }
? ? }
? ? internal class RichEditPrinter : IDisposable
? ? {
? ? ? ? Graphics dc = null;
? ? ? ? RTBPrinter rtb = null;
? ? ? ? public RichEditPrinter(Graphics graphics)
? ? ? ? {
? ? ? ? ? ? this.dc = graphics;
? ? ? ? ? ? this.rtb = new RTBPrinter() { ScrollBars = RichTextBoxScrollBars.None };
? ? ? ? }
? ? ? ? public void DrawRtf(string rtf, Rectangle canvas, Rectangle layoutArea, Color color)
? ? ? ? {
? ? ? ? ? ? rtb.Rtf = rtf;
? ? ? ? ? ? rtb.Draw(dc, canvas, layoutArea, color);
? ? ? ? ? ? rtb.Clear();
? ? ? ? }
? ? ? ? public void Dispose() => this.rtb.Dispose();
? ? ? ? private class RTBPrinter : RichTextBox
? ? ? ? {
? ? ? ? ? ? public void Draw(Graphics g, Rectangle hdcArea, Rectangle layoutArea, Color color)
? ? ? ? ? ? {
? ? ? ? ? ? ? ? using (var brush = new SolidBrush(color)) {
? ? ? ? ? ? ? ? ? ? g.FillRectangle(brush, layoutArea);
? ? ? ? ? ? ? ? };
? ? ? ? ? ? ? ? IntPtr hdc = g.GetHdc();
? ? ? ? ? ? ? ? var canvasAreaTwips = new RECT().ToInches(hdcArea);
? ? ? ? ? ? ? ? var layoutAreaTwips = new RECT().ToInches(layoutArea);
? ? ? ? ? ? ? ? var formatRange = new FORMATRANGE() {
? ? ? ? ? ? ? ? ? ? charRange = new CHARRANGE() { cpMax = -1, cpMin = 0 },
? ? ? ? ? ? ? ? ? ? hdc = hdc,
? ? ? ? ? ? ? ? ? ? hdcTarget = hdc,
? ? ? ? ? ? ? ? ? ? rect = layoutAreaTwips,
? ? ? ? ? ? ? ? ? ? rectPage = canvasAreaTwips
? ? ? ? ? ? ? ? };
? ? ? ? ? ? ? ? IntPtr lParam = Marshal.AllocCoTaskMem(Marshal.SizeOf(formatRange));
? ? ? ? ? ? ? ? Marshal.StructureToPtr(formatRange, lParam, false);
? ? ? ? ? ? ? ? SendMessage(this.Handle, EM_FORMATRANGE, (IntPtr)1, lParam);
? ? ? ? ? ? ? ? Marshal.FreeCoTaskMem(lParam);
? ? ? ? ? ? ? ? g.ReleaseHdc(hdc);
? ? ? ? ? ? }
? ? ? ? ? ? [DllImport("User32.dll", CharSet = CharSet.Auto, SetLastError = true)]
? ? ? ? ? ? internal static extern int SendMessage(IntPtr hWnd, int uMsg, IntPtr wParam, IntPtr lParam);
? ? ? ? ? ? internal const int WM_USER = 0x0400;
? ? ? ? ? ? // https://learn.microsoft.com/en-us/windows/win32/controls/em-formatrange
? ? ? ? ? ? internal const int EM_FORMATRANGE = WM_USER + 57;
? ? ? ? ? ? [StructLayout(LayoutKind.Sequential)]
? ? ? ? ? ? internal struct RECT
? ? ? ? ? ? {
? ? ? ? ? ? ? ? public int Left;
? ? ? ? ? ? ? ? public int Top;
? ? ? ? ? ? ? ? public int Right;
? ? ? ? ? ? ? ? public int Bottom;
? ? ? ? ? ? ? ? public Rectangle ToRectangle() => Rectangle.FromLTRB(Left, Top, Right, Bottom);
? ? ? ? ? ? ? ? public RECT ToInches(Rectangle rectangle)
? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? float inch = 14.92f;
? ? ? ? ? ? ? ? ? ? return new RECT() {
? ? ? ? ? ? ? ? ? ? ? ? Left = (int)(rectangle.Left * inch),
? ? ? ? ? ? ? ? ? ? ? ? Top = (int)(rectangle.Top * inch),
? ? ? ? ? ? ? ? ? ? ? ? Right = (int)(rectangle.Right * inch),
? ? ? ? ? ? ? ? ? ? ? ? Bottom = (int)(rectangle.Bottom * inch)
? ? ? ? ? ? ? ? ? ? };
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? ? ? // https://learn.microsoft.com/en-us/windows/win32/api/richedit/ns-richedit-formatrange?
? ? ? ? ? ? [StructLayout(LayoutKind.Sequential)]
? ? ? ? ? ? internal struct FORMATRANGE
? ? ? ? ? ? {
? ? ? ? ? ? ? ? public IntPtr hdcTarget;? ? ? // A HDC for the target device to format for
? ? ? ? ? ? ? ? public IntPtr hdc;? ? ? ? ? ? // A HDC for the device to render to, if EM_FORMATRANGE is being used to send the output to a device
? ? ? ? ? ? ? ? public RECT rect;? ? ? ? ? ? ?// The area within the rcPage rectangle to render to. Units are measured in twips.
? ? ? ? ? ? ? ? public RECT rectPage;? ? ? ? ?// The entire area of a page on the rendering device. Units are measured in twips.
? ? ? ? ? ? ? ? public CHARRANGE charRange;? ?// The range of characters to format (see CHARRANGE)
? ? ? ? ? ? }
? ? ? ? ? ? [StructLayout(LayoutKind.Sequential)]
? ? ? ? ? ? internal struct CHARRANGE
? ? ? ? ? ? {
? ? ? ? ? ? ? ? public int cpMin;? ? ? ? ? ?// First character of range (0 for start of doc)
? ? ? ? ? ? ? ? public int cpMax;? ? ? ? ? ?// Last character of range (-1 for end of doc)
? ? ? ? ? ? }
? ? ? ? }
? ? }
}
- 1 回答
- 0 關(guān)注
- 167 瀏覽
添加回答
舉報(bào)