在表格外使用放大镜

Using Magnifying Glass Outside the Form

下午好!有一个表格(镜头),用作放大镜(在表格内部)。我需要摆脱 TargetForm 属性 以便表单(Lens)作为一个独立的控件在不绑定到表单的情况下工作。用什么代码代替,请告诉我。

Main_Form

的代码
private void button1_Click(object sender, EventArgs e) {
    new Lens_Form() { TargetForm = this }.Show(this);
    Cursor.Hide();
}

Lens_Form

的代码
public partial class Lens_Form : Form {
    public Form TargetForm { get; set; }
    public new float Scale { get; set; }

    private Bitmap tmpBmp;

    public Lens_Form() {
        InitializeComponent();

        // Drawing the Ellipse
        GraphicsPath path = new GraphicsPath();
        path.AddEllipse(ClientRectangle);
        Region = new Region(path);

        // Set Scale
        Scale = 2; // 2-4-6-8
    }

    protected override void OnPaint(PaintEventArgs e) {
        Point pos = TargetForm.PointToClient(Cursor.Position);
        Location = new Point(Cursor.Position.X - Width / 2, Cursor.Position.Y - Height / 2);

        Rectangle screenRectangle = TargetForm.RectangleToScreen(TargetForm.ClientRectangle);
        int dY = screenRectangle.Top - TargetForm.Top;
        int dX = screenRectangle.Left - TargetForm.Left;

        e.Graphics.TranslateTransform(Width / 2, Height / 2);
        e.Graphics.ScaleTransform(Scale, Scale);
        e.Graphics.TranslateTransform(-pos.X - dX, -pos.Y - dY);

        if (tmpBmp != null) e.Graphics.DrawImage(tmpBmp, 0, 0);
    }

    // Timer
    private void Main_Timer_Tick(object sender, EventArgs e) {
        tmpBmp = new Bitmap(TargetForm.Size.Width, TargetForm.Size.Height);
        TargetForm.DrawToBitmap(tmpBmp, new Rectangle(0, 0, TargetForm.Width, TargetForm.Height));
        Invalidate();
    }
}

您需要按比例放大并使用更大的边界,屏幕的边界而不是表单的边界。通过 Graphics.CopyFromScreen 方法捕获屏幕而不是表单。

这是一个使用相同方法的一些附加功能的工作示例。

using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;

public class Lens_Form : Form
{
    private readonly Timer timer;
    private Bitmap scrBmp;
    private Graphics scrGrp;
    private bool mouseDown;

    public Lens_Form() : base()
    {
        SetStyle(
            ControlStyles.OptimizedDoubleBuffer |
            ControlStyles.Opaque |
            ControlStyles.UserPaint |
            ControlStyles.AllPaintingInWmPaint, true);
        UpdateStyles();

        FormBorderStyle = FormBorderStyle.None;
        ShowInTaskbar = false;
        TopMost = true;
        Width = 150;
        Height = 150;

        timer = new Timer() { Interval = 55, Enabled = true };
        timer.Tick += (s, e) => Invalidate();
    }

    public int ZoomFactor { get; set; } = 2;
    public bool HideCursor { get; set; } = true;
    public bool AutoClose { get; set; } = true;
    public bool NearestNeighborInterpolation { get; set; }

    protected override void OnShown(EventArgs e)
    {
        base.OnShown(e);

        var gp = new GraphicsPath();
        gp.AddEllipse(0, 0, Width, Height);
        Region = new Region(gp);

        CopyScreen();
        SetLocation();

        Capture = true;
        mouseDown = true;
        if (HideCursor) Cursor.Hide();
    }

    protected override void OnMouseDown(MouseEventArgs e)
    {
        base.OnMouseDown(e);

        if (e.Button == MouseButtons.Left)
        {
            mouseDown = true;
            if (HideCursor) Cursor.Hide();
        }
    }

    protected override void OnMouseMove(MouseEventArgs e)
    {
        base.OnMouseMove(e);
        Invalidate();
    }

    protected override void OnMouseUp(MouseEventArgs e)
    {
        base.OnMouseUp(e);

        mouseDown = false;
        if (HideCursor) Cursor.Show();
        if (AutoClose) Dispose();
    }

    protected override void OnKeyDown(KeyEventArgs e)
    {
        base.OnKeyDown(e);

        if (e.KeyCode == Keys.Escape) Dispose();
    }

    protected override void OnPaint(PaintEventArgs e)
    {
        if (mouseDown) SetLocation();
        else CopyScreen();

        var pos = Cursor.Position;
        var cr = RectangleToScreen(ClientRectangle);
        var dY = cr.Top - Top;
        var dX = cr.Left - Left;

        e.Graphics.TranslateTransform(Width / 2, Height / 2);
        e.Graphics.ScaleTransform(ZoomFactor, ZoomFactor);
        e.Graphics.TranslateTransform(-pos.X - dX, -pos.Y - dY);
        e.Graphics.Clear(BackColor);

        if (NearestNeighborInterpolation)
        {
            e.Graphics.InterpolationMode = InterpolationMode.NearestNeighbor;
            e.Graphics.PixelOffsetMode = PixelOffsetMode.Half;
        }

        if (scrBmp != null) e.Graphics.DrawImage(scrBmp, 0, 0);
    }

    protected override void Dispose(bool disposing)
    {
        if (disposing)
        {
            timer.Dispose();
            scrBmp?.Dispose();
            scrGrp?.Dispose();
        }
        base.Dispose(disposing);
    }

    private void CopyScreen()
    {
        if (scrBmp == null)
        {
            var sz = Screen.FromControl(this).Bounds.Size;

            scrBmp = new Bitmap(sz.Width, sz.Height);
            scrGrp = Graphics.FromImage(scrBmp);
        }

        scrGrp.CopyFromScreen(Point.Empty, Point.Empty, scrBmp.Size);
    }

    private void SetLocation()
    {
        var p = Cursor.Position;

        Left = p.X - Width / 2;
        Top = p.Y - Height / 2;
    }
}

主窗体中的调用者:

private void SomeButton_MouseDown(object sender, MouseEventArgs e)
{
    var f = new Lens_Form()
    {
        Size = new Size(150, 150),
        AutoClose = true,
        HideCursor = true,
        ZoomFactor = 2,
        NearestNeighborInterpolation = false
    };
    f.Show();
}

备注

  • 在构造函数中调用SetStyle方法,将此上下文中的一些有用的样式应用到控件中。接管绘制程序,减少闪烁,防止绘制背景。

  • 重写OnShown方法获取调用方设置的大小并设置区域,截图,设置Form位置

  • 覆盖鼠标方法以应用属性并刷新视图。

  • 覆盖 OnPaint 方法以在按下鼠标左键时移动窗体,否则重新复制屏幕。这是为您提供两个选项,如果您禁用 AutoClose 属性,则在拖动表单时放大或在不按下任何按钮的情况下移动鼠标时放大。对于转换部分,使用当前Cursor.Position和Form的屏幕坐标(而不是主要的From)来做数学运算。

  • 最后,覆盖 Dispose 方法进行清理。

演示