Windows Forms Screenshot of whole screen, except the calling window? - c#

Just to take on a challenge, I decided to write an application to magnify only a particular section of the screen (under the mouse, following the mouse). The best way I can think to do this is to take a screenshot of the space under the form, enlarge, and paint on the form. I'm using this section of code to snap the picture inside a timer:
timer1.Stop();
//Reposition window
this.Left = Cursor.Position.X - this.Width / 2;
this.Top = Cursor.Position.Y - this.Height / 2;
//Bitmap to save image to
Bitmap bmpScreenshot = new Bitmap(this.Width, this.Height, PixelFormat.Format32bppArgb);
Graphics gfxScreenshot = Graphics.FromImage(bmpScreenshot);
//Grab section of screen shot
this.Opacity = 0.01;
gfxScreenshot.CopyFromScreen(this.Left, this.Top, 0, 0, this.Size, CopyPixelOperation.SourceCopy);
this.Opacity = 1;
//Save to class level so Paint can paint it
frame = bmpScreenshot;
//Make Double Buffered Panel repaint
dbPanel1.Invalidate();
timer1.Start();
In order to get the desktop (or whatever windows/contents are present) beneath the form, I set it's opacity to 0.01 (so that it's still clickable, which a click closes the app. Absolute 0 is not clickable) before taking the picture and then 1 after taking the snapshot... This creates a flicker. Is there a way I can get a bitmap of what's under the form without changing opacity or hiding/showing the form? I want a live image of what's under the form, not just a snapshot of when the app started.
I'm not looking for other apps that do this, I'm working on this so when I approach these problems I can learn a solution. I've tried changing the opacity (to all sorts of levels), using show and hide, SetVisibleCore(bool) on the window, but nothing lowers the flicker to an acceptable level. Any thoughts?

I would think you'd be better off displaying the magnified image offset from the cursor (look at the iPhone text selection/editing interface for example), and add some smart handling for the screen edges. That way you don't have to keep changing opacity, you just update the image and or placement of your form.

Related

How to keep form centered to the middle of the screen after resize

I have a form that is centered according to the screen position that I resize by fontsize when loading. After resizing the location remains the same as it was before resizing so the form is no longer in the center, like I would like.
Let me draw you a sketch:
I've tried calling
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
this.ResumeLayout(false);
this.PerformLayout();
again after resizing (this is, I believe, the part of the code that centers the form in the beginning). It didn't work. I've also found some similar issues like this:
"Keeping winform control centered after window resize
" but they always only deal with centring a control, not the form itself.
Add method for ResizeEnd event. In method, when ResizeEnd is fired, get current screen size (on multiple monitors, screen that contains current form) and then calculate form's position. Take a look at this example
private void Form1_ResizeEnd(object sender, EventArgs e)
{
Screen myScreen = Screen.FromControl(this);
Rectangle area = myScreen.WorkingArea;
this.Top = (area.Height - this.Height) / 2;
this.Left = (area.Width - this.Width) / 2;
}

Background Panel Windows Form

I hope you can help me with this problem, attached videos to explain in a simpler way.
First example
Panel (has a textured background) with labels (the labels have a png image without background)
Events: MouseDown, MouseUp and MouseMove.
As you will notice in the video to drag the label the background turns white panel and regains its background image when I stop dragging the label
Panel controls have a transparent background as property, but changing the background with any color, let the problem occurred related to the substance, I do not understand why this happens and how to fix less.
Second Example
Contains the above, with the only difference that the panel controls instead of having transparent background, I chose black color for that property
You have to use double buffer and you don't have to stop using an image on the background, you can have everything running smoothly.
You have a couple of ways to do this, the fast way (not enough most of the time) is to enable doublebuffer of the panel.
The "slow" but better way is to do your own Double Buffer using a Bitmap object as a buffer.
This example creates a "side buffer" and accepts an image as parameter and draws it using created buffer.
public void DrawSomething(Graphics graphics, Bitmap yourimage)
{
Graphics g;
Bitmap buffer = new Bitmap(yourimage.Width, yourimage.Height, graphics);
g = Graphics.FromImage(buffer);
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
g.DrawImage(yourimage, 0, 0);
graphics.DrawImage(buffer, 0, 0);
g.Dispose();
}
Call this on your OnPaint event.
BTW... this is just a double buffer example.
Cheers
Change DoubleBuffered to true for both form and panel. I think that should solve your problem.
this is totally normal, because System.Windows.Forms.Control based items were not designed to do this kind of advanced Graphics operations.
in fact the reason that this effect happens here, is that when you assign any value other than 255 to the alpha component of a control BackColor, the form does the following when you change the control size or position:
it sets the new control position
it redraws the parent control
it gets the background of the control's parent as an image
it draws acquired image into the control body to seem as if the control is transparent
the control body gets drawn on top of the previously drawn background
the control children are drawn
* this is is a simplified explanation for the sake of illustration to deliver the idea
steps 1, 2 are responsible for the flickering effect that you see.
but you have two ways to solve this,
-the first is some kinda advanced solution but it's very powerful, which is you would have to create a double buffered custom control that would be your viewport.
the second is to use WPF instead of windows forms, as WPF was designed exactly to do this kind of things.
if you can kindly provide some code, i can show you how to do both.

Redraw panel contents after ClientSizeChanged

So my application runs in fixed size window and in full screen. The problem I'm facing is how to properly scale the current contents of the panel (which depend on the application use) when the window is resized. This is my current code:
private void Form1_ClientSizeChanged(object sender, EventArgs e)
{
System.Drawing.Drawing2D.Matrix transformMatrix = new System.Drawing.Drawing2D.Matrix();
float px = panel2.Width;
float py = panel2.Height;
panel2.Width = this.Width / 2;
panel2.Height = panel2.Width;
panel2.Location = new Point(this.Width - panel2.Width - 30, 30);
transformMatrix.Scale(panel2.Width / px, panel2.Height / py);
panel2.Region.Transform(transformMatrix);
//Rest of the code
}
But the drawn content doesn't scale, and if I use Invalidate() or Refresh() the drawn content gets cleared (the panel is redrawn empty). What am I missing?
.NET doesn't remember what's drawn on the panel, as simple as that. As soon as anything invalidates the windows bitmap buffer (causing a WM_PAINT), it's going to be repainted again. So, you have to draw what you want to draw using the Paint event (or overriding OnPaint).
However, there is another way that might be easier to implement - don't paint into a Panel. Instead, paint into a PictureBox (or rather, a Bitmap assigned to the Image property of the PictureBox). The Bitmap will be reused when invalidating (and redrawing) the picture box, so nothing will be lost. By using PictureBox.ScaleMode, you can define how you want the picture box to scale the bitmap, and it will do so as well as it can.
In any case, transforming the Region property doesn't do anything useful - you're simply changing the region, not doing anything to the drawing itself. To use 2D transformation matrices, you want to apply them on a Graphics object during the drawing (in Paint handler or OnPaint override) - drawing anything on the Graphics object will then transform everything you're trying to draw, which in your case means scaling the painting.
So you have to decide: do you want to just scale a stored bitmap with the painted image, or do you want to redraw it all from scratch (which also means you can pick any level of detail you can provide)?
I think that you're mistaking what the Region property is meant for. According to the MSDN docs (empasis mine, replace 'window' with 'control' when reading):
The window region is a collection of pixels within the window where the operating system permits drawing. The operating system does not display any portion of a window that lies outside of the window region. The coordinates of a control's region are relative to the upper-left corner of the control, not the client area of the control.
All that you're doing is changing the region that the OS will allow painting, which explains why you're not seeing anything. I think that you should be resizing the control when the form is resized, either through Anchor, or through my preference of Dock with several controls, or a panel like TableLayoutPanel where it will handle scaling and relative sizing for you.
Thank you for your answers, but I wrote my own function and logic that serves the purpose for this application. Basically the function checks for the state of the application variables, and calls the appropriate function that originally drew the content, and since those functions use the panel width and height as arguments they properly scale the drawn content and retain the drawing composition.
P.S. I'll accept Luaan's answers since it offers a valid alternative and is complete.

using WinForms Dock with MaximumSize; I want it centered not top left

I have a PictureBox docked with Fill inside a larger control. The PictureBox is set to scale my image, but I don't want to scale the image larger than the original. Hence, my PictureBox has a maximum size set. As long as the container is smaller than the picture box, the image is fine. As the container expands beyond the maximum size of the PicutreBox, it is obvious that the picture box is tied to the top left. I would rather have the box centered vertically and horizontally in the parent. How do I make the Dock behavior fill from center rather than top left?
Use the Layout Anchor property of the PictureBox. You need to set it to "Top, Left, Bottom, Right" instead of using Dock.Fill. You can set this in the property window for the PictureBox:
Advantage of anchors against docking: the (in this case PictureBox) container can be positioned everywhere, but still be relative to other components in the parent panel/container. You can do this using Dock.Fill only with Layouts (different panels).
I think what you are looking for is the Anchor style of None, which will make the control "float" in the middle of the control. The catch though is that now you have to "initially" center it yourself:
PictureBox pb = new PictureBox();
pb.SizeMode = PictureBoxSizeMode.AutoSize;
pb.Anchor = AnchorStyles.None;
pb.Image = bmp;
pb.Location = new Point((this.ClientSize.Width / 2) - (pb.Width / 2),
(this.ClientSize.Height / 2) - (pb.Height / 2));
this.Controls.Add(pb);
this.AutoScrollMinSize = pb.Size;
After further thought, I solved the issue with this line of code:
_box.SizeChanged += (sender, args) => _box.SizeMode = _box.Width < _cross.Width || _box.Height < _cross.Height ? PictureBoxSizeMode.Zoom : PictureBoxSizeMode.CenterImage;

c# - clear surface when resizing

I'm trying to build my own custom control for a windows forms application in C#.Net. Currently I paint some rectangles and other graphic elements using the paint event.
When I now resize the app form to fit the desktop size, all elements are repainted (which is exactly the behaviour I need) but the old one's are shown in the background.
Here's what I'm doing by now:
Pen penDefaultBorder = new Pen(Color.Wheat, 1);
int margin = 5;
private void CustomControl_Paint(object sender, PaintEventArgs e) {
CustomControl calendar = (CustomControl)sender;
Graphics graphics = e.Graphics;
graphics.Clear(Color.WhiteSmoke);
graphics.DrawRectangle(penDefaultBorder, margin, margin, calendar.Width - margin * 2, calendar.Height - margin * 2);
//...
}
Neither the graphics.Clear, nor adding a graphics.FillRectangle(...) will hide the old rectangle from the surface.
Ideas? Thank you all.
Paint events usually don't request an update for the entire canvas, just the area specified in the PaintEventArgs. I'm guessing what's happening is that only the newly-exposed regions of the canvas are being passed in the PaintEventArgs.
This one of the reasons that you shouldn't do any rendering in the Paint event. You should render to an offscreen bitmap - a buffer - and copy from that buffer to the control's canvas in the Paint event.
Searching for "double buffering" here or on Google will give you many examples of the technique.
Have you tried .Invalidate() to cause the form to redraw?

Categories