1

ユーザーがマウスをクリック、保持、およびドラッグするときに、ユーザーのマウス位置に関連する長方形の領域を描画する Windows フォーム アプリケーションを開発しました。

クラスは非常に単純で、次のとおりです。

public partial class MainForm : LayeredForm
{
    private bool drawing = false;
    private Point startLocation = Point.Empty;

    public MainForm()
    {
        InitializeComponent();
        Location = Screen.PrimaryScreen.Bounds.Location;
        Size = Screen.PrimaryScreen.Bounds.Size;
        Cursor = Cursors.Cross;
        TopMost = true;
        ShowInTaskbar = false;
        DoubleBuffered = true;
    }

    private void MainForm_MouseDown(object sender, MouseEventArgs e)
    {
        drawing = true;
        startLocation = e.Location;
    }

    private void MainForm_MouseMove(object sender, MouseEventArgs e)
    {
        if (drawing)
            Invalidate();
    }

    private void MainForm_MouseUp(object sender, MouseEventArgs e)
    {
        drawing = false;
    }

    private void MainForm_Paint(object sender, PaintEventArgs e)
    {
        Rectangle r = new Rectangle(Math.Min(startLocation.X, Cursor.Position.X), Math.Min(startLocation.Y, Cursor.Position.Y),
            Math.Abs(startLocation.X - Cursor.Position.X), Math.Abs(startLocation.Y - Cursor.Position.Y));

        e.Graphics.FillRectangle(Brushes.Blue, r);
    }
}


public class LayeredForm : Form
{
    public new event PaintEventHandler Paint;

    public LayeredForm()
    {
        SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
        StartPosition = FormStartPosition.Manual;
    }

    protected override CreateParams CreateParams
    {
        get
        {
            if (DesignMode) return base.CreateParams;
            CreateParams cParams = base.CreateParams;
            cParams.ExStyle = cParams.ExStyle | 0x80000;
            return cParams;
        }
    }

    private void PaintNative(Bitmap bitmap)
    {
        IntPtr hdcDestination = Win32.GetDC(IntPtr.Zero);
        IntPtr hdcSource = Win32.CreateCompatibleDC(hdcDestination);
        IntPtr hdcBitmap = IntPtr.Zero;
        IntPtr previousBitmap = IntPtr.Zero;

        try
        {
            hdcBitmap = bitmap.GetHbitmap(Color.FromArgb(0));
            previousBitmap = Win32.SelectObject(hdcSource, hdcBitmap);

            Win32.SIZE size = new Win32.SIZE(bitmap.Width, bitmap.Height);
            Win32.POINT source = new Win32.POINT(0, 0);
            Win32.POINT destination = new Win32.POINT(Left, Top);
            Win32.BLENDFUNCTION blendFunc = new Win32.BLENDFUNCTION()
            {
                BlendOp = Win32.AC_SRC_OVER,
                BlendFlags = 0,
                SourceConstantAlpha = 50,
                AlphaFormat = Win32.AC_SRC_ALPHA
            };

            Win32.UpdateLayeredWindow(Handle, hdcDestination, ref destination, ref size, hdcSource, ref source, 0, ref blendFunc, 2);
        }
        finally
        {
            Win32.ReleaseDC(IntPtr.Zero, hdcDestination);
            if (hdcBitmap != IntPtr.Zero)
            {
                Win32.SelectObject(hdcSource, previousBitmap);
                Win32.DeleteObject(hdcBitmap);
            }
            Win32.DeleteDC(hdcSource);
        }
    }

    public new void Invalidate()
    {
        using (Bitmap bitmap = new Bitmap(ClientSize.Width, ClientSize.Height))
        {
            using (Graphics graphics = Graphics.FromImage(bitmap))
            {
                if (Paint != null)
                    Paint(this, new PaintEventArgs(graphics, Rectangle.Empty));
            }
            PaintNative(bitmap);
        }
    }
}

私のコンピューターと他のほとんどのコンピューターでは、すべて正常に動作します。ただし、さまざまなマシンでいくつかのテストを行った後、一部のコンピューターで四角形の描画に苦労していることがわかりました (著しく遅く、途切れ途切れです)。問題を特定したと思いますが、この問題の検証と解決が必要です。

問題は、マウス移動イベントが発生するたびに、フォーム全体が何度も何度も描画されるという事実にあると思います。大きな解像度を持つ弱いコンピュータでは、これは非常に骨の折れる作業です。

いくつかの調査を行った後、解決策は、フォーム全体ではなく、変更された長方形の部分のみを描画することだと思いますが、これを行う方法についてはわかりません. SOが提供できるヘルプを本当に感謝します。事前に感謝します。

アップデート

ケンの完全なコード:

public sealed partial class RegionForm : LayeredWindow // : Form
{
    private Bitmap bitmap;
    private bool mouseDown;
    private Point newPoint = Point.Empty;
    private Point oldPoint = Point.Empty;
    private Point startPoint = Point.Empty;

    public RegionForm()
    {
        InitializeComponent();
        Location = Screen.PrimaryScreen.Bounds.Location;
        Size = Screen.PrimaryScreen.Bounds.Size;
        Cursor = Cursors.Cross;
        TopMost = true;
        ShowInTaskbar = false;
    }

    private void regionPanel_MouseDown(object sender, MouseEventArgs e)
    {
        bitmap = new Bitmap(regionPanel.ClientSize.Width,
                            regionPanel.ClientSize.Height,
                            PixelFormat.Format32bppPArgb);

        regionPanel.DrawToBitmap(bitmap, regionPanel.ClientRectangle);
        startPoint = e.Location;
        mouseDown = true;
    }

    private void regionPanel_MouseUp(object sender, MouseEventArgs e)
    {
        mouseDown = false;

        if (bitmap != null)
        {
            bitmap.Dispose();
            bitmap = null;
        }

        Rectangle region = new Rectangle(startPoint, new Size(oldPoint.X - startPoint.X + 1,
                                                              oldPoint.Y - startPoint.Y + 1));
        regionPanel.Invalidate(region, true);
    }

    private void regionPanel_MouseMove(object sender, MouseEventArgs e)
    {
        if (mouseDown)
        {
            using (Graphics g = regionPanel.CreateGraphics())
            {
                g.SmoothingMode = SmoothingMode.None;
                newPoint = e.Location;
                ClearRegion(g);
                oldPoint = newPoint;
                DrawRegion(g);
            }
        }
    }

    private void DrawRegion(Graphics g)
    {
        int x1 = startPoint.X;
        int y1 = startPoint.Y;
        int x2 = newPoint.X;
        int y2 = newPoint.Y;

        //block "negative" selection
        if (x1 > x2)
        {
            x2 = x1;
        }
        if (y1 > y2)
        {
            y2 = y1;
        }

        //Draw a red rectangle
        g.FillRectangle(Brushes.Red, x1, y1, x2 - x1, y2 - y1);
    }

    private void ClearRegion(Graphics g)
    {
        int x1 = startPoint.X;
        int y1 = startPoint.Y;
        int x2 = oldPoint.X;
        int y2 = oldPoint.Y;

        if (x1 > x2)
        {
            x2 = x1;
        }
        if (y1 > y2)
        {
            y2 = y1;
        }

        //check left line
        if (newPoint.Y < y2)
        {
            Rectangle rectdst = new Rectangle(x1, newPoint.Y, 1, oldPoint.Y - newPoint.Y);
            g.DrawImage(bitmap, rectdst, rectdst, GraphicsUnit.Pixel);
        }

        //upper line
        if (newPoint.X < x2)
        {
            Rectangle rectdst = new Rectangle(newPoint.X, y1, oldPoint.X - newPoint.X, 1);
            g.DrawImage(bitmap, rectdst, rectdst, GraphicsUnit.Pixel);
        }

        //right line
        if (newPoint.X != x2 || newPoint.Y < y2)
        {
            int h = 0;
            int y = 0;
            if (newPoint.X == x2)
            {
                y = newPoint.Y;
                h = oldPoint.Y - newPoint.Y + 1;
            }
            else
            {
                y = startPoint.Y;
                h = oldPoint.Y - startPoint.Y + 1;
            }

            Rectangle rectdst = new Rectangle(oldPoint.X, y, 1, h);
            g.DrawImage(bitmap, rectdst, rectdst, GraphicsUnit.Pixel);
        }

        //bottom line
        if (newPoint.Y != y2 || newPoint.X < x2)
        {
            int w = 0;
            int x = 0;

            if (newPoint.Y == y2)
            {
                x = newPoint.X;
                w = oldPoint.X - newPoint.X + 1;
            }
            else
            {
                x = startPoint.X;
                w = oldPoint.X - x1 + 1;
            }

            Rectangle rectdst = new Rectangle(x, oldPoint.Y, w, 1);
            g.DrawImage(bitmap, rectdst, rectdst, GraphicsUnit.Pixel);
        }
    }

public class LayeredWindow : Form
{
    public new event PaintEventHandler Paint;

    public LayeredWindow()
    {
        SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
        StartPosition = FormStartPosition.Manual;
    }

    protected override CreateParams CreateParams
    {
        get
        {
            if (DesignMode) return base.CreateParams;
            CreateParams cParams = base.CreateParams;
            cParams.ExStyle = cParams.ExStyle | 0x80000;
            return cParams;
        }
    }

    private void PaintNative(Bitmap bitmap)
    {
        IntPtr hdcDestination = Win32.GetDC(IntPtr.Zero);
        IntPtr hdcSource = Win32.CreateCompatibleDC(hdcDestination);
        IntPtr hdcBitmap = IntPtr.Zero;
        IntPtr previousBitmap = IntPtr.Zero;

        try
        {
            hdcBitmap = bitmap.GetHbitmap(Color.FromArgb(0));
            previousBitmap = Win32.SelectObject(hdcSource, hdcBitmap);

            Size size = new Size(bitmap.Width, bitmap.Height);
            Point source = new Point(0, 0);
            Point destination = new Point(Left, Top);
            Win32.BLENDFUNCTION blendFunc = new Win32.BLENDFUNCTION()
            {
                BlendOp = Win32.AC_SRC_OVER,
                BlendFlags = 0,
                SourceConstantAlpha = 50,
                AlphaFormat = Win32.AC_SRC_ALPHA
            };

            Win32.UpdateLayeredWindow(Handle, hdcDestination, ref destination, ref size, hdcSource, ref source, 0, ref blendFunc, 2);
        }
        finally
        {
            Win32.ReleaseDC(IntPtr.Zero, hdcDestination);
            if (hdcBitmap != IntPtr.Zero)
            {
                Win32.SelectObject(hdcSource, previousBitmap);
                Win32.DeleteObject(hdcBitmap);
            }
            Win32.DeleteDC(hdcSource);
        }
    }

    public new void Invalidate()
    {
        using (Bitmap bitmap = new Bitmap(ClientSize.Width, ClientSize.Height))
        {
            using (Graphics graphics = Graphics.FromImage(bitmap))
            {
                if (Paint != null)
                    Paint(this, new PaintEventArgs(graphics, Rectangle.Empty));
            }
            PaintNative(bitmap);
        }
    }
}

public sealed class Win32
{
    [DllImport("user32.dll")]
    public static extern bool HideCaret(IntPtr hWnd);

    [DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true, CallingConvention = CallingConvention.Winapi)]
    public static extern short GetKeyState(int keyCode);

    [DllImport("user32.dll")]
    public static extern bool RegisterHotKey(IntPtr hWnd, int id, uint fsModifiers, uint vk);

    [DllImport("user32.dll")]
    public static extern bool UnregisterHotKey(IntPtr hWnd, int id);

    [DllImport("gdi32.dll", SetLastError = true)]
    public static extern IntPtr CreateCompatibleDC(IntPtr hdc);

    [DllImport("user32.dll")]
    public static extern IntPtr GetDC(IntPtr hWnd);

    [DllImport("gdi32.dll", ExactSpelling = true, PreserveSig = true, SetLastError = true)]
    public static extern IntPtr SelectObject(IntPtr hdc, IntPtr hgdiobj);

    [DllImport("user32.dll")]
    public static extern bool ReleaseDC(IntPtr hWnd, IntPtr hDC);

    [DllImport("gdi32.dll")]
    public static extern bool DeleteDC(IntPtr hdc);

    [DllImport("gdi32.dll")]
    public static extern bool DeleteObject(IntPtr hObject);

    [DllImport("user32.dll", ExactSpelling = true, SetLastError = true)]
    public static extern bool UpdateLayeredWindow(IntPtr hwnd, IntPtr hdcDst, ref Point pptDst, ref Size psize, IntPtr hdcSrc, ref Point pptSrc, uint crKey, [In] ref BLENDFUNCTION pblend, uint dwFlags);

    [DllImport("user32.dll")]
    public static extern IntPtr GetDesktopWindow();

    [DllImport("user32.dll")]
    public static extern IntPtr GetWindowDC(IntPtr ptr);

    [DllImport("gdi32.dll")]
    public static extern IntPtr CreateCompatibleBitmap(IntPtr hdc, int nWidth, int nHeight);

    [DllImport("gdi32.dll")]
    public static extern bool BitBlt(IntPtr hdcDest, int xDest, int yDest, int wDest, int hDest, IntPtr hdcSource, int xSrc, int ySrc, CopyPixelOperation rop);

    [DllImport("user32.dll")]
    public static extern IntPtr GetForegroundWindow();

    [DllImport("user32.dll")]
    public static extern bool GetWindowRect(IntPtr hWnd, out RECT rect);

    public const byte AC_SRC_OVER = 0;
    public const byte AC_SRC_ALPHA = 1;
    public const byte ULW_ALPHA = 2;

    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    public struct BLENDFUNCTION
    {
        public byte BlendOp;
        public byte BlendFlags;
        public byte SourceConstantAlpha;
        public byte AlphaFormat;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct RECT
    {
        public int Left;
        public int Top;
        public int Right;
        public int Bottom;
    }

}

ソリューションのダウンロード: http://www.mediafire.com/?b9ql4pzh69u10n4

4

3 に答える 3

3

MouseMoveイベントからそれを引き出す必要があります。一部のコンピューター/解像度では、コントロール全体の無効化が遅くなります。

アップデート:

このコードは、高度に最適化された方法で、選択した領域の四角形を描画してクリアします。

ルーチンは に四角形を描画しますがMouseMove、必要に応じて線の断片のみを消去します。つまり、領域が以前のサイズよりも小さい場合、行の外側部分のみが格納されたビットマップからクリアされます。

このため、ダブルバッファリングを有効にする必要はありません。

ウィンドウのバッファリングされたビットマップをソース コピーとして正しい位置に取得するには、ちょっとしたトリックを実装する必要があります。

  • Panel(この例では panFill) をフォームに追加し、ドッキングを fill に設定します。
  • フォームではなく、すべてのコントロール/背景画像をパネルに追加します

Panelこれで、後で必要なビットマップを の代わりにからコピーできますForm。これが必要な理由は、 を使用this.DrawBitmap()すると境界線とタイトル バーもビットマップに描画されるため、それは望ましくないからです。を使用するPanelと、これが解消されます。

フォームのクラスのグローバル スコープで、次のように設定します。

Bitmap bmp = null;
bool inDrag = false;
Point regStart = Point.Empty;
Point regNew = Point.Empty;
Point regOld = Point.Empty;

パネルの MouseDown/Up イベント:

private void panFill_MouseDown(object sender, MouseEventArgs e)
{
    //Create a bitmap
    bmp = new Bitmap(panFill.ClientSize.Width, _
                     panFill.ClientSize.Height, _
                     Imaging.PixelFormat.Format32bppPArgb);

    panFill.DrawToBitmap(bmp, panFill.ClientRectangle);

    //store origo/start point and mark that we're active
    regStart = e.Location;
    inDrag = true;
}

private void panFill_MouseUp(object sender, MouseEventArgs e)
{
    inDrag = false;

    //we're done, clean up resources if any
    if (bmp != null) {
        bmp.Dispose();
        bmp = null; //use as marker for this check
    }

    //clean up by redrawing panel
    Rectangle r = new Rectangle(regStart, _
                                new Size(regOld.X - regStart.X + 1, _
                                         regOld.Y - regStart.Y + 1));
    panFill.Invalidate(r, true);

}

このMouseMoveイベントでは、clear を呼び出して、新しい四角形を描画します。

private void panFill_MouseMove(object sender, MouseEventArgs e)
{
    if (inDrag) {

        using (Graphics g = panFill.CreateGraphics) {
            g.SmoothingMode = Drawing2D.SmoothingMode.None;

            //we store new pos. here as it's used to calculate
            //delta for what we need to redraw
            regNew = e.Location;
            ClearRegion(g);

            regOld = regNew;
            DrawRegion(g);
            }
        }
}

長方形を描画する関数は非常に単純です。

private void DrawRegion(Graphics g)
{
    int x1 = regStart.X;
    int y1 = regStart.Y;
    int x2 = regNew.X;
    int y2 = regNew.Y;

    //block "negative" selection
    if (x1 > x2) {
        x2 = x1;
    }
    if (y1 > y2) {
        y2 = y1;
    }

    //Draw a red rectangle
    g.DrawRectangle(Pens.Red, x1, y1, x2 - x1, y2 - y1);
}

次のメソッドは、必要なものだけを描画するように最適化されたすべてのマジックを保持します。これは、origo からの 2 つのメイン ラインをチェックし、長さが縮んでいるかどうかを確認することによって実現されます。その場合、古い位置と新しい位置のデルタを計算し、「ギャップ」のみを再描画します。

他の 2 つの線についても、接線の位置が同じ場合は同じことを行い、そうでない場合は線全体を再描画します。

private void ClearRegion(Graphics g)
{
    int x1 = regStart.X;
    int y1 = regStart.Y;
    int x2 = regOld.X;
    int y2 = regOld.Y;

    if (x1 > x2) {
        x2 = x1;
    }
    if (y1 > y2) {
        y2 = y1;
    }

    //check left line
    if (regNew.Y < y2) {
        Rectangle rectdst = new Rectangle(x1, regNew.Y, 1, regOld.Y - regNew.Y);
        g.DrawImage(bmp, rectdst, rectds, GraphicsUnit.Pixel);
    }

    //upper line
    if (regNew.X < x2) {
        Rectangle rectdst = new Rectangle(regNew.X, y1, regOld.X - regNew.X, 1);
        g.DrawImage(bmp, rectdst, rectdst, GraphicsUnit.Pixel);
    }

    //right line
    if (regNew.X != x2 || regNew.Y < y2) {
        int h = 0;
        int y = 0;
        if (regNew.X == x2) {
            y = regNew.Y;
            h = regOld.Y - regNew.Y + 1;
        } else {
            y = regStart.Y;
            h = regOld.Y - regStart.Y + 1;
        }

        Rectangle rectdst = new Rectangle(regOld.X, y, 1, h);
        g.DrawImage(bmp, rectdst, rectdst, GraphicsUnit.Pixel);
    }

    //bottom line
    if (regNew.Y != y2 || regNew.X < x2) {
        int w = 0;
        int x = 0;

        if (regNew.Y == y2) {
            x = regNew.X;
            w = regOld.X - regNew.X + 1;
        } else {
            x = regStart.X;
            w = regOld.X - x1 + 1;
        }

        Rectangle rectdst = new Rectangle(x, regOld.Y, w, 1);
        g.DrawImage(bmp, rectdst, rectdst, GraphicsUnit.Pixel);

    }
}

これは、フォーム上で GDI+ から得られる最大のものです。さまざまなアプローチには、DirectX とより低レベルのものが含まれていました。必要に応じて再描画されるものを制限することで、速度が最適化されます。

また、塗りつぶしパネルのビットマップ コピーは、アルファが事前に乗算されるため、最速のタイプである PARG であることに注意してください。この場合は必要ありませんが、スムージング モードを none に設定して、エイリアシングが漏れることなく線がシャープに保たれるようにしています。

Windows XP 32 ビット、2 GB メモリ、および Atom CPU では、これはスムーズに実行されます。

注: 元のコードは VB で記述され、C# に変換されています。エラーが発生する場合があります。必要に応じて調整してください。必要に応じてハンドラーを追加します。

興味のある方のための元の VB コード:

Public Class Form1

    Private bmp As Bitmap = Nothing
    Private inDrag As Boolean = False
    Private regStart As Point = Point.Empty
    Private regNew As Point = Point.Empty
    Private regOld As Point = Point.Empty

    Public Event RegionSelected(r As Rectangle)

    Private Sub panfill_MouseDown(sender As Object, e As System.Windows.Forms.MouseEventArgs) Handles panFill.MouseDown

        bmp = New Bitmap(panFill.ClientSize.Width, panFill.ClientSize.Height, Imaging.PixelFormat.Format32bppPArgb)
        panFill.DrawToBitmap(bmp, panFill.ClientRectangle)

        regStart = e.Location

        inDrag = True

    End Sub
    Private Sub panFill_MouseMove(sender As Object, e As System.Windows.Forms.MouseEventArgs) Handles panFill.MouseMove

        If inDrag Then
            Using g As Graphics = panFill.CreateGraphics

                g.SmoothingMode = Drawing2D.SmoothingMode.None

                regNew = e.Location

                ClearRegion(g)

                regOld = regNew

                DrawRegion(g)

            End Using

        End If

    End Sub
    Private Sub panFill_MouseUp(sender As Object, e As System.Windows.Forms.MouseEventArgs) Handles panFill.MouseUp

        inDrag = False

        If bmp IsNot Nothing Then
            bmp.Dispose()
            bmp = Nothing
        End If

        Dim r As New Rectangle(regStart, New Size(regOld.X - regStart.X + 1, regOld.Y - regStart.Y + 1))
        panFill.Invalidate(r, True)

        RaiseEvent RegionSelected(r)

    End Sub
    Private Sub DrawRegion(g As Graphics)

        Dim x1, y1, x2, y2 As Integer
        x1 = regStart.X
        y1 = regStart.Y
        x2 = regNew.X
        y2 = regNew.Y

        If x1 > x2 Then
            x2 = x1
        End If
        If y1 > y2 Then
            y2 = y1
        End If

        g.DrawRectangle(Pens.Red, x1, y1, x2 - x1, y2 - y1)

    End Sub
    Private Sub ClearRegion(g As Graphics)

        Dim x1, y1, x2, y2 As Integer

        x1 = regStart.X
        y1 = regStart.Y
        x2 = regOld.X
        y2 = regOld.Y

        If x1 > x2 Then
            x2 = x1
        End If
        If y1 > y2 Then
            y2 = y1
        End If

        'left line
        If regNew.Y < y2 Then

            Dim rectdst As New Rectangle(x1, regNew.Y, 1, regOld.Y - regNew.Y)
            g.DrawImage(bmp, rectdst, rectdst, GraphicsUnit.Pixel)

        End If

        'upper line
        If regNew.X < x2 Then

            Dim rectdst As New Rectangle(regNew.X, y1, regOld.X - regNew.X, 1)
            g.DrawImage(bmp, rectdst, rectdst, GraphicsUnit.Pixel)

        End If

        'right line
        If regNew.X <> x2 OrElse regNew.Y < y2 Then

            Dim h, y As Integer
            If regNew.X = x2 Then
                y = regNew.Y
                h = regOld.Y - regNew.Y + 1
            Else
                y = regStart.Y
                h = regOld.Y - regStart.Y + 1
            End If

            Dim rectdst As New Rectangle(regOld.X, y, 1, h)
            g.DrawImage(bmp, rectdst, rectdst, GraphicsUnit.Pixel)

        End If

        'bottom line
        If regNew.Y <> y2 OrElse regNew.X < x2 Then

            Dim w, x As Integer
            If regNew.Y = y2 Then
                x = regNew.X
                w = regOld.X - regNew.X + 1
            Else
                x = regStart.X
                w = regOld.X - x1 + 1
            End If

            Dim rectdst As New Rectangle(x, regOld.Y, w, 1)
            g.DrawImage(bmp, rectdst, rectdst, GraphicsUnit.Pixel)

        End If

    End Sub

End Class
于 2012-11-18T06:29:21.920 に答える
2

わかりました アレックス、あなたが要求したコード例をここに示します。

まず、自分でペイントするコントロールにいくつかのスタイルを設定する必要があります。これは、DoubleBuffered、AllPaintingInWmPaint、および UserPaint です。これにより、ちらつきが回避されます (この例を参照してください)。次に、MouseMove イベント自体をペイントしないでください。ペイントに必要なすべてのデータを記憶し、Invalidate を呼び出します。すべての描画はオーバーライド メソッド「Control.OnPaint」またはイベント「Cotrol.Paint」で行います。また、RegionForm を再度閉じることができるように、KeyPress ハンドラーも追加しました。

MouseDown イベントでは、マウス カーソルの位置とコントロールの現在の外観 (ビットマップ) を覚えています。MouseMove 中、マウス ダウン位置と現在のマウス カーソル位置の間のベクトル (距離) を計算します。これは、ユーザーがマウス ダウン ポイントの左上に移動した場合にも機能します。この四角形は、描画する必要がある新しい領域です。ただし、古いリージョンも無効にする必要があります。これが、古い領域と新しい領域の両方を含む結合四角形を計算する理由です。この共用体は、「Control.Invalidate」を呼び出すために使用されます。古い領域をクリアするには、保存されたイメージの特定の部分のみを描画するだけです。

public sealed partial class RegionForm : Form
{
    private Bitmap bitmap;
    private bool mouseDown;
    private Rectangle newRegion;
    private Rectangle oldRegion;
    private Point startPoint;

    public RegionForm()
    {
        InitializeComponent();
        Location = Screen.PrimaryScreen.Bounds.Location;
        Size = Screen.PrimaryScreen.Bounds.Size;
        Cursor = Cursors.Cross;
        TopMost = true;
        ShowInTaskbar = false;

        this.SetStyle(
            ControlStyles.UserPaint |
            ControlStyles.AllPaintingInWmPaint |
            ControlStyles.DoubleBuffer, true);
    }

    protected override void OnKeyPress(KeyPressEventArgs e)
    {
        base.OnKeyPress(e);

        if (e.KeyChar == (char)Keys.Escape)
            this.Close();
    }

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

        bitmap = new Bitmap(this.ClientSize.Width,
                    this.ClientSize.Height,
                    PixelFormat.Format32bppPArgb);

        this.DrawToBitmap(bitmap, this.ClientRectangle);
        startPoint = e.Location;
        mouseDown = true;
    }

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

        mouseDown = false;

        if (bitmap != null)
        {
            bitmap.Dispose();
            bitmap = null;
        }

        // reset regions
        newRegion = Rectangle.Empty;
        oldRegion = Rectangle.Empty;

        // invalidate all
        Invalidate(true);
    }

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

        if (mouseDown)
        {
            // calculate new region
            var vector = Point.Subtract(e.Location, new Size(startPoint));
            newRegion = new Rectangle(System.Math.Min(startPoint.X, e.Location.X), System.Math.Min(startPoint.Y, e.Location.Y), System.Math.Abs(vector.X), System.Math.Abs(vector.Y));

            // invalidate only the area of interest
            var invalidate = Rectangle.Union(oldRegion, newRegion);
            Invalidate(invalidate, true);
        }
    }

    protected override void OnPaint(PaintEventArgs e)
    {
        base.OnPaint(e);

        e.Graphics.SmoothingMode = SmoothingMode.None;

        ClearRegion(e.Graphics, bitmap, oldRegion);
        DrawRegion(e.Graphics, newRegion);

        // remember which region has been handled
        oldRegion = newRegion;
    }

    static void DrawRegion(Graphics g, Rectangle region)
    {
        if (g == null || region == Rectangle.Empty)
            return;

        //Draw a red rectangle
        g.FillRectangle(Brushes.Red, region);
    }

    static void ClearRegion(Graphics g, Bitmap bitmap, Rectangle region)
    {
        if (g == null || region == Rectangle.Empty || bitmap == null)
            return;

        // take only the selected region from the original image and draw that part
        g.DrawImage(bitmap, region, region, GraphicsUnit.Pixel);
    }
于 2013-01-14T11:44:46.840 に答える
0

これは、フォームに表示されるコンテンツに大きく依存します。たとえば、自分でたくさんのオブジェクトを描画している場合、マウス移動イベントごとに完全なフォームを無効にする必要があります。

それ以外の場合は、特定のリージョンのみを無効にする必要があります。次に、その領域を背景色でクリアし、その領域に完全または部分的に配置されているすべてのオブジェクトを呼び出して再描画する必要があります。その後、DragDrop カーソル/グラフィックを描画できます。

ほとんどの場合、無効化された領域にあるオブジェクトを特定するのは複雑すぎます。

于 2013-01-06T14:58:45.970 に答える