c# Drawing square to highlight mouse selection area - c#

I am building screen capture for part of desktop app with c#. Z keyup event to start drawing red rectangle as mouse move to next point then Z keyup again to stop drawing red box.
The problem is that it will run very slow or crash in slow computers like laptops. I did something wrong and I tried to fix it for a month but it didn't work. Help me, plz. Also, is there a better way or a library to solve it?
private void step1()
{
start = true;
mypic.pics = mypic.capturepic(Cursor.Position);
Bmp = new Bitmap(mypic.pics);
timer1.Enabled = true;
timer1.Start();
}
private void draw_rec_repeat()
{
InvalidateRect(IntPtr.Zero, IntPtr.Zero, true);
IntPtr desktopPtr = GetDC(IntPtr.Zero);
using (System.Drawing.Graphics gg = System.Drawing.Graphics.FromHdc(desktopPtr))
{
Rectangle bb = DrawRec(Cursor.Position.X, Cursor.Position.Y, desktopPtr, gg);
gg.DrawRectangle(new Pen(Color.Red, 3), bb);
}
ReleaseDC(this.Handle, desktopPtr);
}
private void timer1_Tick(object sender, EventArgs e)
{
draw_rec_repeat();
}

C# How to Draw a Rubber Band Selection Rectangle on Panel, like one used in Windows Explorer?
It helped. The screen was saved then pasted into a maximized picturebox. Then I draw rectangles onto double-buffered picturebox control.
Also, I tried to use customize timer.interval so slow laptop users could slow down the draw time.
That's about it.

Related

Confused about why two Panels work differently

I have a problem that is really confusing me. Let me lay some background. I am trying to develop my own Editor Control. I wish to have a blinking caret, I know I can do this using CreateCaret, ShowCaret ect but this is not how I wish to do it, I wish to implement this myself. My caret does not blink and I cant understand why.
The way I'm trying to implement this is by caching the area below the caret and then display the caret. Then half a second later I repaint the cached data back to the Editor Control therefore effecting a flashing caret. I have tried using just a Graphics Object and a Bitmap for the cache neither worked but I think I know why. So I decided to experiment. I set up one Panel on the Form itself through the Designer and one is handed coded into the Caret class itself. The Panel on the Form works but the Panel encapsulated within the class doesn't and I don't know why. My question is why?
Below is the Code. The BlinkTimer_Tick method just causes the Blink. Paint just Paints the caret, none of this should be hard to understand.
BackupBackground copies the area below the Caret to the cache, while RestoreBackground copies the cache to the Control. Now the problem is if you comment out the lines commented with "Works if this line is commented out" in both methods it all works but when these are not commented it doesn't work the caret does not blink. Both these Panels are set up the same.
private void BlinkTimer_Tick(object sender, EventArgs e)
{
Paint();
_BlinkTimer.Start();
}
private void BackupBackground(Graphics SrcGraph)
{
Form TF = _Parent.FindForm() as Form;
Panel P = TF.Controls["_TestPanel"] as Panel;
P = _Buffer; // Works if this line is Commentted out
Graphics DestGraph = P.CreateGraphics();
IntPtr SrcHDC = SrcGraph.GetHdc();
IntPtr DestHDC = DestGraph.GetHdc();
BitBlt(DestHDC, 0, 0, _Size.Width, _Size.Height,
SrcHDC, _Location.X, _Location.Y, TernaryRasterOperations.SRCCOPY);
DestGraph.ReleaseHdc(DestHDC);
SrcGraph.ReleaseHdc(SrcHDC);
}
private void RestoreBackground(Graphics DestGraph)
{
Form TF = _Parent.FindForm() as Form;
Panel P = TF.Controls["_TestPanel"] as Panel;
P = _Buffer; // Works if this line is Commentted out
Graphics SrcGraph = P.CreateGraphics();
IntPtr SrcHDC = SrcGraph.GetHdc();
IntPtr DestHDC = DestGraph.GetHdc();
BitBlt(DestHDC, _Location.X, _Location.Y, _Size.Width, _Size.Height,
SrcHDC, 0, 0, TernaryRasterOperations.SRCCOPY);
DestGraph.ReleaseHdc(DestHDC);
SrcGraph.ReleaseHdc(SrcHDC);
}
internal void Paint()
{
Graphics Graph = _Parent.CreateGraphics();
if (!_BlinkOn)
{
// Restore Graphics from Backup
RestoreBackground(Graph);
_BlinkOn = true;
}
else
{
// Backup Graphics
Graph.Flush();
BackupBackground(Graph);
// Draw Caret
using (SolidBrush P = new SolidBrush(Color.Black))
{
Graph.FillRectangle(P, new Rectangle(_Location, _Size));
}
_BlinkOn = false;
}
}
The Caret should flash in both circumstances as I change nothing really but it only flashes when I use the Panel on the Form.
Note: I do not intend to use a Panel for the cache I was just experimenting and found this behavior and its weird, so I need to know.
Thanks Danny.

scrolling in a ScrollableControl: is there a .NET variant of MFC ViewPort?

I'm learning how to draw in Winforms. I've created a Form, with a panel with scrollbars. Upone Event Paint I draw an ellipse. This is fairly straightforward:
this.panel1.AutoScroll = true;
this.panel1.AutoScrollMinSize = = new System.Drawing.Size(500, 300);
private void OnPaint(object sender, PaintEventArgs e)
{
Rectangle ellipse = new Rectangle(Point.Empty, new Size(400, 400));
ellipse.Offset(this.panel1.AutoScrollPosition);
using (Pen myPen = new System.Drawing.Pen(System.Drawing.Color.Red))
{
e.Graphics.DrawEllipse(myPen, ellipse);
}
}
private void OnPanelScroll(object sender, ScrollEventArgs e)
{
this.panel1.Invalidate();
}
This works fine, but the complete image is redrawn when resizing or scrolling the panel.
A long time ago, in the time of MFC there was the notion of ViewPort / SetViewPortOrg / mapping modes / etc.. Scrolling and resizing did not require a recalculation of the complete image. Once you had drawn the image you didn't have to redraw as long as the complete image was not changed. All you had to do was move the viewport, or change the mapping mode
Does .NET have something similar that can do the scrolling for me? Maybe I should not draw on a panel, but on another subclass of a ScrollableControl?

Efficiently moving a WinForms button on a form with a background image

I am creating an application for an industrial touch screen computer with no hardware to brag about. The operator of this touch screen computer is among other things supposed to be able to unlock and drag buttons around on a form with a background image.
However, as many of you already might know, moving controls on a parent control with a background image isn't pretty. The dragging is slow and instead of experiencing a smooth dragging, the operator will see a button jumping after through hoops in the wake of the mouse pointer as you move the pointer across the screen.
This is the current code for moving the button:
private void btnButton_MouseMove(object sender, MouseEventArgs e)
{
// Do not proceed unless it is allowed to move buttons
if (!this._AllowButtonsToBeMoved)
return;
if (this._IsBeingDragged)
{
var btn = (sender as Button);
var newPoint = btn.PointToScreen(new Point(e.X, e.Y));
newPoint.Offset(this._ButtonOffset);
btn.Location = newPoint;
}
}
I am not looking to solve this exact problem, I'd rather eliminate it to save some time. What I wish to implement in order to eliminate this, is a more resource efficient way to move the box around. I'm thinking that moving a dotted rectangle instead of the button, then dropping it where I want the button must be way more efficient than dragging the button around the screen, causing who knows how many repaint operations.
Does anyone have any better suggestions? If not, then I would very much appreciate pointers on how to proceed with creating and moving this rectangle around the screen, as I am having some difficulty finding good sources of information regarding how to approach this on good ol' Google.
Update, 26/11/13
I'm attempting Luaan's suggestion regarding overriding the form's OnPaint, however I am unsure as to how exactly I can add the rendering of the button in this code. Any ideas?
protected override void OnPaint(PaintEventArgs e)
{
if (_IsBeingDragged)
{
e.Graphics.DrawImage(this._FormPaintBuffer, new Point(0, 0));
}
else
{
base.OnPaint(e);
}
}
This is a standard case of Winforms being too programmer-friendly. Details that any game programmer pays careful attention to but are way too easy to miss. It allows you to set a BackgroundImage and it will take anything you throw at it. That usually works just fine, except when you need the image to render quickly. Like you do in this case.
Two things you need to do to make it draw ~fifty times quicker:
Resize the bitmap yourself to fit the form's ClientSize so it doesn't have to be done repeatedly every time the image needs to be painted. The default Graphics.InterpolationMode property value produces very nice looking images but it is not cheap. Do note that this can take a significant memory hit, the reason it isn't done automatically.
Pay attention to the pixel format of the image. There's only one that draws fast, the one that can be blitted directly to the video adapter without having the value of every single pixel converted to the frame buffer format. That is PixelFormat.Format32bppPArgb on all video adapters in use in the past 10+ years. Big difference, it is ten times faster than all the other ones. You never get that format out of a bitmap that you created with a painting program so explicitly converting it is required. Again heed the memory hit.
It takes but a little scrap of code to get both:
private static Bitmap Resample(Image img, Size size) {
var bmp = new Bitmap(size.Width, size.Height,
System.Drawing.Imaging.PixelFormat.Format32bppPArgb);
using (var gr = Graphics.FromImage(bmp)) {
gr.DrawImage(img, new Rectangle(Point.Empty, size));
}
return bmp;
}
In the somewhat unlikely case you still have painting trails or stuttering you need to consider a very different approach. Like the one that's used in the Winforms designer. You use layers, an extra borderless transparent window on top of the original. You get one with the TransparencyKey property.
1) If you want to keep everything the way it is, you might want to implement some sort of double buffering for the background. When the form is redrawn, it always has to redraw pretty much the whole thing, while actually doing some logic (ie. JPG/PNG is slower to draw than a BMP). If you store the Paint canvas in a Bitmap, you can draw the whole form except for that one button in a Bitmap and draw only that as background while you're dragging the button - this way you get around all the draw logic of the form and its controls, which should be vastly faster.
2) You can only draw an outline of the button being moved. The trick is that you draw lines in XOR mode - the first time you draw the rectangle it adds the outline, and the next time you draw it in the same location, it disappears. This way you don't have to redraw the form all the time, just the few pixels that form the rectangle. The support for XOR lines in C# isn't the best, but you can use the ControlPaint.DrawReversibleLine method.
To drag a control you need to use the .DrawToBitmap function of the control and set the appropriate styles of the form. I haven't done it in the sample code but you'll need a "design mode" and a "normal mode". To drag the control you simply click it, drag it and click again. You can get fancy and make the Bitmap holding the control transparent so as to accommodate rounded edges.
For this example, make a standard C# Windows Forms application (Form1) and drop a button (button1) onto the form then place this code after the Form1 constructor in your Form class. Make sure to change the location of the background bitmap in code.
private Bitmap b = null;
private bool IsDragging = false;
private Point down = Point.Empty;
private Point offset = Point.Empty;
private void button1_MouseUp(object sender, MouseEventArgs e)
{
IsDragging = true;
button1.Visible = false;
down = button1.PointToScreen(e.Location);
offset = e.Location;
this.Invalidate();
}
private void Form1_MouseUp(object sender, MouseEventArgs e)
{
if (IsDragging)
{
IsDragging = false;
down = new Point(down.X - offset.X, down.Y - offset.Y);
button1.Location = down;
button1.Visible = true;
down = Point.Empty;
this.Invalidate();
}
}
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
if (IsDragging)
{
down.X += (e.X - down.X);
down.Y += (e.Y - down.Y);
this.Invalidate();
}
}
private void Form1_Load(object sender, EventArgs e)
{
try
{
b = new Bitmap(button1.Width, button1.Height, System.Drawing.Imaging.PixelFormat.Format32bppPArgb);
button1.DrawToBitmap(b, new Rectangle(0, 0, button1.Width, button1.Height));
button1.MouseUp += new MouseEventHandler(button1_MouseUp);
this.MouseUp += new MouseEventHandler(Form1_MouseUp);
this.MouseMove += new MouseEventHandler(Form1_MouseMove);
this.DoubleBuffered = true;
this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.UserPaint, true);
this.UpdateStyles();
this.BackgroundImage = Image.FromFile(#"C:\Users\Public\Pictures\Sample Pictures\desert.jpg");
this.BackgroundImageLayout = ImageLayout.Stretch;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
protected override void OnPaint(PaintEventArgs e)
{
if (IsDragging)
{
e.Graphics.DrawImage(b, new Point(down.X - offset.X, down.Y - offset.Y));
}
base.OnPaint(e);
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
if (b != null)
{
b.Dispose();
}
}

PictureBox onPaint Clears Every Control

I've been having problems with onPaint event handler for a pictureBox. I've simplified the code, so right now I'm trying to achieve this: load the bitmap from path into pictureBox1 and draw a small rectangle around mouse cursor when moving over the pictureBox1.
private int mouseX;
private int mouseY;
private String path;
public Form1()
{
InitializeComponent();
path = "images\\image.jpg";
}
private void Form1_Load(object sender, EventArgs e)
{
pictureBox1.Image = new Bitmap(path);
}
private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
mouseX = e.X;
mouseY = e.Y;
}
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
e.Graphics.DrawRectangle(Pens.Black, mouseX - 10, mouseY - 10, 20, 20);
pictureBox1.Invalidate();
}
When I run the application the bitmap is loaded successfully and the rectangle is being drawn nicely wherever I drag the mouse. Anyway there are two problems:
1) When the Form is loaded everything is white but pictureBox until I move the window somewhere. It means menuStrip, toolStrip eg is cleared with white color. When I add pictureBox.Invalidate(); into Form1_Load it seems to take care of that problem (everything is loaded and visible) but on the toolStrip there are some buttons and comboBoxes that are interacting, changing its visibility and this still causes problems. I'd like to reduce the funcionality of the Paint only on pictureBox.
2) The rectangle is drawn the moment the Form is loaded despite the mouse cursor didn't enter pictureBox. Also when cursor leaves the pictureBox the last drawn rectangle stays there. I've tried to remove it but I couldn't figure it out.
Thanks for any suggestions, it seems to me that something fundamental is missing there but don't know what.
2) MouseMove works only when cursor is over picture box. Try to use MouseEnter and MouseLeave in order to handle when to start drawing rectangle and when to stop doing that.

Imitating MSpaints drawing methods

For graphics exercises and some self-improvement sorta stuff, i've decided to basically just mess around and try and recreate some of the functionality of paint within a winform. I've got a lot of the standard ones to work, for example paint can, drawing dots around the cursor, free hand draw and what not, however i'm a little puzzled as to how paint does the mid-drawing animations. For example;
To draw a simple line i can simply get mouse coordinates on MouseUp and MouseDown events, and use the graphics class to draw a line between the two.
However, on MSpaint whilst drawing a line, you get almost a "preview" of the line after you click the first point, and whilst dragging it to the second point the line follows your cursor, but i'm a little stuck as to how this would be done? Does it involve constant redrawing of the line and the graphics device? It'd be great if someone could give me some hints / inner knowledge, i've had a search around the internet but can't REALLY find anything of use..
And very modern via ControlPaint.DrawReversibleLine method :)
Point? startPoint;
Point? endPoint;
private void Form_MouseDown(object sender, MouseEventArgs e)
{
startPoint = PointToScreen(e.Location);
}
private void Form_MouseMove(object sender, MouseEventArgs e)
{
if (!startPoint.HasValue)
return;
if (endPoint.HasValue)
ControlPaint.DrawReversibleLine(startPoint.Value, endPoint.Value, Color.White);
endPoint = PointToScreen(e.Location);
ControlPaint.DrawReversibleLine(startPoint.Value, endPoint.Value, Color.White);
}
private void Form_MouseUp(object sender, MouseEventArgs e)
{
startPoint = null;
endPoint = null;
}
Bitmap/raster software makes use of two memory buffers: one is the current "persisted" canvas that contains the pixels that the user has modified explicitly, the second is the framebuffer on the graphics card which is used to display the canvas on-screen.
Making the bitmap document appear on-screen is done by simply copying the raw bytes of the in-memory bitmap document to the framebuffer (if the framebuffer has a different byte-format or color-depth than the in-memory bitmap then you need to perform a conversion. GDI can do this for you if necessary, but let's just assume everything is 32-bit ARGB).
In WinForms, the framebuffer is exposed by the Graphics argument passed into your Control.OnPaint override.
You can implement these "preview" effects using one of two approaches:
Modern
The first approach is used today, and has been for the past 17 years or so (since Windows 95). The in-memory bitmap is copied to the framebuffer whenever the screen needs to be updated, such as a single mouse movement (of even 1px). The preview effect (such as the line the user would be painting once they release the mouse button) is then drawn on-top. The process is repeated as soon as the user's mouse moves again, so to update the preview.
You'd have something like this:
public class PaintingCanvas : Control {
private Bitmap _canvas = new Bitmap();
private Boolean _inOp; // are we in a mouse operation?
private Point _opStart; // where the current mouse operation started
private Point _opEnd; // where it ends
public override void OnPaint(PaintEventArgs e) {
Graphics g = e.Graphics;
g.DrawImage( _canvas ); // draw the current state
if( _inOp ) {
// assuming the only operation is to draw a line
g.DrawLine( _opStart, _opEnd );
}
}
protected override OnMouseDown(Point p) {
_inOp = true;
_opStart = _opEnd = p;
}
protected override OnMouseMove(Point p) {
_opEnd = p;
this.Invalidate(); // trigger repainting
}
protected override OnMouseUp(Point p) {
using( Graphics g = Graphics.FromImage( _bitmap ) ) {
g.DrawLine( _opStart, _opEnd ); // a permanent line
}
_inOp = false;
}
}
1980s Flashblack
In ye olden days (think: 1980s), copying the bitmap from memory to the framebuffer was slow, so a surprisingly good hack was using XOR painting. The program assumes ownership of the framebuffer (so no overlapping windows would cause it need to be copied from memory). The preview line is drawn by performing an XOR of all of the pixels that the line would cover. This is fast because XORing the pixels means that their original colour can be restored without needing to recopy the pixels from memory. This trick was used in computers for many kinds of selection, highlight, or preview effect until recently.
highlightOrPreviewColor = originalPixelColor XOR (2^bpp - 1)
originalPixelColor = highlightOrPreviewColor XOR (2^bpp - 1)

Categories

Resources