Simulate Windows Dragging Effect in a Winform FlowLayoutPanel - c#

I'm currently simulating the windows multiple selection rectangle when the user is dragging the mouse. To synchronize our understanding, this picture shows the effect I want to simulate:
Now I want to simulate this effect on a FlowLayoutPanel with some controls inside.
So far I am managed to get the effect almost done:
What I did here was putting a unfocused border-less semi-transparent (half the opacity) form on top the main form. To get the border simulated, I handled SizeChanged and Paint to draw the border.
However, this solution sometimes flickers, as in the owner border couldn't get cleared on-time:
I have tried using double buffering on the cover form by setting DoubleBuffer to true, and override CreateParam to set WM_EX_COMPOSITED, but neither works.
My question is: How to reduce this artifact?
Thanks a lot!
My code:
For the cover form:
public partial class CoverForm : Form
{
public CoverForm()
{
InitializeComponent();
BackColor = Color.CadetBlue;
FormBorderStyle = FormBorderStyle.None;
SizeChanged += (s, e) => Invalidate();
Paint += (s, e) =>
{
e.Graphics.Clear(BackColor);
using (var pen = new Pen(Color.DodgerBlue))
{
e.Graphics.DrawRectangle(pen, 1, 1, Size.Width - 2, Size.Height - 2);
}
};
}
protected override bool ShowWithoutActivation
{
get { return true; }
}
}
For the main form:
public Form1()
{
InitializeComponent();
// mainPanel is the panel that simulates the dragging effect
mainPanel.MouseDown += (s, e) =>
{
_isMouseDown = true;
_startPosition = e.Location;
coverForm.Location = mainPanel.PointToScreen(e.Location);
coverForm.Show();
};
mainPanel.MouseUp += (s, e) =>
{
_isMouseDown = false;
coverForm.Hide();
};
mainPanel.MouseMove += CoverPanelMouseMoveHandler;
DoubleBuffered = true;
}
~Form1()
{
if (coverForm != null && !coverForm.IsDisposed)
{
coverForm.Dispose();
}
}
# region Dragging Effect
private void CoverPanelMouseMoveHandler(object sender, MouseEventArgs e)
{
if (_isMouseDown)
{
_curPosition = e.Location;
// find the dragging rectangle
var rect = CreateRect(_curPosition, _startPosition);
coverForm.Size = rect.Size;
coverForm.Location = mainPanel.PointToScreen(rect.Location);
foreach (Control control in mainPanel.Controls)
{
// logic to get button backcolor changed
}
mainPanel.Invalidate(true);
}
}
Update
I have tried to override OnPaint and put my drawing there, but it gave even worse result: the old paints wouldn't get erased:
Code I modified for cover form:
public partial class CoverForm : Form
{
public CoverForm()
{
InitializeComponent();
BackColor = Color.CadetBlue;
FormBorderStyle = FormBorderStyle.None;
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
e.Graphics.Clear(BackColor);
using (var pen = new Pen(Color.FromArgb(255, 0, 0, 255)))
{
e.Graphics.DrawRectangle(pen, 0, 0, Size.Width - 1, Size.Height - 1);
}
}
protected override bool ShowWithoutActivation
{
get { return true; }
}
}
Update 2
Actually the problem I am facing is about drawing above a FlowLayoutPanel, not a normal Panel. The reason I put Panel before was I was seeking answer for my flickering 2-layers design. But since someone approach the problem by adding control to the panel to get it drawn above all controls, I would like to point this out: adding control to a panel would be trivial, but FlowLayoutPanel will auto-align the newly added control to the next available position, which may screw up the expected effect.

Video Demo of the Solution: Remember to Switch to 1080p
Recorded in a VM on a crappy machine. So kinda slow.
You are getting those artifacts because you're doing a combination of 3 things all at once.
The two big ones are moving the form to another location and resizing the form. It also doesn't help if the form is semi transparent :) To get a better understanding of what I mean, just open VS2013 up and resize the window very quickly (at the top-left corner, and run in random directions really fast), you will see that around the edges it can't keep up. And yes, you will get different results when you're resizing from a different position around the window (just think about it for a minute and you will figure it out).
Aybe, provided a pretty clever solution but it doesn't allow you to see through it or see if any updates to the panel....since it basically just copies the last output to a bitmap and uses that as a back buffer (much like what you assume someone might do when doing the selection thing in a paint program).
If you really want to do it with an overlay form and keep it semi-transparent then you will need to eliminate those three things if you don't want artifacts.
The code requires quite a bit of WIN32 knowledge.... lucky for you Microsoft has already done the hard part. We are going to enable per pixel transparency in your cover frame by using the PerPixelAlphaForm by Microsoft (you can google it) I will paste the code here. It basically just creates a Window with a Style of WS_EX_LAYERED. Keeps a Backbuffer which is AlphaBlended with the screen (simple huh?).
/******************************** Module Header ********************************\
Module Name: PerPixelAlphaForm.cs
Project: CSWinFormLayeredWindow
Copyright (c) Microsoft Corporation.
This source is subject to the Microsoft Public License.
See http://www.microsoft.com/opensource/licenses.mspx#Ms-PL.
All other rights reserved.
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
\*******************************************************************************/
#region Using directives
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
#endregion
namespace CSWinFormLayeredWindow
{
public partial class PerPixelAlphaForm : Form
{
public PerPixelAlphaForm()
{
InitializeComponent();
}
protected override CreateParams CreateParams
{
get
{
// Add the layered extended style (WS_EX_LAYERED) to this window.
CreateParams createParams = base.CreateParams;
createParams.ExStyle |= WS_EX_LAYERED;
return createParams;
}
}
/// <summary>
/// Let Windows drag this window for us (thinks its hitting the title
/// bar of the window)
/// </summary>
/// <param name="message"></param>
protected override void WndProc(ref Message message)
{
if (message.Msg == WM_NCHITTEST)
{
// Tell Windows that the user is on the title bar (caption)
message.Result = (IntPtr)HTCAPTION;
}
else
{
base.WndProc(ref message);
}
}
/// <summary>
///
/// </summary>
/// <param name="bitmap"></param>
public void SelectBitmap(Bitmap bitmap)
{
SelectBitmap(bitmap, 255);
}
/// <summary>
///
/// </summary>
/// <param name="bitmap">
///
/// </param>
/// <param name="opacity">
/// Specifies an alpha transparency value to be used on the entire source
/// bitmap. The SourceConstantAlpha value is combined with any per-pixel
/// alpha values in the source bitmap. The value ranges from 0 to 255. If
/// you set SourceConstantAlpha to 0, it is assumed that your image is
/// transparent. When you only want to use per-pixel alpha values, set
/// the SourceConstantAlpha value to 255 (opaque).
/// </param>
public void SelectBitmap(Bitmap bitmap, int opacity)
{
// Does this bitmap contain an alpha channel?
if (bitmap.PixelFormat != PixelFormat.Format32bppArgb)
{
throw new ApplicationException("The bitmap must be 32bpp with alpha-channel.");
}
// Get device contexts
IntPtr screenDc = GetDC(IntPtr.Zero);
IntPtr memDc = CreateCompatibleDC(screenDc);
IntPtr hBitmap = IntPtr.Zero;
IntPtr hOldBitmap = IntPtr.Zero;
try
{
// Get handle to the new bitmap and select it into the current
// device context.
hBitmap = bitmap.GetHbitmap(Color.FromArgb(0));
hOldBitmap = SelectObject(memDc, hBitmap);
// Set parameters for layered window update.
Size newSize = new Size(bitmap.Width, bitmap.Height);
Point sourceLocation = new Point(0, 0);
Point newLocation = new Point(this.Left, this.Top);
BLENDFUNCTION blend = new BLENDFUNCTION();
blend.BlendOp = AC_SRC_OVER;
blend.BlendFlags = 0;
blend.SourceConstantAlpha = (byte)opacity;
blend.AlphaFormat = AC_SRC_ALPHA;
// Update the window.
UpdateLayeredWindow(
this.Handle, // Handle to the layered window
screenDc, // Handle to the screen DC
ref newLocation, // New screen position of the layered window
ref newSize, // New size of the layered window
memDc, // Handle to the layered window surface DC
ref sourceLocation, // Location of the layer in the DC
0, // Color key of the layered window
ref blend, // Transparency of the layered window
ULW_ALPHA // Use blend as the blend function
);
}
finally
{
// Release device context.
ReleaseDC(IntPtr.Zero, screenDc);
if (hBitmap != IntPtr.Zero)
{
SelectObject(memDc, hOldBitmap);
DeleteObject(hBitmap);
}
DeleteDC(memDc);
}
}
#region Native Methods and Structures
const Int32 WS_EX_LAYERED = 0x80000;
const Int32 HTCAPTION = 0x02;
const Int32 WM_NCHITTEST = 0x84;
const Int32 ULW_ALPHA = 0x02;
const byte AC_SRC_OVER = 0x00;
const byte AC_SRC_ALPHA = 0x01;
[StructLayout(LayoutKind.Sequential)]
struct Point
{
public Int32 x;
public Int32 y;
public Point(Int32 x, Int32 y)
{ this.x = x; this.y = y; }
}
[StructLayout(LayoutKind.Sequential)]
struct Size
{
public Int32 cx;
public Int32 cy;
public Size(Int32 cx, Int32 cy)
{ this.cx = cx; this.cy = cy; }
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct ARGB
{
public byte Blue;
public byte Green;
public byte Red;
public byte Alpha;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct BLENDFUNCTION
{
public byte BlendOp;
public byte BlendFlags;
public byte SourceConstantAlpha;
public byte AlphaFormat;
}
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool UpdateLayeredWindow(IntPtr hwnd, IntPtr hdcDst,
ref Point pptDst, ref Size psize, IntPtr hdcSrc, ref Point pprSrc,
Int32 crKey, ref BLENDFUNCTION pblend, Int32 dwFlags);
[DllImport("gdi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern IntPtr CreateCompatibleDC(IntPtr hDC);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern IntPtr GetDC(IntPtr hWnd);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC);
[DllImport("gdi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool DeleteDC(IntPtr hdc);
[DllImport("gdi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern IntPtr SelectObject(IntPtr hDC, IntPtr hObject);
[DllImport("gdi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool DeleteObject(IntPtr hObject);
#endregion
}
}
OK, that should eliminate your semi-transparent problem. Remember to get rid of the override of the WndProc (you won't need it). Set Double-Buffer to false and TopMost to true.
Now to eliminate the other two problems. I hope you thought of a way of doing it....but I will give you my solution. Always keep the PerPixelAlphaForm the size of your MainForm. Same location, Same SIZE. :) And resize the PerPixelAlphaForm's backbuffer bitmap to the same size as well. When you do it this way, all you have to do is redraw the Selection Rectangle. Why? because it overlays the entire MainForm perfectly.
So basically
`OnMouseDown` = Save initial point of mouse, show the Cover layer
`OnMouseMove` = clear the PerPixelAlphaForm bitmap, draw your rectangle
call SelectBitmap again update the form
`OnMouseUp` = hide the Cover layer (or whatever you want to do)
I personally have all this hook up to the Control-Key
To clear the PerPixelAlphaForm we need to do in a certain way. Give all values an Alpha of 0.
public void ClearBackbuffer()
{
Graphics g = Graphics.FromImage(_reference_to_your_backbuffer_);
g.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceCopy;
SolidBrush sb = new SolidBrush(Color.FromArgb(0x00, 0x00, 0x00, 0x00));
g.FillRectangle(sb, this.ClientRectangle);
sb.Dispose();
g.Dispose();
}
Video Demo of the Solution: Remember to Switch to 1080p
If you need more help, let me know I can find some time to rip the code out of the larger program. But it seems to me you're the kind of person that likes tinkering with stuff :D

EDIT : using an additional PictureBox and Bitmap makes the whole thing working
The following Panel draws a rectangle without flickering:
internal sealed class MyPanel : Panel
{
private readonly PictureBox _pictureBox;
private Bitmap _bitmapContent;
private Bitmap _bitmapForeground;
private Point? _point1;
private Point? _point2;
public MyPanel()
{
DoubleBuffered = true;
_pictureBox = new PictureBox();
}
protected override void OnSizeChanged(EventArgs e)
{
if (_bitmapForeground != null) _bitmapForeground.Dispose();
_bitmapForeground = new Bitmap(Size.Width, Size.Height);
if (_bitmapContent != null) _bitmapContent.Dispose();
_bitmapContent = new Bitmap(Size.Width, Size.Height);
_pictureBox.Size = Size;
_pictureBox.Image = _bitmapForeground;
base.OnSizeChanged(e);
}
protected override void OnMouseDown(MouseEventArgs e)
{
_point1 = e.Location;
DrawToBitmap(_bitmapContent, new Rectangle(0, 0, Size.Width, Size.Height));
SetControlsVisibility(false);
Controls.Add(_pictureBox);
base.OnMouseDown(e);
}
private void SetControlsVisibility(bool visible)
{
IEnumerable<Control> ofType = Controls.OfType<Control>();
foreach (Control control in ofType)
{
control.Visible = visible;
}
}
protected override void OnMouseUp(MouseEventArgs e)
{
Controls.Remove(_pictureBox);
SetControlsVisibility(true);
_point1 = null;
_point2 = null;
Refresh();
base.OnMouseUp(e);
}
protected override void OnMouseMove(MouseEventArgs e)
{
if (_point1 != null)
{
_point2 = e.Location;
if (_point1 != null && _point2 != null)
{
Point p1 = _point1.Value;
Point p2 = _point2.Value;
int x1 = p1.X;
int y1 = p1.Y;
int x2 = p2.X;
int y2 = p2.Y;
int xmin = Math.Min(x1, x2);
int ymin = Math.Min(y1, y2);
int xmax = Math.Max(x1, x2);
int ymax = Math.Max(y1, y2);
using (Graphics graphics = Graphics.FromImage(_bitmapForeground))
{
graphics.DrawImageUnscaled(_bitmapContent, 0, 0, _bitmapContent.Width, _bitmapContent.Height);
graphics.DrawRectangle(Pens.Red, new Rectangle(xmin, ymin, xmax - xmin, ymax - ymin));
}
_pictureBox.Refresh();
}
}
base.OnMouseMove(e);
}
}
However, the rectangle will be below the controls, not sure why ...

Related

c# Windows form - Reference 32bpp BGRA BackgroundImage with layered window style WS_EX_LAYERED for alpha transparency support? [duplicate]

When I tried from with transparent background, it's not completely transparent.
I tried two code blocks for this issue. First i tried like this code:
public Form1()
{
InitializeComponent();
SetStyle(ControlStyles.SupportsTransparentBackColor, true);
this.BackColor = Color.Transparent;
this.FormBorderStyle = FormBorderStyle.None;
//this.WindowState = System.Windows.Forms.FormWindowState.Maximized;
}
it look like this picture;
Then i found some different codes and tried ike this;
public Form1()
{
InitializeComponent();
this.TransparencyKey = Color.White;
this.BackColor = Color.White;
this.FormBorderStyle = FormBorderStyle.None;
this.WindowState = System.Windows.Forms.FormWindowState.Maximized;
}
And this looks like this picture;
You can see logo with a white border. I want to show only .png Logo completely transparent. What should i do? How can do this?
Here is my Logo image as .png;
You can use Layered Windows:
Using a layered window can significantly improve performance and
visual effects for a window that has a complex shape, animates its
shape, or wishes to use alpha blending effects. The system
automatically composes and repaints layered windows and the windows of
underlying applications. As a result, layered windows are rendered
smoothly, without the flickering typical of complex window regions. In
addition, layered windows can be partially translucent, that is,
alpha-blended.
Create layered window in Windows Forms
Here is some code from msdn code gallery which demonstrates creating Layered Windows in Windows Forms. It allows you to create a shaped splash screen and let you to move it by mouse.
Add PerPixelAlphaForm to the project and then it's enough to inherit from this form and call its SelectBitmap and pass your png to the method to create a layered window.
PerPixelAlphaForm.cs
#region Using directives
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
#endregion
namespace CSWinFormLayeredWindow
{
public partial class PerPixelAlphaForm : Form
{
public PerPixelAlphaForm()
{
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
this.ShowInTaskbar = false;
this.StartPosition = FormStartPosition.CenterScreen;
this.Load += PerPixelAlphaForm_Load;
}
void PerPixelAlphaForm_Load(object sender, EventArgs e)
{
this.TopMost = true;
}
protected override CreateParams CreateParams
{
get
{
// Add the layered extended style (WS_EX_LAYERED) to this window.
CreateParams createParams = base.CreateParams;
if(!DesignMode)
createParams.ExStyle |= WS_EX_LAYERED;
return createParams;
}
}
/// <summary>
/// Let Windows drag this window for us (thinks its hitting the title
/// bar of the window)
/// </summary>
/// <param name="message"></param>
protected override void WndProc(ref Message message)
{
if (message.Msg == WM_NCHITTEST)
{
// Tell Windows that the user is on the title bar (caption)
message.Result = (IntPtr)HTCAPTION;
}
else
{
base.WndProc(ref message);
}
}
/// <summary>
///
/// </summary>
/// <param name="bitmap"></param>
public void SelectBitmap(Bitmap bitmap)
{
SelectBitmap(bitmap, 255);
}
/// <summary>
///
/// </summary>
/// <param name="bitmap">
///
/// </param>
/// <param name="opacity">
/// Specifies an alpha transparency value to be used on the entire source
/// bitmap. The SourceConstantAlpha value is combined with any per-pixel
/// alpha values in the source bitmap. The value ranges from 0 to 255. If
/// you set SourceConstantAlpha to 0, it is assumed that your image is
/// transparent. When you only want to use per-pixel alpha values, set
/// the SourceConstantAlpha value to 255 (opaque).
/// </param>
public void SelectBitmap(Bitmap bitmap, int opacity)
{
// Does this bitmap contain an alpha channel?
if (bitmap.PixelFormat != PixelFormat.Format32bppArgb)
{
throw new ApplicationException("The bitmap must be 32bpp with alpha-channel.");
}
// Get device contexts
IntPtr screenDc = GetDC(IntPtr.Zero);
IntPtr memDc = CreateCompatibleDC(screenDc);
IntPtr hBitmap = IntPtr.Zero;
IntPtr hOldBitmap = IntPtr.Zero;
try
{
// Get handle to the new bitmap and select it into the current
// device context.
hBitmap = bitmap.GetHbitmap(Color.FromArgb(0));
hOldBitmap = SelectObject(memDc, hBitmap);
// Set parameters for layered window update.
Size newSize = new Size(bitmap.Width, bitmap.Height);
Point sourceLocation = new Point(0, 0);
Point newLocation = new Point(this.Left, this.Top);
BLENDFUNCTION blend = new BLENDFUNCTION();
blend.BlendOp = AC_SRC_OVER;
blend.BlendFlags = 0;
blend.SourceConstantAlpha = (byte)opacity;
blend.AlphaFormat = AC_SRC_ALPHA;
// Update the window.
UpdateLayeredWindow(
this.Handle, // Handle to the layered window
screenDc, // Handle to the screen DC
ref newLocation, // New screen position of the layered window
ref newSize, // New size of the layered window
memDc, // Handle to the layered window surface DC
ref sourceLocation, // Location of the layer in the DC
0, // Color key of the layered window
ref blend, // Transparency of the layered window
ULW_ALPHA // Use blend as the blend function
);
}
finally
{
// Release device context.
ReleaseDC(IntPtr.Zero, screenDc);
if (hBitmap != IntPtr.Zero)
{
SelectObject(memDc, hOldBitmap);
DeleteObject(hBitmap);
}
DeleteDC(memDc);
}
}
#region Native Methods and Structures
const Int32 WS_EX_LAYERED = 0x80000;
const Int32 HTCAPTION = 0x02;
const Int32 WM_NCHITTEST = 0x84;
const Int32 ULW_ALPHA = 0x02;
const byte AC_SRC_OVER = 0x00;
const byte AC_SRC_ALPHA = 0x01;
[StructLayout(LayoutKind.Sequential)]
struct Point
{
public Int32 x;
public Int32 y;
public Point(Int32 x, Int32 y)
{ this.x = x; this.y = y; }
}
[StructLayout(LayoutKind.Sequential)]
struct Size
{
public Int32 cx;
public Int32 cy;
public Size(Int32 cx, Int32 cy)
{ this.cx = cx; this.cy = cy; }
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct ARGB
{
public byte Blue;
public byte Green;
public byte Red;
public byte Alpha;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct BLENDFUNCTION
{
public byte BlendOp;
public byte BlendFlags;
public byte SourceConstantAlpha;
public byte AlphaFormat;
}
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool UpdateLayeredWindow(IntPtr hwnd, IntPtr hdcDst,
ref Point pptDst, ref Size psize, IntPtr hdcSrc, ref Point pprSrc,
Int32 crKey, ref BLENDFUNCTION pblend, Int32 dwFlags);
[DllImport("gdi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern IntPtr CreateCompatibleDC(IntPtr hDC);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern IntPtr GetDC(IntPtr hWnd);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC);
[DllImport("gdi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool DeleteDC(IntPtr hdc);
[DllImport("gdi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern IntPtr SelectObject(IntPtr hDC, IntPtr hObject);
[DllImport("gdi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool DeleteObject(IntPtr hObject);
#endregion
}
}
SplashScreen.cs
public partial class Form4 : CSWinFormLayeredWindow.PerPixelAlphaForm
{
public Form4()
{
InitializeComponent();
this.SelectBitmap(Properties.Resources.splash);
}
}
Note
The original answer was based on turning double buffer off and overriding OnPaintBackground and drawing the image without calling base method. The answer had a known issue; while the form was moveless it was working well but if form was moving or the window behind the form was changed the window was not updating. You can see previous code in revisions. The current edit which is completely based on an MSDN code doesn't have any known issue.
You can choose the level of transparency for an image via changing the alpha coefficient which is between 0 and 255. An image can be a backgrounf image too,no prob
private static Image ToGrayscale(Image s,int alpha)
{
Bitmap tImage = new Bitmap(s);
for (int x = 0; x < tImage.Width; x++)
{
for (int y = 0; y < tImage.Height; y++)
{
Color tCol = tImage.GetPixel(x, y);
Color newColor = Color.FromArgb(alpha, tCol.R, tCol.G, tCol.B);
tImage.SetPixel(x, y, newColor);
}
}
return tImage;
}

Transparent background image for Form - Smooth edge shape for the Form

When I tried from with transparent background, it's not completely transparent.
I tried two code blocks for this issue. First i tried like this code:
public Form1()
{
InitializeComponent();
SetStyle(ControlStyles.SupportsTransparentBackColor, true);
this.BackColor = Color.Transparent;
this.FormBorderStyle = FormBorderStyle.None;
//this.WindowState = System.Windows.Forms.FormWindowState.Maximized;
}
it look like this picture;
Then i found some different codes and tried ike this;
public Form1()
{
InitializeComponent();
this.TransparencyKey = Color.White;
this.BackColor = Color.White;
this.FormBorderStyle = FormBorderStyle.None;
this.WindowState = System.Windows.Forms.FormWindowState.Maximized;
}
And this looks like this picture;
You can see logo with a white border. I want to show only .png Logo completely transparent. What should i do? How can do this?
Here is my Logo image as .png;
You can use Layered Windows:
Using a layered window can significantly improve performance and
visual effects for a window that has a complex shape, animates its
shape, or wishes to use alpha blending effects. The system
automatically composes and repaints layered windows and the windows of
underlying applications. As a result, layered windows are rendered
smoothly, without the flickering typical of complex window regions. In
addition, layered windows can be partially translucent, that is,
alpha-blended.
Create layered window in Windows Forms
Here is some code from msdn code gallery which demonstrates creating Layered Windows in Windows Forms. It allows you to create a shaped splash screen and let you to move it by mouse.
Add PerPixelAlphaForm to the project and then it's enough to inherit from this form and call its SelectBitmap and pass your png to the method to create a layered window.
PerPixelAlphaForm.cs
#region Using directives
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
#endregion
namespace CSWinFormLayeredWindow
{
public partial class PerPixelAlphaForm : Form
{
public PerPixelAlphaForm()
{
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
this.ShowInTaskbar = false;
this.StartPosition = FormStartPosition.CenterScreen;
this.Load += PerPixelAlphaForm_Load;
}
void PerPixelAlphaForm_Load(object sender, EventArgs e)
{
this.TopMost = true;
}
protected override CreateParams CreateParams
{
get
{
// Add the layered extended style (WS_EX_LAYERED) to this window.
CreateParams createParams = base.CreateParams;
if(!DesignMode)
createParams.ExStyle |= WS_EX_LAYERED;
return createParams;
}
}
/// <summary>
/// Let Windows drag this window for us (thinks its hitting the title
/// bar of the window)
/// </summary>
/// <param name="message"></param>
protected override void WndProc(ref Message message)
{
if (message.Msg == WM_NCHITTEST)
{
// Tell Windows that the user is on the title bar (caption)
message.Result = (IntPtr)HTCAPTION;
}
else
{
base.WndProc(ref message);
}
}
/// <summary>
///
/// </summary>
/// <param name="bitmap"></param>
public void SelectBitmap(Bitmap bitmap)
{
SelectBitmap(bitmap, 255);
}
/// <summary>
///
/// </summary>
/// <param name="bitmap">
///
/// </param>
/// <param name="opacity">
/// Specifies an alpha transparency value to be used on the entire source
/// bitmap. The SourceConstantAlpha value is combined with any per-pixel
/// alpha values in the source bitmap. The value ranges from 0 to 255. If
/// you set SourceConstantAlpha to 0, it is assumed that your image is
/// transparent. When you only want to use per-pixel alpha values, set
/// the SourceConstantAlpha value to 255 (opaque).
/// </param>
public void SelectBitmap(Bitmap bitmap, int opacity)
{
// Does this bitmap contain an alpha channel?
if (bitmap.PixelFormat != PixelFormat.Format32bppArgb)
{
throw new ApplicationException("The bitmap must be 32bpp with alpha-channel.");
}
// Get device contexts
IntPtr screenDc = GetDC(IntPtr.Zero);
IntPtr memDc = CreateCompatibleDC(screenDc);
IntPtr hBitmap = IntPtr.Zero;
IntPtr hOldBitmap = IntPtr.Zero;
try
{
// Get handle to the new bitmap and select it into the current
// device context.
hBitmap = bitmap.GetHbitmap(Color.FromArgb(0));
hOldBitmap = SelectObject(memDc, hBitmap);
// Set parameters for layered window update.
Size newSize = new Size(bitmap.Width, bitmap.Height);
Point sourceLocation = new Point(0, 0);
Point newLocation = new Point(this.Left, this.Top);
BLENDFUNCTION blend = new BLENDFUNCTION();
blend.BlendOp = AC_SRC_OVER;
blend.BlendFlags = 0;
blend.SourceConstantAlpha = (byte)opacity;
blend.AlphaFormat = AC_SRC_ALPHA;
// Update the window.
UpdateLayeredWindow(
this.Handle, // Handle to the layered window
screenDc, // Handle to the screen DC
ref newLocation, // New screen position of the layered window
ref newSize, // New size of the layered window
memDc, // Handle to the layered window surface DC
ref sourceLocation, // Location of the layer in the DC
0, // Color key of the layered window
ref blend, // Transparency of the layered window
ULW_ALPHA // Use blend as the blend function
);
}
finally
{
// Release device context.
ReleaseDC(IntPtr.Zero, screenDc);
if (hBitmap != IntPtr.Zero)
{
SelectObject(memDc, hOldBitmap);
DeleteObject(hBitmap);
}
DeleteDC(memDc);
}
}
#region Native Methods and Structures
const Int32 WS_EX_LAYERED = 0x80000;
const Int32 HTCAPTION = 0x02;
const Int32 WM_NCHITTEST = 0x84;
const Int32 ULW_ALPHA = 0x02;
const byte AC_SRC_OVER = 0x00;
const byte AC_SRC_ALPHA = 0x01;
[StructLayout(LayoutKind.Sequential)]
struct Point
{
public Int32 x;
public Int32 y;
public Point(Int32 x, Int32 y)
{ this.x = x; this.y = y; }
}
[StructLayout(LayoutKind.Sequential)]
struct Size
{
public Int32 cx;
public Int32 cy;
public Size(Int32 cx, Int32 cy)
{ this.cx = cx; this.cy = cy; }
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct ARGB
{
public byte Blue;
public byte Green;
public byte Red;
public byte Alpha;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct BLENDFUNCTION
{
public byte BlendOp;
public byte BlendFlags;
public byte SourceConstantAlpha;
public byte AlphaFormat;
}
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool UpdateLayeredWindow(IntPtr hwnd, IntPtr hdcDst,
ref Point pptDst, ref Size psize, IntPtr hdcSrc, ref Point pprSrc,
Int32 crKey, ref BLENDFUNCTION pblend, Int32 dwFlags);
[DllImport("gdi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern IntPtr CreateCompatibleDC(IntPtr hDC);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern IntPtr GetDC(IntPtr hWnd);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC);
[DllImport("gdi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool DeleteDC(IntPtr hdc);
[DllImport("gdi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern IntPtr SelectObject(IntPtr hDC, IntPtr hObject);
[DllImport("gdi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool DeleteObject(IntPtr hObject);
#endregion
}
}
SplashScreen.cs
public partial class Form4 : CSWinFormLayeredWindow.PerPixelAlphaForm
{
public Form4()
{
InitializeComponent();
this.SelectBitmap(Properties.Resources.splash);
}
}
Note
The original answer was based on turning double buffer off and overriding OnPaintBackground and drawing the image without calling base method. The answer had a known issue; while the form was moveless it was working well but if form was moving or the window behind the form was changed the window was not updating. You can see previous code in revisions. The current edit which is completely based on an MSDN code doesn't have any known issue.
You can choose the level of transparency for an image via changing the alpha coefficient which is between 0 and 255. An image can be a backgrounf image too,no prob
private static Image ToGrayscale(Image s,int alpha)
{
Bitmap tImage = new Bitmap(s);
for (int x = 0; x < tImage.Width; x++)
{
for (int y = 0; y < tImage.Height; y++)
{
Color tCol = tImage.GetPixel(x, y);
Color newColor = Color.FromArgb(alpha, tCol.R, tCol.G, tCol.B);
tImage.SetPixel(x, y, newColor);
}
}
return tImage;
}

After replacing the cursor pointer with an image, how to center the image on the mouse pointer location instead of the top left corner of the image?

I have a cursor image .cur with maximum Width and Height of 250 pixels which i fully need.
I already managed to the replace the mouse pointer image with this cur image instead when holding right click.
The problem is that when using I'm doing that the pointer is associated with the top left corner of the image so when I go beyond for example the bounds of the canvas the cur image disappear and I go back to the normal pointer image.
I want this cur image to be centered on the mouse pointer location, not on it's top left corner. How can I do that?
private void canvas_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
{
Cursor cPro = new Cursor(#"C:\Users\Faris\Desktop\C# Testing Projects\cPro.cur");
globalValues.cursorSave = canvas.Cursor;
canvas.Cursor = cPro;
}
private void canvas_MouseRightButtonUp(object sender, MouseButtonEventArgs e)
{
canvas.Cursor = globalValues.cursorSave;
}
You have two options:
In Visual Studio, open the cursor file or resource in the image editor and select the Hotspot Tool from the toolbar. Then click on the new hotspot and save the file.
Create a cursor pragmatically using a bitmap and specify the hot spot yourself.
The code below is from here:
namespace CursorTest
{
public struct IconInfo
{
public bool fIcon;
public int xHotspot;
public int yHotspot;
public IntPtr hbmMask;
public IntPtr hbmColor;
}
public class CursorTest : Form
{
public CursorTest()
{
this.Text = "Cursor Test";
Bitmap bitmap = new Bitmap(140, 25);
Graphics g = Graphics.FromImage(bitmap);
using (Font f = new Font(FontFamily.GenericSansSerif, 10))
g.DrawString("{ } Switch On The Code", f, Brushes.Green, 0, 0);
this.Cursor = CreateCursor(bitmap, 3, 3);
bitmap.Dispose();
}
[DllImport("user32.dll")]
public static extern IntPtr CreateIconIndirect(ref IconInfo icon);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GetIconInfo(IntPtr hIcon, ref IconInfo pIconInfo);
public static Cursor CreateCursor(Bitmap bmp, int xHotSpot, int yHotSpot)
{
IconInfo tmp = new IconInfo();
GetIconInfo(bmp.GetHicon(), ref tmp);
tmp.xHotspot = xHotSpot;
tmp.yHotspot = yHotSpot;
tmp.fIcon = false;
return new Cursor(CreateIconIndirect(ref tmp));
}
}
}

Getting pixel color from PictureBox after drawing lines

I have a PictureBox loaded with a bitmap, say, a photo:
Picture1.Image = new Bitmap("photo.bmp");
And in Picture1_Paint() event, I paint a line on the photo:
e.Graphics.DrawLine(myPen, pointA, pointB);
Now, I want to display RGB info of a clicked pixel:
Bitmap bitmap = (Bitmap) Picture1.Image; /* Making sure I'm using the image being displayed */
Color color = bitmap.GetPixel(e.X, e.Y);
lblSelectedColor.Text = color.R.ToString() + ", " + color.G.ToString() + ", " + color.B.ToString();
The problem is: the RGB value I get is the color of that pixel on the original photo, not including my line drawings. For example, if a thick red line is drawn in the sky, when I click on that red line, I still get sky-blue color from the photo.
I want to get the color info of whatever is shown in the PictureBox, including the lines or ellipses I drew.
There is a difference between drawing on the screen occupied by the control through the Paint event or the OnPaint method, and drawing on the bitmap viewed inside the control. You do the former but are trying to get a pixel from the latter.
Instead of drawing in the Paint event you have to create a Graphics object for your image and draw on that directly. Then assign the image to the Image property of the picture box.
For example, from the top of my head:
Image image = /* ... */;
using (Graphics g = Graphics.FromImage(image))
{
g.DrawLine(myPen, pointA, pointB);
}
picture1.Image = image;
Then when you do GetPixel on the Image object in the picture box, you'll get the pixel value of the line you just drew.
Use Bob Powell's Eye Dropper; here is the code:
using System;
using System.Runtime.InteropServices;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
namespace pixelcolor
{
/// <summary>
/// Summary description for Form1.
/// </summary>
public class Form1 : System.Windows.Forms.Form
{
[DllImport("Gdi32.dll")]
public static extern int GetPixel(
System.IntPtr hdc, // handle to DC
int nXPos, // x-coordinate of pixel
int nYPos // y-coordinate of pixel
);
[DllImport("User32.dll")]
public static extern IntPtr GetDC(IntPtr wnd);
[DllImport("User32.dll")]
public static extern void ReleaseDC(IntPtr dc);
private System.Windows.Forms.Panel panel1;
private System.Timers.Timer timer1;
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.Container components = null;
public Form1()
{
//
// Required for Windows Form Designer support
//
InitializeComponent();
this.SetStyle(ControlStyles.ResizeRedraw,true);
}
/// <summary>
/// Clean up any resources being used.
/// </summary>
protected override void Dispose( bool disposing )
{
if( disposing )
{
if (components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.panel1 = new System.Windows.Forms.Panel();
this.timer1 = new System.Timers.Timer();
((System.ComponentModel.ISupportInitialize)(this.timer1)).BeginInit();
this.SuspendLayout();
//
// panel1
//
this.panel1.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D;
this.panel1.Location = new System.Drawing.Point(216, 8);
this.panel1.Name = "panel1";
this.panel1.Size = new System.Drawing.Size(64, 56);
this.panel1.TabIndex = 0;
//
// timer1
//
this.timer1.Enabled = true;
this.timer1.SynchronizingObject = this;
this.timer1.Elapsed += new System.Timers.ElapsedEventHandler(this.timer1_Elapsed);
//
// Form1
//
this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
this.BackColor = System.Drawing.Color.White;
this.ClientSize = new System.Drawing.Size(292, 273);
this.Controls.Add(this.panel1);
this.Name = "Form1";
this.Text = "Form1";
this.Paint += new System.Windows.Forms.PaintEventHandler(this.Form1_Paint);
((System.ComponentModel.ISupportInitialize)(this.timer1)).EndInit();
this.ResumeLayout(false);
}
#endregion
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.Run(new Form1());
}
private void Form1_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
{
Random r=new Random(1);
for(int x=0;x<100;x++)
{
SolidBrush b=new SolidBrush(Color.FromArgb(r.Next(255),r.Next(255),r.Next(255)));
e.Graphics.FillRectangle(b,r.Next(this.ClientSize.Width),r.Next(this.ClientSize.Height),r.Next(100),r.Next(100));
}
}
private void timer1_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
Point p=Control.MousePosition;
IntPtr dc=GetDC(IntPtr.Zero);
this.panel1.BackColor=ColorTranslator.FromWin32(GetPixel(dc,p.X,p.Y));
ReleaseDC(dc);
}
}
}
If you wanted to sample the colours in a PictureBox or in your own form then you simply need to obtain the DC for that object. This can be accomplished using CreateGraphics, Graphics.GetHdc and Graphics.ReleaseHdc. The listing below shows a MouseMove handler that can be used to obtain the pixel colour from a form.
protected override void OnMouseMove(MouseEventArgs e)
{
Graphics g=this.CreateGraphics();
IntPtr myDC=g.GetHdc();
Color c=ColorTranslator.FromWin32(GetPixel(myDC,e.X,e.Y));
g.ReleaseHdc(myDC);
this.panel1.BackColor=c;
}
Had to make a change to Jeremy's answer above to get it to work for me. The prototype for ReleaseDC seems to be different to what he posted. This worked for me:
[DllImport("Gdi32.dll")]
public static extern int GetPixel(
System.IntPtr hdc, // handle to DC
int nXPos, // x-coordinate of pixel
int nYPos // y-coordinate of pixel
);
[DllImport("User32.dll")]
public static extern IntPtr GetDC(IntPtr wnd);
[DllImport("User32.dll")]
public static extern bool ReleaseDC(IntPtr hWnd, IntPtr hDC);
//For best results, use a component like a Panel and use it's handle (panel1.Handle) and e.X and e.Y on a component's MouseDown event
private Color GetColorAtPoint(Point? p = null, IntPtr? handle = null)
{
var hwnd = handle ?? IntPtr.Zero; // Handle;
Point point = p ?? MousePosition;
var dc = GetDC(hwnd);
Color c = ColorTranslator.FromWin32(GetPixel(dc, point.X, point.Y));
ReleaseDC(hwnd, dc);
return c;
}

C# - Form user button click, get next window clicked by user

This shouldnt be a difficult question, but it is difficult to google the question and get the idea across.
The problem is simple: I have a windows form where the user presses a button, then, it will wait on the user to click another window. It stores that selected window information for manipulation later (specifically the dimensions).
How can I get the active window of the next user click after a button is pressed?
Thanks
You need to get the foreground window.
[DllImport("user32.dll")]
static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll", SetLastError = true)]
static extern bool GetWindowRect(IntPtr hWnd, out Rectangle lpRect);
Rect rect = new Rect ();
GetWindowRect(GetForegroundWindow(), out rect);
//calculate width and height from rect
using (Bitmap bitmap = new Bitmap(width, height))
{
using (Graphics g = Graphics.FromImage(bitmap))
{
Size size = new System.Drawing.Size(width, height);
g.CopyFromScreen(new Point(rect.Left, rect.Top), Point.Empty, size);
}
bitmap.Save("C://test.jpg", ImageFormat.Jpeg);
}
[StructLayout(LayoutKind.Sequential)]
public struct Rect {
public int Left;
public int Top;
public int Right;
public int Bottom;
}
I found most of the code in these two answers on SO. Modifed it to suit your question
Capture window
Find window width and height
Interested by your question i have created this small screen capture app.
It has strange workarounds:
Timer used to capture mouse position outside winform is strange but was easier to implement than using Global System Hooks . You might try to use lib from link or implement it by yourself. You wouldn't need than to use Timer and what is more important you could drop this constant reactivation of form.
I have found somewhere in SE info about overriding CreateParams but i can't find link to it anymore it allows you to click trough form (i think that not all of added params are neccessery but as i said i lost link :) ).
Only visible part of window is captured + all windows overlapping it (probably sinc u got a windowHandle to it u might try to show/activate it somehow).
For some windows it gets controls inside window.
Provided app is probably very unprofessional and unsafe and might blow up your computer so beware ;P
Used form is borderless with opacity set to 80%.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
namespace WindowInfo
{
public partial class CurrentWindow : Form
{
Rectangle GDIrect = new Rectangle(0, 0, 100, 100);
[DllImport("user32.dll")]
public static extern IntPtr WindowFromPoint(Point lpPoint);
[DllImport("user32.dll")]
public static extern bool GetCursorPos(out Point lpPoint);
[DllImport("user32.dll")]
private static extern IntPtr GetWindowRect(IntPtr hWnd, ref Rect rect);
[DllImport("user32.dll")]
static extern IntPtr GetForegroundWindow();
[StructLayout(LayoutKind.Sequential)]
public struct Rect
{
public int Left;
public int Top;
public int Right;
public int Bottom;
}
public CurrentWindow()
{
InitializeComponent();
}
protected override CreateParams CreateParams
{
get
{
CreateParams baseParams = base.CreateParams;
baseParams.ExStyle |= (int)(
0x00080000 |
0x08000000 |
0x00000080 |
0x00000020
);
return baseParams;
}
}
public static IntPtr GetWindowUnderCursor()
{
Point ptCursor = new Point();
GetCursorPos(out ptCursor);
return WindowFromPoint(ptCursor);
}
public Bitmap CaptureScreen()
{
var result = new Bitmap(this.DisplayRectangle.Width, this.DisplayRectangle.Height);
using (var g = Graphics.FromImage(result))
{
g.CopyFromScreen(this.Location.X, this.Location.Y, 0, 0, this.DisplayRectangle.Size);
}
return result;
}
private void timer1_Tick(object sender, EventArgs e)
{
IntPtr windowHandle = GetWindowUnderCursor();
Rect rect = new Rect();
GetWindowRect(windowHandle, ref rect);
GDIrect = new Rectangle(rect.Left, rect.Top, rect.Right - rect.Left, rect.Bottom - rect.Top);
this.Location = new Point(GDIrect.Left, GDIrect.Top);
this.Size = GDIrect.Size;
this.Activate();
}
private void CurrentWindow_KeyPress(object sender, KeyPressEventArgs e)
{
if (e.KeyChar == 'c')
{
this.Visible = false;
Bitmap bmp = CaptureScreen();
bmp.Save(Application.StartupPath + "\\example.png");
this.Visible = true;
}
else if (e.KeyChar == 'x')
{
this.Close();
}
}
}
}
U might add it to your app and run after button click, it should work but i have tested it only separately. Good luck :).

Categories

Resources