2

I have found this question (as a few others), but this is the one I have implemented so far:

Crosshair cursor with additional lines in C#

As it states, I can use a stock cursor "cross" directly in the IDE. This is a really good way to do things. The answer specified in the answer above draws a cross on the screen at the given width / height. Eg:

private Cursor crossCursor(Pen pen, Brush brush, int x, int y)
{
    var pic = new Bitmap(x, y);
    Graphics gr = Graphics.FromImage(pic);

    var pathX = new GraphicsPath();
    var pathY = new GraphicsPath();
    pathX.AddLine(0, y / 2, x, y / 2);
    pathY.AddLine(x / 2, 0, x / 2, y);
    gr.DrawPath(pen, pathX);
    gr.DrawPath(pen, pathY);

    IntPtr ptr = pic.GetHicon();
    var c = new Cursor(ptr);
    return c;
}

My issue is that I want my cross hairs to extend to the Bounds of the viewing area. To provide context here, I have:

//Form
  //TableLayoutPanel
      //UserControl (fills the TableLayoutPanel visible area)

So how can I adjust my cursor so that the lines extend (much like in CAD pacakages)?

Thanks.

Update: I have tried calling the method from here:

protected override void OnLoad(System.EventArgs e)
{
    Cursor = crossCursor(Pens.WhiteSmoke, Brushes.WhiteSmoke, Bounds.Width, Bounds.Height);
}

But it is not Ok because at this point in time Bounds is returning a dimension of 150 by 150 which is not the size of the TableLayoutPanel.

Update: I have adjuted it to use the Resize handler instead and it does improve things:

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

    Cursor = crossCursor(Pens.WhiteSmoke, Brushes.WhiteSmoke, Bounds.Width, Bounds.Height);
}

The only problem now (and it kind of makes sense I suppose) is that the cursor will only take the full width and height of the view when it is central to the view. As soon as I move about in the view that cursor does not adjust. I always want a horizontal/vertical line through the mouse position (not just the initial cross).

See:

Crosshairs

The crosshairs need extending (the thicker red lines). Either I need to constantly create the cursor as the mouse moves or construct the two lines another way. What to do?

I came across this:

https://social.msdn.microsoft.com/Forums/vstudio/en-US/7bdbad6d-1f65-461b-8f0c-6ef4f243fa6b/crosshair-cursor-using-c?forum=csharpgeneral

So, instead of changing the cursor object I now draw lines in the controls MouseMove handler:

Region r = new Region();
r.Union(new Rectangle(0, lastY, this.Width, 1));
r.Union(new Rectangle(lastX, 0, 1, this.Height));
this.Invalidate(r);
this.Update();
Graphics g = Graphics.FromHwnd(this.Handle);
g.DrawLine(Pens.White, 0, e.Y, this.Width, e.Y);
g.DrawLine(Pens.White, e.X, 0, e.X, this.Height);
int intDiameter = 20;//the diameter of this circle
g.DrawEllipse(Pens.White, e.X - intDiameter / 2, e.Y - intDiameter / 2, 20, 20);
//to draw the circle
lastX = e.X;
lastY = e.Y;

It works, but I get noticiable screen flicker doing it this way.

Community
  • 1
  • 1
Andrew Truckle
  • 17,769
  • 16
  • 66
  • 164
  • Looks like that is exactly what it should do. You should be passing `Bounds.Width` for X and `Bounds.Height` for Y. – DonBoitnott May 31 '16 at 11:21
  • @DonBoitnott Thanks, please see my updated question. – Andrew Truckle May 31 '16 at 11:28
  • 1
    You should hook into a `Resize` event instead of a `Load` event. That way, you not only get the right `Bounds` the first time, but it can update every time the form is resized. – DonBoitnott May 31 '16 at 11:44
  • @DonBoitnott See updated notes. – Andrew Truckle May 31 '16 at 11:54
  • Is it a custom painted control? – Reza Aghaei Jun 03 '16 at 19:26
  • The user control has an embedded Teigha view. But, I wonder if the region is correct? We only need to invalidate the two lines and then draw the new ones. We don't need to invalidate complete rectangles. Thoughts? – Andrew Truckle Jun 04 '16 at 18:00
  • @AndrewTruckle The solution depends on what you are drawing. Are you drawing some shapes like CAD? – Reza Aghaei Jun 04 '16 at 20:10
  • @RezaAghaei is there a way for me to email you provately so that I can show you a complete class? Then I can explain in a bit more detail where I am at. – Andrew Truckle Jun 04 '16 at 20:12
  • @AndrewTruckle Yes, to contact me send an email to r.aghaei at outlook.com. But I think describing the situation here in a way which be helpful for all future readers is more useful :) Also surely I will not read the whole project and codes ;) – Reza Aghaei Jun 04 '16 at 20:16
  • @AndrewTruckle I read your email and read code of control. I couldn't see any reason about why you don't use `DoubleBuffered`. Use `DoubleBuffered` also another thing that you can use as an idea to decrease rendering time and have flicker free drawing: You can draw shapes/drawings or whatever you are drawing on a bitmap then draw the bitmap on design surface of your control. How can this be useful? This way you can simply draw rubber-band rectangle or the cross over your control without rendering all shapes/drawings again. To render shapes/drawings it's enough to render that bitmap. – Reza Aghaei Jun 05 '16 at 15:32
  • Also when your shapes changed, you can redraw the bitmap. Use this idea only if you have flicker even after setting `DoubleBuffered`. – Reza Aghaei Jun 05 '16 at 15:33
  • In a drawing application which its goal was drawing high level plan for estates and passages, I used only double-buffering to have flicker-free drawing and it worked properly. – Reza Aghaei Jun 05 '16 at 15:37
  • @RezaAghaei Hi. My problem is I can't work out how to draw the _pDevice onto that bitmap. – Andrew Truckle Jun 05 '16 at 15:58
  • I posted an answer which draws a cross and also a rectangle by mouse down and mouse move. Let me know if this is what you are looking for. – Reza Aghaei Jun 05 '16 at 23:01

1 Answers1

2

You don't need to create a cursor. You can create a double buffered control and draw cross over control.

using System;
using System.Drawing;
using System.Windows.Forms;
public class DrawingSurface : Control
{
    Pen crossPen;
    Pen rectanglePen;
    Brush rectangleBrush;

    public DrawingSurface()
    {
        this.DoubleBuffered = true;
        this.ResizeRedraw = true;
        crossPen = new Pen(Color.Red, 2);
        rectangleBrush = new SolidBrush(Color.FromArgb(50, Color.Blue));
        rectanglePen = new Pen(Color.Blue, 1);
    }
    bool mouseDown = false;
    Point startPoint = Point.Empty;
    Point endPoint = Point.Empty;
    protected override void OnMouseDown(MouseEventArgs e)
    {
        startPoint = e.Location;
        mouseDown = true;
        base.OnMouseDown(e);
    }
    protected override void OnMouseUp(MouseEventArgs e)
    {
        mouseDown = false;
        base.OnMouseUp(e);
    }
    protected override void OnMouseMove(MouseEventArgs e)
    {
        endPoint = e.Location;
        this.Invalidate();
        base.OnMouseMove(e);
    }
    protected override void OnPaint(PaintEventArgs e)
    {
        var g = e.Graphics;

        if (this.ClientRectangle.Contains(endPoint))
            DrawCross(e.Graphics, endPoint);

        if (mouseDown)
            DrawRectangle(e.Graphics, startPoint, endPoint);
    }

    void DrawCross(Graphics g, Point point)
    {
        g.DrawLine(crossPen, new Point(0, point.Y), new Point(Width, point.Y));
        g.DrawLine(crossPen, new Point(point.X, 0), new Point(point.X, Height));
    }
    void DrawRectangle(Graphics g, Point point1, Point point2)
    {
        var rectangle = new Rectangle(
            Math.Min(point1.X, point2.X), Math.Min(point1.Y, point2.Y),
            Math.Abs(point1.X - point2.X), Math.Abs(point1.Y - point2.Y));

        g.FillRectangle(rectangleBrush, rectangle);
        g.DrawRectangle(rectanglePen, rectangle);
    }
    protected override void Dispose(bool disposing)
    {
        crossPen.Dispose();
        rectanglePen.Dispose();
        rectangleBrush.Dispose();
        base.Dispose(disposing);
    }
}

enter image description here

Reza Aghaei
  • 120,393
  • 18
  • 203
  • 398
  • Thanks. In principle what you have said it right. But the issues I had was because of the internal view object which I have now rectified. So now I know how to properly render the internal view object onto the bitmap (with whatever graphics). All is now good. :) – Andrew Truckle Jun 06 '16 at 09:51
  • You are welcome. It seems you are you are in the right direction :) – Reza Aghaei Jun 06 '16 at 09:53