I'm trying to recreate a Winform application in C# that has the same functionality as the snipping tool windows provides. That is, allowing the user to drag a rectangle over the desktop and capture what ever is inside as an image.
At the moment I only have the ability to draw a rectangle with the mouse, and that's within the winform. Can anyone point me in the direction of how to do it so I can do it within the whole desktop?
My code for drawing the rectangle is as follows:
Rectangle rect;
public Form1()
{
InitializeComponent();
// set the cursor to be a + sign
this.Cursor = System.Windows.Forms.Cursors.Cross;
this.DoubleBuffered = true;
}
private void Form1_MouseDown(object sender, MouseEventArgs e)
{
// e.X and e.Y are used to get the X and Y pos of the mouse
rect = new Rectangle(e.X, e.Y, 0, 0);
this.Invalidate();
}
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
// draw rectangle as mouse moves
rect = new Rectangle(rect.Left,rect.Top, e.X - rect.Left, e.Y - rect.Top);
}
this.Invalidate();
}
private void Form1_Paint(object sender, PaintEventArgs e)
{
// Replace "Color.Red" with any color and repalce "2" with any size you like.
using (Pen pen = new Pen(Color.Red, 2))
{
e.Graphics.DrawRectangle(pen, rect);
}
}
}
I've been looking around online, but my searches haven't provided anything of use yet.
Any help would be greatly appreciated.
See:
http://www.codeproject.com/Articles/485883/Create-your-own-Snipping-Tool
And:
http://www.codeproject.com/Articles/21913/TeboScreen-Basic-C-Screen-Capture-Application
as references of Snipping tool creation.
Hope this helps.
Related
1.I'm trying to scale a rectangle while a user does mouse up and mouse down on the picture box.
2.After doing 5 mouse wheel up and if you do mouse wheel down the rectangle still keeps scales up(expands).
3.Any solution to it?
GraphicsPath path=new GraphicsPath();
private float scale=1.0F;
private bool ActiveWheel=false;
public Form1()
{
path.AddRectangle(new Rectangle(10,10,50,100));
}
private void PictureBox1_Paint(object sender,PaintEventArgs e)
{
if(ActiveWheel)
{
ActiveWheel=false;
ScaleRectangle(e);
}
else
{
e.Graphics.DrawPath(Pens.Red,path);
}
}
private void PictureBox1_MouseWheel(object sender,MouseEventArgs e)
{
ActiveWheel=true;
scale=Math.Max(scale+Math.Sign(e.Delta)*0.1F,0.1F);
pictureBox1.Refresh();
}
}
private void ScaleRectangle(PaintEventArgs e)
{
var matrix=new Matrix();
matrix.Scale(scale,scale,MatrixOrder.Append);
path.Transform(matrix);
e.Graphics.DrawPath(Pens.Blue,path);
}
Any solution or idea how to scale down or scale up a shape suddenly without a delay in between mouse wheel ups and mouse wheel downs(see 2. if want to see actually o/p).
Just adjust the scale value in the MouseWheel() event, then ScaleTransform() the GRAPHICS surface (not the Path itself) in the Paint() event and draw:
public partial class Form1 : Form
{
private GraphicsPath path = new GraphicsPath();
private float scale = 1.0F;
public Form1()
{
InitializeComponent();
path.AddRectangle(new Rectangle(10, 10, 50, 100));
pictureBox1.MouseWheel += PictureBox1_MouseWheel;
pictureBox1.Paint += pictureBox1_Paint;
}
private void PictureBox1_MouseWheel(object sender, MouseEventArgs e)
{
scale = Math.Max(scale + Math.Sign(e.Delta) * 0.1F, 0.1F);
pictureBox1.Invalidate();
}
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
e.Graphics.ScaleTransform(scale, scale);
e.Graphics.DrawPath(Pens.Red, path);
}
}
--- EDIT ---
It scales entirely, could you show me how to scale only graphics path
object and top, left have to be fixed, meaning without scaling the
top, left point?
In that case, translate to the top left point of your rectangle, scale, then translate back to the origin. Now draw your UNCHANGED rectangle:
private void PictureBox1_Paint(object sender, PaintEventArgs e)
{
e.Graphics.TranslateTransform(10, 10);
e.Graphics.ScaleTransform(scale, scale);
e.Graphics.TranslateTransform(-10, -10);
e.Graphics.DrawPath(Pens.Red, path);
}
If you have other elements that are being drawn at different positions and/or scales, then you can reset the graphics surface before and after (each "element" can do the same type of thing to position itself and scale itself):
private void PictureBox1_Paint(object sender, PaintEventArgs e)
{
// possibly other drawing operations
e.Graphics.ResetTransform();
e.Graphics.TranslateTransform(10, 10);
e.Graphics.ScaleTransform(scale, scale);
e.Graphics.TranslateTransform(-10, -10);
e.Graphics.DrawPath(Pens.Red, path);
e.Graphics.ResetTransform();
// possibly other drawing operations
}
This approach is nice as it keeps the original information about your rectangle; the change is simply visual.
I am trying to create a small paint application in Visual Studio 2015. My project falls into the category of Windows Form Applications. I have the following problem:
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
if (a == 1)
{
if (r == 1 || el == 1)
{
int x = Math.Min(inX, e.X);
int y = Math.Min(inY, e.Y);
int width = Math.Max(inX, e.X) - Math.Min(inX, e.X);
int height = Math.Max(inY, e.Y) - Math.Min(inY, e.Y);
rect = new Rectangle(x, y, width, height);
Refresh();
}
else if (l == 1)
{
ep = e.Location;
Refresh();
}
else
{
ep = e.Location;
g = this.CreateGraphics();
g.DrawLine(p, sp, ep);
sp = ep;
}
}
}
This part of my codes creates a Rectangular (2nd if), a line segment(3rd if) and just a line. It works pretty much the same as MS Paint; the rectangular or the line segment isn not completed until the user releases the left mouse click (Mouse up). But when a rectangular is finally made when I try again to create another one, the form refreshes ( Refresh(); ) and I lose all the previous drawn rectangulars or lines. I tried replacing Refresh(); with Invalidate(rect); and Update();, but I do not get the result I want.
Instead, I get this:
You should do all your drawing to a separate Bitmap "buffer" that you keep around. Draw your shapes to that bitmap, then when the screen actually needs to be updated, draw the buffer to the screen.
Also, anytime you call Graphics.FromImage you need to remember to Dispose, or it will leak resources like crazy.
Incredibly simple example
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Windows.Forms;
namespace DrawExample
{
public partial class Form1 : Form
{
private Bitmap _canvas; //This is the offscreen drawing buffer
private Point _anchor; //The start point for click-drag operations
private Rectangle? _ghost;
private Brush _ghostBrush;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
_ghostBrush = new SolidBrush(Color.FromArgb(200, 200, 200, 255)); //This creates a slightly blue, transparent brush for the ghost preview
ResizeCanvas();
}
private void Form1_Resize(object sender, EventArgs e)
{
ResizeCanvas();
}
/// <summary>
/// Resizes the offscreen bitmap to match the current size of the window, it preserves what is currently in the bitmap.
/// </summary>
private void ResizeCanvas()
{
Bitmap tmp = new Bitmap(this.Width, this.Height, PixelFormat.Format32bppRgb);
using (Graphics g = Graphics.FromImage(tmp))
{
g.Clear(Color.White);
if (_canvas != null)
{
g.DrawImage(_canvas, 0, 0);
_canvas.Dispose();
}
}
_canvas = tmp;
}
private void Form1_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
_anchor = new Point(e.X, e.Y);
}
}
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
_ghost = new Rectangle(_anchor.X, _anchor.Y, e.X - _anchor.X, e.Y - _anchor.Y);
this.Invalidate();
}
}
private void Form1_MouseUp(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
//Create a Graphics for the offscreen bitmap
using (Graphics g = Graphics.FromImage(_canvas))
{
Rectangle rect = new Rectangle(_anchor.X, _anchor.Y, e.X - _anchor.X, e.Y - _anchor.Y);
g.FillRectangle(Brushes.White, rect);
g.DrawRectangle(Pens.Black, rect);
}
_ghost = null;
//This queues up a redraw call for the form
this.Invalidate();
}
}
private void Form1_Paint(object sender, PaintEventArgs e)
{
if (_ghost.HasValue)
{
using (Bitmap tmp = new Bitmap(_canvas))
{
using (Graphics g = Graphics.FromImage(tmp))
{
g.FillRectangle(_ghostBrush, _ghost.Value);
g.DrawRectangle(Pens.Black, _ghost.Value);
e.Graphics.DrawImage(tmp, 0, 0);
}
}
}
else
{
e.Graphics.DrawImage(_canvas, 0, 0);
}
}
//This stops the flickering
protected override void OnPaintBackground(PaintEventArgs e)
{
//Do nothing
}
}
}
You are drawing directly onto the form's drawing surface. That surface is not persistent. It lasts until the next paint cycle.
Instead you should:
Draw to an offscreen bitmap.
Draw that bitmap onto, for instance, a picture box control. Or paint it directly onto the form's drawing surface in its Paint event.
I'm trying to code a draggable rectangle zoombox that's transparent, once the mouse is up again, it zooms into that area and deletes the drawn rectangle.
I've got the zoom working and drawing the rectangle, however I can't 1) Figure out how to make it transparent, and 2) Figure out how to delete the rectangle after it's zoomed in. It gets deleted again once the mouse is clicked down to draw another zoombox on the zoomed in image (I'm drawing a fractal) but I can't figure out what to write to get it to delete after zooming in.
Paint
private void Form1_Paint(object sender, PaintEventArgs e)
{
Graphics windowG = e.Graphics;
windowG.DrawImageUnscaled(picture, 0, 0);
if (rectangle == true)
{
e.Graphics.FillRectangle(Brushes.Aquamarine, rec);
}
if (rectangle == false)
{
Invalidate();
}
}
Mouse Down
rectangle = true;
if (e.Button == MouseButtons.Left)
{
rec = new Rectangle(e.X, e.Y, 0, 0);
Invalidate();
}
Mouse up
{
rectangle = false;
}
Mouse Move
if (e.Button == MouseButtons.Left)
{
rec.Width = e.X - rec.X;
rec.Height = e.Y - rec.Y;
Invalidate();
}
At first I thought you need this:
This is one of the very rare cases where your drawing should not be done in the Paint event but in the MouseMove using a Graphics object created with CreateGraphics.
The reason why this is the right way here is that for this kind of interactive rubberband drawing you do not want the drawing to persist. Other examples would be a line preview or a cursor cross.
To make the Rectangle transparent you can either
Use DrawRectangle
or use a semi-transparent color and FillRectangle
or use both as in the example below:
Here is the code you need:
Point mDown = Point.Empty;
private void Form1_MouseDown(object sender, MouseEventArgs e)
{
mDown = e.Location; // note the first corner
}
private void Form1_MouseUp(object sender, MouseEventArgs e)
{
Invalidate(); // clear the rectangle
}
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == System.Windows.Forms.MouseButtons.Left)
using (Graphics G = this.CreateGraphics() ) // !!! usually wrong !!!
{
G.Clear(BackColor); // Invalidate();
Rectangle rect = rectangle(mDown, e.Location);
// either
using (SolidBrush brush = new SolidBrush(Color.FromArgb(32, 255, 0, 0)))
G.FillRectangle(brush, rect);
// or
G.DrawRectangle(Pens.Red, rect);
}
}
This is a function that lets you start drawing any any corner, not just top left:
Rectangle rectangle (Point p1, Point p2)
{
int x = Math.Min(p1.X, p2.X);
int y = Math.Min(p1.Y, p2.Y);
int w = Math.Abs(p1.X - p2.X);
int h = Math.Abs(p1.Y - p2.Y);
return new Rectangle(x, y, w, h);
}
Note that if you have a BackgroundImage this above code won't work so well.
But now I think this is closer to your situation:
In this case we go back to the normal way and draw things in the Paint but only as long as the mouse button is pressed. As we don't have the mouse parameters here we need another class level Point and also use the Control.MouseButtons property:
Point mCurrent = Point.Empty;
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
mCurrent = e.Location;
Invalidate();
}
private void Form1_MouseUp(object sender, MouseEventArgs e)
{
mDown = Point.Empty;
mCurrent = Point.Empty;
Invalidate();
}
private void Form1_Paint(object sender, PaintEventArgs e)
{
if (Control.MouseButtons == System.Windows.Forms.MouseButtons.Left)
{
Rectangle rect = rectangle(mDown, mCurrent);
// either one..
using (SolidBrush brush = new SolidBrush(Color.FromArgb(32, 255, 0, 0)))
e.Graphics.FillRectangle(brush, rect);
// ..or both of these
e.Graphics.DrawRectangle(Pens.Red, rect);
}
}
So to sum it up: Besides quite a few details your main problem is missing the check for the mouse button in the paint event..
I have a picture box and I draw a string on it by DrawString(). I change position of the string by scrolling a TrackBar. But I want to move the string by directly clicking on the string and then dragging. It'll be easier for any user. Can anybody help me achieve this?
Edit: I already move my pictureBox1 my mouse click:
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
e.Graphics.DrawImage(img, 0, 0);
e.Graphics.DrawString(str, font, new SolidBrush(color), new PointF(NinjaClass.NINJA.pointX, NinjaClass.NINJA.pointY));
}
private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
x = e.X;
y = e.Y;
}
}
private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
pictureBox1.Left += (e.X - x);
pictureBox1.Top += (e.Y - y);
}
}
Using DrawString is not very convenient for such a task, you have to save a Rectangle around the string, update that rectangle according to the mouse movement ... If we need to click exactly on the string curve to move the string, using DrawString can't help. In such a case we have to use a GraphicsPath which supports a little hittesting. However in this case we just allow user to click on the string bounds, because clicking on the string curve with small font or even normal font is not easy and very annoying indeed. Try the following code:
//your form constructor
public Form1(){
InitializeComponent();
//add string to the GraphicsPath, the string location is initialized with (10,10)
gp.AddString("Your string goes here", Font.FontFamily,
(int)Font.Style, 20, new Point(10, 10), StringFormat.GenericDefault);
}
GraphicsPath gp = new GraphicsPath();
float dx, dy;
//the Paint event handler for your pictureBox1
private void pictureBox1_Paint(object sender, PaintEventArgs e) {
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
gp.Transform(new Matrix(1, 0, 0, 1, dx, dy));//Translate and paint
e.Graphics.FillPath(Brushes.Red, gp);
gp.Transform(new Matrix(1,0,0,1,-dx,-dy));//translate back (reset to old location)
}
Point downPoint;
bool hitOn;
//MouseDown event handler for your pictureBox1
private void pictureBox1_MouseDown(object sender, MouseEventArgs e){
if(e.Button == MouseButtons.Left){
downPoint = e.Location;
if (gp.GetBounds(new Matrix(1,0,0,1,dx,dy)).Contains(e.Location)) {
gp.Transform(new Matrix(1, 0, 0, 1, dx, dy));
hitOn = true;
}
}
}
//MouseMove event handler for your pictureBox1
private void pictureBox1_MouseMove(object sender, MouseEventArgs e) {
if (e.Button == MouseButtons.Left) {
if(hitOn){
dx = e.X - downPoint.X;
dy = e.Y - downPoint.Y;
pictureBox1.Invalidate();
} else {
pictureBox1.Left += e.X - downPoint.X;
pictureBox1.Top += e.Y - downPoint.Y;
}
}
}
//MouseUp event handler for your pictureBox1
private void pictureBox1_MouseUp(object sender, MouseEventArgs e) {
hitOn = false;
}
Update: For using a transparent backColor Label: There is a note that when you drag and drop a label on a pictureBox at design time, the Parent of the label will be the pictureBox container not the PictureBox, that's by design, because PictureBox is not intended to contain any control. So you have to set the Parent using code, for the code moving the label, you do similarly to what you do with your PictureBox, the difference is the parent of PictureBox is your form while the parent of the label is your pictureBox:
public Form1(){
InitializeComponent();
label1.BackColor = Color.Transparent;
label1.Parent = pictureBox1;
//try this to prevent a little flicker, but looks like it does not help much
typeof(Control).GetProperty("DoubleBuffered", System.Reflection.BindingFlags.NonPublic |
System.Reflection.BindingFlags.Instance)
.SetValue(pictureBox1, true, null);
}
Point lblDownPoint;
//MouseDown event handler for your label1
private void label1_MouseDown(object sender, MouseEventArgs e){
if(e.Button == MouseButtons.Left) lblDownPoint = e.Location;
}
//MouseMove event handler for your label1
private void label1_MouseMove(object sender, MouseEventArgs e){
if(e.Button == MouseButtons.Left) {
label1.Left += e.X - lblDownPoint.X;
label2.Top += e.Y - lblDownPoint.Y;
}
}
However after trying using a transparent BackColor label instead, I can see that it's fairly worse (caused by flicker) than draw directly on the pictureBox as the previous code does. You should consider to choose between them yourself, the previous code seems a little complicated (but not really if you understand it).
I am trying to code in .Net to select an area and the area will be where ever in the form, that is not restricted to a control and asusually the area appears like a square by dragging using mouse at runtime on the form and if on clicking on a button for ex "Save" i.e., saving the selected area and when the next time the page loads that particular area should be clickable.Right now i have no idea where to start but if some idea is given it will be more helpful to me to develop some code for this functionality. Does any one give a small idea that how can I do this functionality, so that I can proceed developing code?
This is not my code ... Select area
public partial class Form1 : Form
{
bool canResize;
int mX, mY;
Rectangle rect = new Rectangle();
public Form1()
{
InitializeComponent();
}
private void Form1_Paint(object sender, PaintEventArgs e)
{
e.Graphics.DrawRectangle(new Pen(Brushes.Purple, 2), rect);
}
private void Form1_MouseDown(object sender, MouseEventArgs e)
{
canResize = true;
mX = e.X; mY = e.Y;
rect.Location = new Point(mX, mY);
}
private void Form1_MouseUp(object sender, MouseEventArgs e)
{
canResize = false;
}
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
if (canResize)
{
if (mX < e.X)
{
rect.X = mX;
rect.Width = e.X - mX;
}
else
{
rect.X = e.X;
rect.Width = mX - e.X;
}
if (mY < e.Y)
{
rect.Y = mY;
rect.Height = e.Y - mY;
}
else
{
rect.Y = e.Y;
rect.Height = mY - e.Y;
}
this.Invalidate();
}
}
}
Whether you are programming in WinForms or WPF, you can always catch mouseup and mousedown events, and their respective on-screen locations.
You could then ask every element on the stage if any of its corners are within the rectangular area between mouse-up and mouse-down point.