Unexpected behavior while rendering Custom Controls(Form) C# - c#

Introduction
I am developing custom controls [Here: Form] with custom 3D shape and PathGradient colors.
everything is working very smoothly and even I had achieved what I exactly want
And the whole code to generate this form is given below :-
using System;
using System.Drawing;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.ComponentModel;
namespace CustomControls
{
public class SplashFORM : Form
{
[Description("If True then user can close the form by pressing Alt + F4 while it is focused.")]
public bool CanClose { get; set; } = false;
private readonly Timer Drawer = new Timer();
public SplashFORM()
{
FormBorderStyle = FormBorderStyle.None;
StartPosition = FormStartPosition.CenterScreen;
}
protected override void OnFormClosing(FormClosingEventArgs FCEArgs)
{
if (!CanClose) { FCEArgs.Cancel = true; return; }
base.OnFormClosing(FCEArgs);
}
protected override void OnResize(EventArgs e)
{
Invalidate();
base.OnResize(e);
}
protected override void OnLoad(EventArgs EArgs)
{
if (!DesignMode)
{
DrawForm(null, null);
}
base.OnLoad(EArgs);
}
private void DrawForm(object _, EventArgs __)
{
using (Bitmap BackIMG = new Bitmap(Width, Height))
{
using (Graphics Gfx = Graphics.FromImage(BackIMG))
{
Gfx.SmoothingMode = SmoothingMode.HighQuality;
FillRoundedRectangle(Gfx);
foreach (Control C in Controls)
{
using (Bitmap BitMP = new Bitmap(C.Width, C.Height))
{
Rectangle Rect = new Rectangle(0, 0, C.Width, C.Height);
C.DrawToBitmap(BitMP, Rect);
Gfx.DrawImage(BitMP, C.Location);
}
}
SetBitmap(BackIMG, Left, Top, Handle);
}
}
}
protected override void OnPaint(PaintEventArgs PEArgs)
{
if (DesignMode)
{
Graphics Gfx = PEArgs.Graphics;
Gfx.SmoothingMode = SmoothingMode.HighQuality;
FillRoundedRectangle(Gfx);
}
base.OnPaint(PEArgs);
}
protected override CreateParams CreateParams
{
get
{
CreateParams Params = base.CreateParams;
Params.ClassStyle = 0x00020000;
Params.Style |= 0x00020000;
if (!DesignMode) { Params.ExStyle |= 0x00080000; }
return Params;
}
}
protected override void OnPaintBackground(PaintEventArgs PEArgs)
{
using (Brush GPBrush = Helper.Get_SplashBrush(new Rectangle(0, 0, Width - 1, Height - 1)))
{ PEArgs.Graphics.FillRectangle(GPBrush, ClientRectangle); }
}
private void SetBitmap(Bitmap BitMP, int CLeft, int CTop, IntPtr CHndl)
{
if (BitMP.PixelFormat != PixelFormat.Format32bppArgb) throw new ApplicationException("The BitMP must be 32ppp with alpha-channel.");
IntPtr ScrnDC = Win32.GetDC(IntPtr.Zero);
IntPtr MemDC = Win32.CreateCompatibleDC(ScrnDC);
IntPtr HBitMP = IntPtr.Zero;
IntPtr OBitMP = IntPtr.Zero;
byte OPCity = 255;
try
{
HBitMP = BitMP.GetHbitmap(Color.FromArgb(0));
OBitMP = Win32.SelectObject(MemDC, HBitMP);
Win32.Size _Size = new Win32.Size(BitMP.Width, BitMP.Height);
Win32.Point _PointSource = new Win32.Point(0, 0);
Win32.Point _TopPos = new Win32.Point(CLeft, CTop);
Win32.BLENDFUNCTION _Blend = new Win32.BLENDFUNCTION
{
BlendOp = Win32.AC_SRC_OVER,
BlendFlags = 0,
SourceConstantAlpha = OPCity,
AlphaFormat = Win32.AC_SRC_ALPHA
};
Win32.UpdateLayeredWindow(CHndl, ScrnDC, ref _TopPos, ref _Size, MemDC, ref _PointSource, 0, ref _Blend, Win32.ULW_ALPHA);
}
finally
{
Win32.ReleaseDC(IntPtr.Zero, ScrnDC);
if (HBitMP != IntPtr.Zero)
{
Win32.SelectObject(MemDC, OBitMP);
Win32.DeleteObject(HBitMP);
}
Win32.DeleteDC(MemDC);
}
}
private GraphicsPath RoundedRect()
{
Rectangle _2DSize = new Rectangle(0, 0, Width - 1, Height - 1);
int Diameter = 50 * 2;
Size _Size = new Size(Diameter, Diameter);
Rectangle _Arc = new Rectangle(_2DSize.Location, _Size);
GraphicsPath _Path = new GraphicsPath();
_Path.AddArc(_Arc, 180, 90);
_Arc.X = _2DSize.Right - 50;
_Path.AddArc(_Arc, 270, 90);
_Arc.Y = _2DSize.Bottom - 5;
_Path.AddArc(_Arc, 0, 90);
_Arc.X = _2DSize.Left - Diameter;
_Path.AddArc(_Arc, 90, 90);
_Path.CloseFigure();
return _Path;
}
private void FillRoundedRectangle(Graphics Gfx)
{
if (Gfx == null) throw new ArgumentNullException("Graphics supplied is null");
using (GraphicsPath GPth = RoundedRect())
{
var Bnds = new Rectangle(0, 0, Width - 1, Height - 1);
PointF[] PT = new PointF[]
{
new PointF(-50, -50),
new PointF(Bnds.Right, 0),
new PointF(Bnds.Right, Bnds.Bottom),
new PointF(40, Bnds.Bottom),
new PointF((float)((float)Bnds.Right / 2 - ((float)Bnds.Bottom * 0.15)), (float)((float)Bnds.Bottom / 2 + ((float)Bnds.Bottom * 0.35)))
};
Brush GPBrush = Helper.Get_SplashBrush(Bnds);
//
Gfx.FillPath(GPBrush, GPth);
Region = new Region(GPth);
//using (Brush GPBrush = Helper.Get_SplashBrush(new Rectangle(0, 0, Width - 1, Height - 1)))
//{ Gfx.FillPath(GPBrush, GPth); }
}
}
}
internal class Win32
{
public const Int32 ULW_COLORKEY = 0x00000001;
public const Int32 ULW_ALPHA = 0x00000002;
public const Int32 ULW_OPAQUE = 0x00000004;
public const byte AC_SRC_OVER = 0x00;
public const byte AC_SRC_ALPHA = 0x01;
[DllImport("user32.dll", ExactSpelling = true, SetLastError = true)]
public 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("user32.dll", ExactSpelling = true, SetLastError = true)]
public static extern IntPtr GetDC(IntPtr hWnd);
[DllImport("user32.dll", ExactSpelling = true)]
public static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC);
[DllImport("gdi32.dll", ExactSpelling = true, SetLastError = true)]
public static extern IntPtr CreateCompatibleDC(IntPtr hDC);
[DllImport("gdi32.dll", ExactSpelling = true, SetLastError = true)]
public static extern Bool DeleteDC(IntPtr hdc);
[DllImport("gdi32.dll", ExactSpelling = true)]
public static extern IntPtr SelectObject(IntPtr hDC, IntPtr hObject);
[DllImport("gdi32.dll", ExactSpelling = true, SetLastError = true)]
public static extern Bool DeleteObject(IntPtr hObject);
public enum Bool
{
False = 0,
True
};
[StructLayout(LayoutKind.Sequential)]
public struct Point
{
public Int32 x;
public Int32 y;
public Point(Int32 x, Int32 y) { this.x = x; this.y = y; }
}
[StructLayout(LayoutKind.Sequential)]
public 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)]
public struct BLENDFUNCTION
{
public byte BlendOp;
public byte BlendFlags;
public byte SourceConstantAlpha;
public byte AlphaFormat;
}
}
internal class Helper
{
public static Brush Get_SplashBrush(Rectangle Bnds)
{
PointF[] PTsGH;
Color[] CLRsGH;
PathGradientBrush PGB;
PTsGH = new PointF[]
{
new PointF(-50, -50),
new PointF(Bnds.Right, 0),
new PointF(Bnds.Right, Bnds.Bottom),
new PointF(40, Bnds.Bottom),
new PointF((float)((float)Bnds.Right / 2 - ((float)Bnds.Bottom * 0.15)), (float)((float)Bnds.Bottom / 2 + ((float)Bnds.Bottom * 0.35)))
};
CLRsGH = new Color[]
{
Color.FromArgb(120,40,40),
Color.FromArgb(60, 100, 40),
Color.FromArgb(50, 50, 120),
Color.FromArgb(0, 60, 100),
Color.FromArgb(240, 120, 20, 40)
};
PGB = new PathGradientBrush(PTsGH)
{
SurroundColors = CLRsGH,
CenterColor = Color.FromArgb(160, 124, 20)
};
return PGB;
}
public enum BrushType
{
Linear, Path, Solid
}
public enum ShapeType
{
Rectangular, Circular, Triangular, SplashSpecial
}
}
}
The Problem
Everything is very fine till now but when I try to change the opacity of the form by using this code
protected override void OnClick(EventArgs e)
{
Opacity -= 0.05;
base.OnClick(e);
}
then the form becomes opaque and looks very dirty
I want to change the opacity of the form using a timer to show a fade in and fade out effect!
I tried to use Invalidate(), Update() and Refresh() on the form after changing the opacity but still no luck :(
Is there a way to solve this issue, or is there any alternative to what I want to achieve?

Related

How do I avoid a generic error in GDI+ when creating a custom cursor

I am using the following code to generate a bespoke cursor dependent on the mouse position inside a win forms control. The cursor becomes a line that points towards the center of the control. Everything works very well for a few seconds and then I get the very unhelpful message :-
Exception thrown: 'System.Runtime.InteropServices.ExternalException' in System.Drawing.Common.dll
An exception of type 'System.Runtime.InteropServices.ExternalException' occurred in System.Drawing.Common.dll but was not handled in user code
A generic error occurred in GDI+.
This error appears to be connected to the garbage collector trying to clean up the pointer while it is still in use (though it could be something else). As you will see I have tried to make the pointer a property so that it isn't cleaned up but that doesn't seem to help.
Any ideas on how to avoid this error would be very welcome.
public struct IconInfo
{
public bool fIcon;
public int xHotspot;
public int yHotspot;
public IntPtr hbmMask;
public IntPtr hbmColor;
}
public partial class CursorTest : UserControl
{
public CursorTest()
{
InitializeComponent();
}
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool GetIconInfo(IntPtr hIcon, ref IconInfo pIconInfo);
[DllImport("user32.dll")]
static extern IntPtr CreateIconIndirect(ref IconInfo icon);
[DllImport("user32.dll")]
private static extern bool DestroyIcon(IntPtr hIcon);
bool IsBusy { get; set; } = false;
IntPtr ptr { get; set; }
/// <summary>
/// Create a 32x32 cursor from a bitmap, with the hot spot in the middle
/// </summary>
public void CreateCursor(Bitmap bmp)
{
ptr = bmp.GetHicon();
IconInfo tmp = new IconInfo();
GetIconInfo(ptr, ref tmp);
tmp.xHotspot = 16;
tmp.yHotspot = 16;
tmp.fIcon = false;
ptr = CreateIconIndirect(ref tmp);
this.Cursor = new Cursor(ptr);// Error Happens here
DestroyIcon(ptr);
}
private void GenerateCursorFromPostion(Point e)
{
if (IsBusy)
{
System.Diagnostics.Trace.WriteLine("Busy!!!");
return;
}
IsBusy = true;
float x = 16 * (Width / 2.0f - e.X);
float y = 16 * (Height / 2.0f - e.Y);
PointF st = new PointF(x + 16, y + 16);
PointF ed = new PointF(16 - x, 16 - y);
Bitmap bmp = new Bitmap(32, 32);
Graphics g = Graphics.FromImage(bmp);
g.DrawLine(Pens.Black, st, ed);
CreateCursor(bmp);
g.Dispose();
bmp.Dispose();
IsBusy = false;
}
private void CursorTest_MouseMove(object sender, MouseEventArgs e)
{
GenerateCursorFromPostion(e.Location);
}
}
You have a number of issues with your current code.
Your primary issue appears to be that you are destroying the icon for the cursor. When you create a Cursor using a handle, the handle is not copied, but used directly. Instead, you need to dispose it either when the control is being disposed, or when the cursor is replaced with another.
You also need to dispose the handle from GetHicon, and the Bitmap and Graphics needs a using, and you need to handle various errors.
public partial class CursorTest : UserControl
{
public CursorTest()
{
InitializeComponent();
}
[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool GetIconInfo(IntPtr hIcon, out IconInfo pIconInfo);
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr CreateIconIndirect(in IconInfo icon);
[DllImport("user32.dll", SetLastError = true)]
private static extern bool DestroyIcon(IntPtr hIcon);
bool IsBusy { get; set; } = false;
IntPtr ptr { get; set; }
protected override Dispose(bool disposing)
{
base.Dispose(disposing);
DestroyIcon(ptr);
}
/// <summary>
/// Create a 32x32 cursor from a bitmap, with the hot spot in the middle
/// </summary>
public void CreateCursor(Bitmap bmp)
{
var original = IntPtr.Zero;
try
{
original = bmp.GetHicon();
if(!GetIconInfo(original, out var tmp))
throw new Win32Exception(Marshal.GetLastWin32Error());
tmp.xHotspot = 16;
tmp.yHotspot = 16;
tmp.fIcon = false;
var newPtr = CreateIconIndirect(in tmp);
if (newPtr != IntPtr.Zero)
throw new Win32Exception(Marshal.GetLastWin32Error());
this.Cursor = new Cursor(newPtr);
DestroyIcon(ptr);
ptr = newPtr;
}
finally
{
if(original != IntPtr.Zero)
DestroyIcon(original);
}
}
private void GenerateCursorFromPostion(Point e)
{
if (IsBusy)
{
System.Diagnostics.Trace.WriteLine("Busy!!!");
return;
}
IsBusy = true;
float x = 16 * (Width / 2.0f - e.X);
float y = 16 * (Height / 2.0f - e.Y);
PointF st = new PointF(x + 16, y + 16);
PointF ed = new PointF(16 - x, 16 - y);
using (Bitmap bmp = new Bitmap(32, 32))
{
using (Graphics g = Graphics.FromImage(bmp))
g.DrawLine(Pens.Black, st, ed);
CreateCursor(bmp);
}
IsBusy = false;
}
private void CursorTest_MouseMove(object sender, MouseEventArgs e)
{
GenerateCursorFromPostion(e.Location);
}
}

How does one create text that can overlay all windows like the Windows Activation Watermark?

I would like to create a count down timer that displays in the lower right hand of the screen with slightly transparent text that can be seen no matter what you have on the screen. Think of something like the windows not activated watermark message that appears when your windows copy isn't activated. That displays in the lower right overlaying all windows and is "stuck" to the screen.
Is there a way to do this via a program? C# preferred.
In my research so far I only came across Deskbands which allow you to put something in the task bar but not outside of it.
Note: The following answer creates a normal semi-transparent topmost form with smooth edge. It's not exactly like windows activation text, for example it goes behind tooltip windows or goes behind menus, but it stays on top of other non-topmost windows.
You can create a Layered Window by setting WS_EX_LAYERED style, then you can assign a smooth-edge transparent region to the window. You can also set WS_EX_TRANSPARENT for the window, then it will ignore the mouse event. Then to make it always on top set its TopMost to true. And finally if you want to make it semi-transparent use suitable Opacity value.
Example
In the following example, I've created an overlay form which is always on top and shows time:
1 - Add the following class which contains required native methods to your project:
using System;
using System.Runtime.InteropServices;
public class NativeMethods
{
public const int WS_EX_LAYERED = 0x80000;
public const int HTCAPTION = 0x02;
public const int WM_NCHITTEST = 0x84;
public const int ULW_ALPHA = 0x02;
public const byte AC_SRC_OVER = 0x00;
public const byte AC_SRC_ALPHA = 0x01;
public const int WS_EX_TRANSPARENT = 0x20;
[StructLayout(LayoutKind.Sequential)]
public struct POINT
{
public int x;
public int y;
public POINT(int x, int y)
{ this.x = x; this.y = y; }
}
[StructLayout(LayoutKind.Sequential)]
public struct SIZE
{
public int cx;
public int cy;
public SIZE(int cx, int cy)
{ this.cx = cx; this.cy = cy; }
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public 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)]
public static extern bool UpdateLayeredWindow(IntPtr hwnd, IntPtr hdcDst,
ref POINT pptDst, ref SIZE psize, IntPtr hdcSrc, ref POINT pprSrc,
int crKey, ref BLENDFUNCTION pblend, int dwFlags);
[DllImport("gdi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr CreateCompatibleDC(IntPtr hDC);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr GetDC(IntPtr hWnd);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC);
[DllImport("gdi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool DeleteDC(IntPtr hdc);
[DllImport("gdi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr SelectObject(IntPtr hDC, IntPtr hObject);
[DllImport("gdi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool DeleteObject(IntPtr hObject);
}
2 - Add the following class which is a base class for semi-transparent smooth-edge windows forms:
using System;
using System.Drawing;
using System.Windows.Forms;
using System.Drawing.Imaging;
using static NativeMethods;
public class PerPixelAlphaForm : Form
{
public PerPixelAlphaForm()
{
this.FormBorderStyle = FormBorderStyle.None;
this.ShowInTaskbar = false;
this.TopMost = true;
}
protected override CreateParams CreateParams
{
get
{
CreateParams createParams = base.CreateParams;
if (!DesignMode)
createParams.ExStyle |= WS_EX_LAYERED | WS_EX_TRANSPARENT;
return createParams;
}
}
public void SelectBitmap(Bitmap bitmap, int opacity = 255)
{
if (bitmap.PixelFormat != PixelFormat.Format32bppArgb)
{
throw new ApplicationException(
"The bitmap must be 32bpp with alpha-channel.");
}
IntPtr screenDc = GetDC(IntPtr.Zero);
IntPtr memDc = CreateCompatibleDC(screenDc);
IntPtr hBitmap = IntPtr.Zero;
IntPtr hOldBitmap = IntPtr.Zero;
try
{
hBitmap = bitmap.GetHbitmap(Color.FromArgb(0));
hOldBitmap = SelectObject(memDc, hBitmap);
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;
UpdateLayeredWindow(this.Handle, screenDc, ref newLocation,
ref newSize, memDc, ref sourceLocation, 0, ref blend, ULW_ALPHA);
}
finally
{
ReleaseDC(IntPtr.Zero, screenDc);
if (hBitmap != IntPtr.Zero)
{
SelectObject(memDc, hOldBitmap);
DeleteObject(hBitmap);
}
DeleteDC(memDc);
}
}
}
3 - Then add the following class which is a form, having a timer that shows time:
using System;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Drawing.Text;
using System.Windows.Forms;
public partial class Form1 : PerPixelAlphaForm
{
private IContainer components = null;
private Timer timer1;
private Bitmap image;
public Form1()
{
this.components = new Container();
this.timer1 = new Timer(this.components);
this.timer1.Enabled = true;
this.timer1.Interval = 500;
this.timer1.Tick += new EventHandler(this.timer1_Tick);
this.StartPosition = FormStartPosition.Manual;
this.Location = new Point(300, 300);
this.Size = new Size(800, 500);
this.image = new Bitmap(Width, Height, PixelFormat.Format32bppArgb);
}
private void timer1_Tick(object sender, EventArgs e)
{
using (var g = Graphics.FromImage(image))
{
g.Clear(Color.Transparent);
g.SmoothingMode = SmoothingMode.AntiAlias;
g.TextRenderingHint = TextRenderingHint.AntiAlias;
g.DrawString(DateTime.Now.ToString("HH:mm:ss"),
new Font(this.Font.FontFamily, 60, FontStyle.Bold), Brushes.Black,
ClientRectangle, StringFormat.GenericDefault);
SelectBitmap(image, 150);
}
}
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
components.Dispose();
if (disposing && image != null)
image.Dispose();
base.Dispose(disposing);
}
}
You can find more information in the following posts:
Windows Forms: Pass clicks through a partially transparent always-on-top window
Transparent background image for Form - Smooth edge shape for the Form

Retrieve color of windows 10 taskbar

I found out, that there is a static property in the System.Windows.SystemParameters class that declares the color the user chose for his Windows overall.
But there is a second possibility for the user that enables him to enable or disable, whether the taskbar/windows bar should use that same color.
I was unable to find a key for that in the SystemParameters-class.
I believe there is a registry value to find the colour and it is probably inside:
HKEY_CURRENT_USER\Control Panel\Colors
However on my system I have colours on the taskbar disabled and that colour value doesn't seem to appear in this key.
A work around would be to combine the answers to the following two questions:
TaskBar Location
How to Read the Colour of a Screen Pixel
You need the following imports:
[DllImport("shell32.dll")]
private static extern IntPtr SHAppBarMessage(int msg, ref APPBARDATA data);
[DllImport("gdi32.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true)]
private static extern int BitBlt(IntPtr hDC, int x, int y, int nWidth, int nHeight, IntPtr hSrcDC, int xSrc, int ySrc, int dwRop);
The following structs:
private struct APPBARDATA
{
public int cbSize;
public IntPtr hWnd;
public int uCallbackMessage;
public int uEdge;
public RECT rc;
public IntPtr lParam;
}
private struct RECT
{
public int left, top, right, bottom;
}
And the following constant:
private const int ABM_GETTASKBARPOS = 5;
Then you can call the following two methods:
public static Rectangle GetTaskbarPosition()
{
APPBARDATA data = new APPBARDATA();
data.cbSize = Marshal.SizeOf(data);
IntPtr retval = SHAppBarMessage(ABM_GETTASKBARPOS, ref data);
if (retval == IntPtr.Zero)
{
throw new Win32Exception("Please re-install Windows");
}
return new Rectangle(data.rc.left, data.rc.top, data.rc.right - data.rc.left, data.rc.bottom - data.rc.top);
}
public static Color GetColourAt(Point location)
{
using (Bitmap screenPixel = new Bitmap(1, 1, PixelFormat.Format32bppArgb))
using (Graphics gdest = Graphics.FromImage(screenPixel))
{
using (Graphics gsrc = Graphics.FromHwnd(IntPtr.Zero))
{
IntPtr hSrcDC = gsrc.GetHdc();
IntPtr hDC = gdest.GetHdc();
int retval = BitBlt(hDC, 0, 0, 1, 1, hSrcDC, location.X, location.Y, (int)CopyPixelOperation.SourceCopy);
gdest.ReleaseHdc();
gsrc.ReleaseHdc();
}
return screenPixel.GetPixel(0, 0);
}
}
Like this:
Color taskBarColour = GetColourAt(GetTaskbarPosition().Location);

how to take Screen shot of webpage

I want to take screenshot of a webpage. I have tried many things but they are for Windows Forms and I am using WebForms with C#. Searched but didn't find. I have applied:
1.
private void CaptureMy()
{
int xheight = 822; int xwidth = 479;
try
{
string StatePath = "Document\\Metro\\Test12.png";
String filepath = Server.MapPath(StatePath);
Bitmap captureBitmap = new Bitmap(xheight, xwidth, PixelFormat.Format32bppArgb);
System.Drawing.Rectangle captureRectangle = Screen.AllScreens[0].Bounds;//capture our Current Screen
Graphics captureGraphics = Graphics.FromImage(captureBitmap); //Creating a New Graphics Object
captureGraphics.CopyFromScreen(182, 207, 0, 0, captureRectangle.Size); //Copying Image from The Screen
ScriptManager.RegisterStartupScript(this, this.GetType(), "Message", "alert(' successfully saved.');", true);
string Type = Convert.ToString(ViewState["Type"]);
captureBitmap.Save(filepath, ImageFormat.Jpeg); //Saving the Image File
}
catch (Exception ex)
{
}
}
2.
public void capscreen_test()
{
System.Drawing.Image img = FullScreen();
String path = Server.MapPath("Document/Test13.png");
string imageFile = #"\Exceptions\ExceptionLog" + DateTime.Now.ToString("MMddyyyy-hhmmss") + ".jpg";
img.Save(path);
}
public static Bitmap FullScreen()
{
return ScreenRectangle(SystemInformation.VirtualScreen);
}
public static Bitmap DisplayMonitor(System.Windows.Forms.Screen monitor)
{
System.Drawing.Rectangle rect;
try
{
rect = monitor.Bounds;
}
catch (Exception ex)
{
throw new ArgumentException("Invalid parameter.", "monitor", ex);
}
return ScreenRectangle(monitor.Bounds);
}
public static Bitmap ActiveWindow()
{
IntPtr hwnd;
Win32.RECT wRect = new Win32.RECT();
hwnd = Win32.GetForegroundWindow();
if (hwnd != IntPtr.Zero)
{
if (Win32.GetWindowRect(hwnd, ref wRect))
{
System.Drawing.Rectangle rect = new System.Drawing.Rectangle(wRect.Left, wRect.Top, wRect.Right - wRect.Left, wRect.Bottom - wRect.Top);
return ScreenRectangle(rect);
}
else
{
throw new System.ComponentModel.Win32Exception();
}
}
else
{
throw new Exception("Could not find any active window.");
}
}
public static Bitmap Window(IntPtr hwnd)
{
Win32.RECT wRect = new Win32.RECT();
if (hwnd != IntPtr.Zero)
{
if (Win32.GetWindowRect(hwnd, ref wRect))
{
System.Drawing.Rectangle rect = new System.Drawing.Rectangle(wRect.Left, wRect.Top, wRect.Right - wRect.Left, wRect.Bottom - wRect.Top);
return ScreenRectangle(rect);
}
else
{
throw new System.ComponentModel.Win32Exception();
}
}
else
{
throw new ArgumentException("Invalid window handle.", "hwnd");
}
}
public static Bitmap Control(Point p)
{
IntPtr hwnd;
Win32.RECT wRect = new Win32.RECT();
hwnd = Win32.WindowFromPoint(p);
if (hwnd != IntPtr.Zero)
{
if (Win32.GetWindowRect(hwnd, ref wRect))
{
System.Drawing.Rectangle rect = new System.Drawing.Rectangle(wRect.Left, wRect.Top, wRect.Right - wRect.Left, wRect.Bottom - wRect.Top);
return ScreenRectangle(rect);
}
else
{
throw new System.ComponentModel.Win32Exception();
}
}
else
{
throw new Exception("Could not find any window at the specified point.");
}
}
public static Bitmap Control(IntPtr hwnd)
{
Win32.RECT wRect = new Win32.RECT();
if (hwnd != IntPtr.Zero)
{
if (Win32.GetWindowRect(hwnd, ref wRect))
{
System.Drawing.Rectangle rect = new System.Drawing.Rectangle(wRect.Left, wRect.Top, wRect.Right - wRect.Left, wRect.Bottom - wRect.Top);
return ScreenRectangle(rect);
}
else
{
throw new System.ComponentModel.Win32Exception();
}
}
else
{
throw new ArgumentException("Invalid control handle.", "hwnd");
}
}
public static Bitmap ScreenRectangle(System.Drawing.Rectangle rect)
{
if (!(rect.IsEmpty) && rect.Width != 0 && rect.Height != 0)
{
System.ComponentModel.Win32Exception win32Ex = null;
IntPtr wHdc = Win32.GetDC(IntPtr.Zero);
if (wHdc == IntPtr.Zero)
{
throw new System.ComponentModel.Win32Exception();
}
Graphics g;
Bitmap img = new Bitmap(rect.Width, rect.Height);
img.MakeTransparent();
g = Graphics.FromImage(img);
IntPtr gHdc = g.GetHdc();
if (!(Win32.BitBlt(gHdc, 0, 0, rect.Width, rect.Height, wHdc, rect.X, rect.Y, Win32.SRCCOPY | Win32.CAPTUREBLT)))
{
win32Ex = new System.ComponentModel.Win32Exception();
}
g.ReleaseHdc(gHdc);
g.Dispose();
Win32.ReleaseDC(IntPtr.Zero, wHdc);
if (!(win32Ex == null))
{
throw win32Ex;
}
else
{
return img;
}
}
else
{
throw new ArgumentException("Invalid parameter.", "rect");
}
}
private class Win32
{
public const int CAPTUREBLT = 1073741824;
public const int BLACKNESS = 66;
public const int DSTINVERT = 5570569;
public const int MERGECOPY = 12583114;
public const int MERGEPAINT = 12255782;
public const int NOTSRCCOPY = 3342344;
public const int NOTSRCERASE = 1114278;
public const int PATCOPY = 15728673;
public const int PATINVERT = 5898313;
public const int PATPAINT = 16452105;
public const int SRCAND = 8913094;
public const int SRCCOPY = 13369376;
public const int SRCERASE = 4457256;
public const int SRCINVERT = 6684742;
public const int SRCPAINT = 15597702;
public const int WHITENESS = 16711778;
public const int HORZRES = 8;
public const int VERTRES = 10;
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
public int Left;
public int Top;
public int Right;
public int Bottom;
}
[DllImport("user32", SetLastError = true)]
public static extern bool GetWindowRect(IntPtr hWnd, ref RECT lpRect);
[DllImport("user32", SetLastError = true)]
public static extern int ReleaseDC(IntPtr hWnd, IntPtr hdc);
[DllImport("user32", SetLastError = true)]
public static extern IntPtr WindowFromPoint(Point pt);
[DllImport("user32", SetLastError = true)]
public static extern IntPtr GetForegroundWindow();
[DllImport("user32", SetLastError = true)]
public static extern IntPtr GetDC(IntPtr hwnd);
[DllImport("gdi32", SetLastError = true)]
public static extern int GetDeviceCaps(IntPtr hdc, int nIndex);
[DllImport("gdi32", SetLastError = true)]
public static extern bool BitBlt(IntPtr hdcDest, int nXDest, int nYDest, int nWidth, int nHeight, IntPtr hdcSrc, int nXSrc, int nYSrc, uint dwRop);
}
3.
private void SaveScreenShot()
{
try
{
string StatePath = "Document\\Metro\\error.png";
String filepath = Server.MapPath(StatePath);
MemoryStream ms = new MemoryStream();
FileStream fs;
string fname = Server.MapPath(StatePath);
Bitmap bmp = new Bitmap(1280, 1024);//Creating BMP object
Graphics gpx = Graphics.FromImage(bmp);//attaching Graphics
gpx.CopyFromScreen(0, 0, 0, 0, new Size(1280, 1024)); //Doing Copy from Screen to capture Client Scren
bmp.Save(ms, ImageFormat.Gif); //SAVING the BMP in the Memory stream MS
Byte[] buffer = ms.ToArray(); // changing the Memory Stream object to ms
ms.Flush(); //flushing the content in ms.
ms.Close(); //closing the stream
fs = new FileStream(fname, FileMode.Create, FileAccess.ReadWrite);
fs.Write(buffer, 0, Convert.ToInt32(buffer.Length));
fs.Dispose(); //Writing the Content in ”D://errorimg//error.gif”.
}
catch {
Exception ex;
}
}
None of these are working. They work fine on local but not on server. Please suggest me better way to accomplish my task.
I have used selenium browser automation framework. An example:
IWebDriver driver = new InternetExplorerDriver();
driver.Navigate().GoToUrl("http://www.google.com");
Screenshot ss = ((ITakesScreenshot)driver).GetScreenshot();
ss.SaveAsFile("ss.png", System.Drawing.Imaging.ImageFormat.Png);
Normally I would prefere to use Firefox or Chrome, but that is just me.

How do you do AppBar docking (to screen edge, like WinAmp) in WPF?

Is there any complete guidance on doing AppBar docking (such as locking to the screen edge) in WPF? I understand there are InterOp calls that need to be made, but I'm looking for either a proof of concept based on a simple WPF form, or a componentized version that can be consumed.
Related resources:
http://www.codeproject.com/KB/dotnet/AppBar.aspx
http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/05c73c9c-e85d-4ecd-b9b6-4c714a65e72b/
Please Note: This question gathered a good amount of feedback, and some people below have made great points or fixes. Therefore, while I'll keep the code here (and possibly update it), I've also created a WpfAppBar project on github. Feel free to send pull requests.
That same project also builds to a WpfAppBar nuget package
I took the code from the first link provided in the question ( http://www.codeproject.com/KB/dotnet/AppBar.aspx ) and modified it to do two things:
Work with WPF
Be "standalone" - if you put this single file in your project, you can call AppBarFunctions.SetAppBar(...) without any further modification to the window.
This approach doesn't create a base class.
To use, just call this code from anywhere within a normal wpf window (say a button click or the initialize). Note that you can not call this until AFTER the window is initialized, if the HWND hasn't been created yet (like in the constructor), an error will occur.
Make the window an appbar:
AppBarFunctions.SetAppBar( this, ABEdge.Right );
Restore the window to a normal window:
AppBarFunctions.SetAppBar( this, ABEdge.None );
Here's the full code to the file - note you'll want to change the namespace on line 7 to something apropriate.
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Interop;
using System.Windows.Threading;
namespace AppBarApplication
{
public enum ABEdge : int
{
Left = 0,
Top,
Right,
Bottom,
None
}
internal static class AppBarFunctions
{
[StructLayout(LayoutKind.Sequential)]
private struct RECT
{
public int left;
public int top;
public int right;
public int bottom;
}
[StructLayout(LayoutKind.Sequential)]
private struct APPBARDATA
{
public int cbSize;
public IntPtr hWnd;
public int uCallbackMessage;
public int uEdge;
public RECT rc;
public IntPtr lParam;
}
private enum ABMsg : int
{
ABM_NEW = 0,
ABM_REMOVE,
ABM_QUERYPOS,
ABM_SETPOS,
ABM_GETSTATE,
ABM_GETTASKBARPOS,
ABM_ACTIVATE,
ABM_GETAUTOHIDEBAR,
ABM_SETAUTOHIDEBAR,
ABM_WINDOWPOSCHANGED,
ABM_SETSTATE
}
private enum ABNotify : int
{
ABN_STATECHANGE = 0,
ABN_POSCHANGED,
ABN_FULLSCREENAPP,
ABN_WINDOWARRANGE
}
[DllImport("SHELL32", CallingConvention = CallingConvention.StdCall)]
private static extern uint SHAppBarMessage(int dwMessage, ref APPBARDATA pData);
[DllImport("User32.dll", CharSet = CharSet.Auto)]
private static extern int RegisterWindowMessage(string msg);
private class RegisterInfo
{
public int CallbackId { get; set; }
public bool IsRegistered { get; set; }
public Window Window { get; set; }
public ABEdge Edge { get; set; }
public WindowStyle OriginalStyle { get; set; }
public Point OriginalPosition { get; set; }
public Size OriginalSize { get; set; }
public ResizeMode OriginalResizeMode { get; set; }
public IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam,
IntPtr lParam, ref bool handled)
{
if (msg == CallbackId)
{
if (wParam.ToInt32() == (int)ABNotify.ABN_POSCHANGED)
{
ABSetPos(Edge, Window);
handled = true;
}
}
return IntPtr.Zero;
}
}
private static Dictionary<Window, RegisterInfo> s_RegisteredWindowInfo
= new Dictionary<Window, RegisterInfo>();
private static RegisterInfo GetRegisterInfo(Window appbarWindow)
{
RegisterInfo reg;
if( s_RegisteredWindowInfo.ContainsKey(appbarWindow))
{
reg = s_RegisteredWindowInfo[appbarWindow];
}
else
{
reg = new RegisterInfo()
{
CallbackId = 0,
Window = appbarWindow,
IsRegistered = false,
Edge = ABEdge.Top,
OriginalStyle = appbarWindow.WindowStyle,
OriginalPosition =new Point( appbarWindow.Left, appbarWindow.Top),
OriginalSize =
new Size( appbarWindow.ActualWidth, appbarWindow.ActualHeight),
OriginalResizeMode = appbarWindow.ResizeMode,
};
s_RegisteredWindowInfo.Add(appbarWindow, reg);
}
return reg;
}
private static void RestoreWindow(Window appbarWindow)
{
RegisterInfo info = GetRegisterInfo(appbarWindow);
appbarWindow.WindowStyle = info.OriginalStyle;
appbarWindow.ResizeMode = info.OriginalResizeMode;
appbarWindow.Topmost = false;
Rect rect = new Rect(info.OriginalPosition.X, info.OriginalPosition.Y,
info.OriginalSize.Width, info.OriginalSize.Height);
appbarWindow.Dispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle,
new ResizeDelegate(DoResize), appbarWindow, rect);
}
public static void SetAppBar(Window appbarWindow, ABEdge edge)
{
RegisterInfo info = GetRegisterInfo(appbarWindow);
info.Edge = edge;
APPBARDATA abd = new APPBARDATA();
abd.cbSize = Marshal.SizeOf(abd);
abd.hWnd = new WindowInteropHelper(appbarWindow).Handle;
if( edge == ABEdge.None)
{
if( info.IsRegistered)
{
SHAppBarMessage((int)ABMsg.ABM_REMOVE, ref abd);
info.IsRegistered = false;
}
RestoreWindow(appbarWindow);
return;
}
if (!info.IsRegistered)
{
info.IsRegistered = true;
info.CallbackId = RegisterWindowMessage("AppBarMessage");
abd.uCallbackMessage = info.CallbackId;
uint ret = SHAppBarMessage((int)ABMsg.ABM_NEW, ref abd);
HwndSource source = HwndSource.FromHwnd(abd.hWnd);
source.AddHook(new HwndSourceHook(info.WndProc));
}
appbarWindow.WindowStyle = WindowStyle.None;
appbarWindow.ResizeMode = ResizeMode.NoResize;
appbarWindow.Topmost = true;
ABSetPos(info.Edge, appbarWindow);
}
private delegate void ResizeDelegate(Window appbarWindow, Rect rect);
private static void DoResize(Window appbarWindow, Rect rect)
{
appbarWindow.Width = rect.Width;
appbarWindow.Height = rect.Height;
appbarWindow.Top = rect.Top;
appbarWindow.Left = rect.Left;
}
private static void ABSetPos(ABEdge edge, Window appbarWindow)
{
APPBARDATA barData = new APPBARDATA();
barData.cbSize = Marshal.SizeOf(barData);
barData.hWnd = new WindowInteropHelper(appbarWindow).Handle;
barData.uEdge = (int)edge;
if (barData.uEdge == (int)ABEdge.Left || barData.uEdge == (int)ABEdge.Right)
{
barData.rc.top = 0;
barData.rc.bottom = (int)SystemParameters.PrimaryScreenHeight;
if (barData.uEdge == (int)ABEdge.Left)
{
barData.rc.left = 0;
barData.rc.right = (int)Math.Round(appbarWindow.ActualWidth);
}
else
{
barData.rc.right = (int)SystemParameters.PrimaryScreenWidth;
barData.rc.left = barData.rc.right - (int)Math.Round(appbarWindow.ActualWidth);
}
}
else
{
barData.rc.left = 0;
barData.rc.right = (int)SystemParameters.PrimaryScreenWidth;
if (barData.uEdge == (int)ABEdge.Top)
{
barData.rc.top = 0;
barData.rc.bottom = (int)Math.Round(appbarWindow.ActualHeight);
}
else
{
barData.rc.bottom = (int)SystemParameters.PrimaryScreenHeight;
barData.rc.top = barData.rc.bottom - (int)Math.Round(appbarWindow.ActualHeight);
}
}
SHAppBarMessage((int)ABMsg.ABM_QUERYPOS, ref barData);
SHAppBarMessage((int)ABMsg.ABM_SETPOS, ref barData);
Rect rect = new Rect((double)barData.rc.left, (double)barData.rc.top,
(double)(barData.rc.right - barData.rc.left), (double)(barData.rc.bottom - barData.rc.top));
//This is done async, because WPF will send a resize after a new appbar is added.
//if we size right away, WPFs resize comes last and overrides us.
appbarWindow.Dispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle,
new ResizeDelegate(DoResize), appbarWindow, rect);
}
}
}
There is an excellent MSDN article from 1996 which is entertainingly up to date: Extend the Windows 95 Shell with Application Desktop Toolbars. Following its guidance produces an WPF based appbar which handles a number of scenarios that the other answers on this page do not:
Allow dock to any side of the screen
Allow dock to a particular monitor
Allow resizing of the appbar (if desired)
Handle screen layout changes and monitor disconnections
Handle Win + Shift + Left and attempts to minimize or move the window
Handle co-operation with other appbars (OneNote et al.)
Handle per-monitor DPI scaling
I have both a demo app and the implementation of AppBarWindow on GitHub.
Example use:
<apb:AppBarWindow x:Class="WpfAppBarDemo.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:apb="clr-namespace:WpfAppBar;assembly=WpfAppBar"
DataContext="{Binding RelativeSource={RelativeSource Self}}" Title="MainWindow"
DockedWidthOrHeight="200" MinHeight="100" MinWidth="100">
<Grid>
<Button x:Name="btClose" Content="Close" HorizontalAlignment="Left" VerticalAlignment="Top" Width="75" Height="23" Margin="10,10,0,0" Click="btClose_Click"/>
<ComboBox x:Name="cbMonitor" SelectedItem="{Binding Path=Monitor, Mode=TwoWay}" HorizontalAlignment="Left" VerticalAlignment="Top" Width="120" Margin="10,38,0,0"/>
<ComboBox x:Name="cbEdge" SelectedItem="{Binding Path=DockMode, Mode=TwoWay}" HorizontalAlignment="Left" Margin="10,65,0,0" VerticalAlignment="Top" Width="120"/>
<Thumb Width="5" HorizontalAlignment="Right" Background="Gray" x:Name="rzThumb" Cursor="SizeWE" DragCompleted="rzThumb_DragCompleted" />
</Grid>
</apb:AppBarWindow>
Codebehind:
public partial class MainWindow
{
public MainWindow()
{
InitializeComponent();
this.cbEdge.ItemsSource = new[]
{
AppBarDockMode.Left,
AppBarDockMode.Right,
AppBarDockMode.Top,
AppBarDockMode.Bottom
};
this.cbMonitor.ItemsSource = MonitorInfo.GetAllMonitors();
}
private void btClose_Click(object sender, RoutedEventArgs e)
{
Close();
}
private void rzThumb_DragCompleted(object sender, DragCompletedEventArgs e)
{
this.DockedWidthOrHeight += (int)(e.HorizontalChange / VisualTreeHelper.GetDpi(this).PixelsPerDip);
}
}
Changing docked position:
Resizing with thumb:
Cooperation with other appbars:
Clone from GitHub if you want to use it. The library itself is only three files, and can easily be dropped in a project.
Very happy to have found this question. Above class is really useful, but doesnt quite cover all the bases of AppBar implementation.
To fully implement all the behaviour of an AppBar (cope with fullscreen apps etc) you're going to want to read this MSDN article too.
http://msdn.microsoft.com/en-us/library/bb776821.aspx
Sorry for my English... Here is the Philip Rieck's solution with some corrects. It correctly works with Taskbar position and size changes.
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Interop;
using System.Windows.Threading;
namespace wpf_appbar
{
public enum ABEdge : int
{
Left,
Top,
Right,
Bottom,
None
}
internal static class AppBarFunctions
{
[StructLayout(LayoutKind.Sequential)]
private struct RECT
{
public int Left;
public int Top;
public int Right;
public int Bottom;
public RECT(Rect r)
{
Left = (int)r.Left;
Right = (int)r.Right;
Top = (int)r.Top;
Bottom = (int)r.Bottom;
}
public static bool operator ==(RECT r1, RECT r2)
{
return r1.Bottom == r2.Bottom && r1.Left == r2.Left && r1.Right == r2.Right && r1.Top == r2.Top;
}
public static bool operator !=(RECT r1, RECT r2)
{
return !(r1 == r2);
}
public override bool Equals(object obj)
{
return base.Equals(obj);
}
public override int GetHashCode()
{
return base.GetHashCode();
}
}
[StructLayout(LayoutKind.Sequential)]
private struct APPBARDATA
{
public int cbSize;
public IntPtr hWnd;
public int uCallbackMessage;
public int uEdge;
public RECT rc;
public IntPtr lParam;
}
private enum ABMsg : int
{
ABM_NEW = 0,
ABM_REMOVE,
ABM_QUERYPOS,
ABM_SETPOS,
ABM_GETSTATE,
ABM_GETTASKBARPOS,
ABM_ACTIVATE,
ABM_GETAUTOHIDEBAR,
ABM_SETAUTOHIDEBAR,
ABM_WINDOWPOSCHANGED,
ABM_SETSTATE
}
private enum ABNotify : int
{
ABN_STATECHANGE = 0,
ABN_POSCHANGED,
ABN_FULLSCREENAPP,
ABN_WINDOWARRANGE
}
private enum TaskBarPosition : int
{
Left,
Top,
Right,
Bottom
}
[StructLayout(LayoutKind.Sequential)]
class TaskBar
{
public TaskBarPosition Position;
public TaskBarPosition PreviousPosition;
public RECT Rectangle;
public RECT PreviousRectangle;
public int Width;
public int PreviousWidth;
public int Height;
public int PreviousHeight;
public TaskBar()
{
Refresh();
}
public void Refresh()
{
APPBARDATA msgData = new APPBARDATA();
msgData.cbSize = Marshal.SizeOf(msgData);
SHAppBarMessage((int)ABMsg.ABM_GETTASKBARPOS, ref msgData);
PreviousPosition = Position;
PreviousRectangle = Rectangle;
PreviousHeight = Height;
PreviousWidth = Width;
Rectangle = msgData.rc;
Width = Rectangle.Right - Rectangle.Left;
Height = Rectangle.Bottom - Rectangle.Top;
int h = (int)SystemParameters.PrimaryScreenHeight;
int w = (int)SystemParameters.PrimaryScreenWidth;
if (Rectangle.Bottom == h && Rectangle.Top != 0) Position = TaskBarPosition.Bottom;
else if (Rectangle.Top == 0 && Rectangle.Bottom != h) Position = TaskBarPosition.Top;
else if (Rectangle.Right == w && Rectangle.Left != 0) Position = TaskBarPosition.Right;
else if (Rectangle.Left == 0 && Rectangle.Right != w) Position = TaskBarPosition.Left;
}
}
[DllImport("SHELL32", CallingConvention = CallingConvention.StdCall)]
private static extern uint SHAppBarMessage(int dwMessage, ref APPBARDATA pData);
[DllImport("User32.dll", CharSet = CharSet.Auto)]
private static extern int RegisterWindowMessage(string msg);
private class RegisterInfo
{
public int CallbackId { get; set; }
public bool IsRegistered { get; set; }
public Window Window { get; set; }
public ABEdge Edge { get; set; }
public ABEdge PreviousEdge { get; set; }
public WindowStyle OriginalStyle { get; set; }
public Point OriginalPosition { get; set; }
public Size OriginalSize { get; set; }
public ResizeMode OriginalResizeMode { get; set; }
public IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam,
IntPtr lParam, ref bool handled)
{
if (msg == CallbackId)
{
if (wParam.ToInt32() == (int)ABNotify.ABN_POSCHANGED)
{
PreviousEdge = Edge;
ABSetPos(Edge, PreviousEdge, Window);
handled = true;
}
}
return IntPtr.Zero;
}
}
private static Dictionary<Window, RegisterInfo> s_RegisteredWindowInfo
= new Dictionary<Window, RegisterInfo>();
private static RegisterInfo GetRegisterInfo(Window appbarWindow)
{
RegisterInfo reg;
if (s_RegisteredWindowInfo.ContainsKey(appbarWindow))
{
reg = s_RegisteredWindowInfo[appbarWindow];
}
else
{
reg = new RegisterInfo()
{
CallbackId = 0,
Window = appbarWindow,
IsRegistered = false,
Edge = ABEdge.None,
PreviousEdge = ABEdge.None,
OriginalStyle = appbarWindow.WindowStyle,
OriginalPosition = new Point(appbarWindow.Left, appbarWindow.Top),
OriginalSize =
new Size(appbarWindow.ActualWidth, appbarWindow.ActualHeight),
OriginalResizeMode = appbarWindow.ResizeMode,
};
s_RegisteredWindowInfo.Add(appbarWindow, reg);
}
return reg;
}
private static void RestoreWindow(Window appbarWindow)
{
RegisterInfo info = GetRegisterInfo(appbarWindow);
appbarWindow.WindowStyle = info.OriginalStyle;
appbarWindow.ResizeMode = info.OriginalResizeMode;
appbarWindow.Topmost = false;
Rect rect = new Rect(info.OriginalPosition.X, info.OriginalPosition.Y,
info.OriginalSize.Width, info.OriginalSize.Height);
appbarWindow.Dispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle,
new ResizeDelegate(DoResize), appbarWindow, rect);
}
public static void SetAppBar(Window appbarWindow, ABEdge edge)
{
RegisterInfo info = GetRegisterInfo(appbarWindow);
info.Edge = edge;
APPBARDATA abd = new APPBARDATA();
abd.cbSize = Marshal.SizeOf(abd);
abd.hWnd = new WindowInteropHelper(appbarWindow).Handle;
if (edge == ABEdge.None)
{
if (info.IsRegistered)
{
SHAppBarMessage((int)ABMsg.ABM_REMOVE, ref abd);
info.IsRegistered = false;
}
RestoreWindow(appbarWindow);
info.PreviousEdge = info.Edge;
return;
}
if (!info.IsRegistered)
{
info.IsRegistered = true;
info.CallbackId = RegisterWindowMessage("AppBarMessage");
abd.uCallbackMessage = info.CallbackId;
uint ret = SHAppBarMessage((int)ABMsg.ABM_NEW, ref abd);
HwndSource source = HwndSource.FromHwnd(abd.hWnd);
source.AddHook(new HwndSourceHook(info.WndProc));
}
appbarWindow.WindowStyle = WindowStyle.None;
appbarWindow.ResizeMode = ResizeMode.NoResize;
appbarWindow.Topmost = true;
ABSetPos(info.Edge, info.PreviousEdge, appbarWindow);
}
private delegate void ResizeDelegate(Window appbarWindow, Rect rect);
private static void DoResize(Window appbarWindow, Rect rect)
{
appbarWindow.Width = rect.Width;
appbarWindow.Height = rect.Height;
appbarWindow.Top = rect.Top;
appbarWindow.Left = rect.Left;
}
static TaskBar tb = new TaskBar();
private static void ABSetPos(ABEdge edge, ABEdge prevEdge, Window appbarWindow)
{
APPBARDATA barData = new APPBARDATA();
barData.cbSize = Marshal.SizeOf(barData);
barData.hWnd = new WindowInteropHelper(appbarWindow).Handle;
barData.uEdge = (int)edge;
RECT wa = new RECT(SystemParameters.WorkArea);
tb.Refresh();
switch (edge)
{
case ABEdge.Top:
barData.rc.Left = wa.Left - (prevEdge == ABEdge.Left ? (int)Math.Round(appbarWindow.ActualWidth) : 0);
barData.rc.Right = wa.Right + (prevEdge == ABEdge.Right ? (int)Math.Round(appbarWindow.ActualWidth) : 0);
barData.rc.Top = wa.Top - (prevEdge == ABEdge.Top ? (int)Math.Round(appbarWindow.ActualHeight) : 0) - ((tb.Position != TaskBarPosition.Top && tb.PreviousPosition == TaskBarPosition.Top) ? tb.Height : 0) + ((tb.Position == TaskBarPosition.Top && tb.PreviousPosition != TaskBarPosition.Top) ? tb.Height : 0);
barData.rc.Bottom = barData.rc.Top + (int)Math.Round(appbarWindow.ActualHeight);
break;
case ABEdge.Bottom:
barData.rc.Left = wa.Left - (prevEdge == ABEdge.Left ? (int)Math.Round(appbarWindow.ActualWidth) : 0);
barData.rc.Right = wa.Right + (prevEdge == ABEdge.Right ? (int)Math.Round(appbarWindow.ActualWidth) : 0);
barData.rc.Bottom = wa.Bottom + (prevEdge == ABEdge.Bottom ? (int)Math.Round(appbarWindow.ActualHeight) : 0) - 1 + ((tb.Position != TaskBarPosition.Bottom && tb.PreviousPosition == TaskBarPosition.Bottom) ? tb.Height : 0) - ((tb.Position == TaskBarPosition.Bottom && tb.PreviousPosition != TaskBarPosition.Bottom) ? tb.Height : 0);
barData.rc.Top = barData.rc.Bottom - (int)Math.Round(appbarWindow.ActualHeight);
break;
}
SHAppBarMessage((int)ABMsg.ABM_QUERYPOS, ref barData);
switch (barData.uEdge)
{
case (int)ABEdge.Bottom:
if (tb.Position == TaskBarPosition.Bottom && tb.PreviousPosition == tb.Position)
{
barData.rc.Top += (tb.PreviousHeight - tb.Height);
barData.rc.Bottom = barData.rc.Top + (int)appbarWindow.ActualHeight;
}
break;
case (int)ABEdge.Top:
if (tb.Position == TaskBarPosition.Top && tb.PreviousPosition == tb.Position)
{
if (tb.PreviousHeight - tb.Height > 0) barData.rc.Top -= (tb.PreviousHeight - tb.Height);
barData.rc.Bottom = barData.rc.Top + (int)appbarWindow.ActualHeight;
}
break;
}
SHAppBarMessage((int)ABMsg.ABM_SETPOS, ref barData);
Rect rect = new Rect((double)barData.rc.Left, (double)barData.rc.Top, (double)(barData.rc.Right - barData.rc.Left), (double)(barData.rc.Bottom - barData.rc.Top));
appbarWindow.Dispatcher.BeginInvoke(new ResizeDelegate(DoResize), DispatcherPriority.ApplicationIdle, appbarWindow, rect);
}
}
}
The same code you can write for the Left and Right edges.
Good job, Philip Rieck, thank you!
I modified code from Philip Rieck (btw. Thanks a lot) to work in multiple display settings. Here's my solution.
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Interop;
using System.Windows.Threading;
namespace AppBarApplication
{
public enum ABEdge : int
{
Left = 0,
Top,
Right,
Bottom,
None
}
internal static class AppBarFunctions
{
[StructLayout(LayoutKind.Sequential)]
private struct RECT
{
public int left;
public int top;
public int right;
public int bottom;
}
[StructLayout(LayoutKind.Sequential)]
private struct APPBARDATA
{
public int cbSize;
public IntPtr hWnd;
public int uCallbackMessage;
public int uEdge;
public RECT rc;
public IntPtr lParam;
}
[StructLayout(LayoutKind.Sequential)]
private struct MONITORINFO
{
public int cbSize;
public RECT rcMonitor;
public RECT rcWork;
public int dwFlags;
}
private enum ABMsg : int
{
ABM_NEW = 0,
ABM_REMOVE,
ABM_QUERYPOS,
ABM_SETPOS,
ABM_GETSTATE,
ABM_GETTASKBARPOS,
ABM_ACTIVATE,
ABM_GETAUTOHIDEBAR,
ABM_SETAUTOHIDEBAR,
ABM_WINDOWPOSCHANGED,
ABM_SETSTATE
}
private enum ABNotify : int
{
ABN_STATECHANGE = 0,
ABN_POSCHANGED,
ABN_FULLSCREENAPP,
ABN_WINDOWARRANGE
}
[DllImport("SHELL32", CallingConvention = CallingConvention.StdCall)]
private static extern uint SHAppBarMessage(int dwMessage, ref APPBARDATA pData);
[DllImport("User32.dll", CharSet = CharSet.Auto)]
private static extern int RegisterWindowMessage(string msg);
[DllImport("User32.dll", CharSet = CharSet.Auto)]
private static extern IntPtr MonitorFromWindow(IntPtr hwnd, uint dwFlags);
[DllImport("User32.dll", CharSet = CharSet.Auto)]
private static extern bool GetMonitorInfo(IntPtr hMonitor, ref MONITORINFO mi);
private const int MONITOR_DEFAULTTONEAREST = 0x2;
private const int MONITORINFOF_PRIMARY = 0x1;
private class RegisterInfo
{
public int CallbackId { get; set; }
public bool IsRegistered { get; set; }
public Window Window { get; set; }
public ABEdge Edge { get; set; }
public WindowStyle OriginalStyle { get; set; }
public Point OriginalPosition { get; set; }
public Size OriginalSize { get; set; }
public ResizeMode OriginalResizeMode { get; set; }
public IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam,
IntPtr lParam, ref bool handled)
{
if (msg == CallbackId)
{
if (wParam.ToInt32() == (int)ABNotify.ABN_POSCHANGED)
{
ABSetPos(Edge, Window);
handled = true;
}
}
return IntPtr.Zero;
}
}
private static Dictionary<Window, RegisterInfo> s_RegisteredWindowInfo
= new Dictionary<Window, RegisterInfo>();
private static RegisterInfo GetRegisterInfo(Window appbarWindow)
{
RegisterInfo reg;
if (s_RegisteredWindowInfo.ContainsKey(appbarWindow))
{
reg = s_RegisteredWindowInfo[appbarWindow];
}
else
{
reg = new RegisterInfo()
{
CallbackId = 0,
Window = appbarWindow,
IsRegistered = false,
Edge = ABEdge.Top,
OriginalStyle = appbarWindow.WindowStyle,
OriginalPosition = new Point(appbarWindow.Left, appbarWindow.Top),
OriginalSize =
new Size(appbarWindow.ActualWidth, appbarWindow.ActualHeight),
OriginalResizeMode = appbarWindow.ResizeMode,
};
s_RegisteredWindowInfo.Add(appbarWindow, reg);
}
return reg;
}
private static void RestoreWindow(Window appbarWindow)
{
RegisterInfo info = GetRegisterInfo(appbarWindow);
appbarWindow.WindowStyle = info.OriginalStyle;
appbarWindow.ResizeMode = info.OriginalResizeMode;
appbarWindow.Topmost = false;
Rect rect = new Rect(info.OriginalPosition.X, info.OriginalPosition.Y,
info.OriginalSize.Width, info.OriginalSize.Height);
appbarWindow.Dispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle,
new ResizeDelegate(DoResize), appbarWindow, rect);
}
public static void SetAppBar(Window appbarWindow, ABEdge edge)
{
RegisterInfo info = GetRegisterInfo(appbarWindow);
info.Edge = edge;
APPBARDATA abd = new APPBARDATA();
abd.cbSize = Marshal.SizeOf(abd);
abd.hWnd = new WindowInteropHelper(appbarWindow).Handle;
if (edge == ABEdge.None)
{
if (info.IsRegistered)
{
SHAppBarMessage((int)ABMsg.ABM_REMOVE, ref abd);
info.IsRegistered = false;
}
RestoreWindow(appbarWindow);
return;
}
if (!info.IsRegistered)
{
info.IsRegistered = true;
info.CallbackId = RegisterWindowMessage("AppBarMessage");
abd.uCallbackMessage = info.CallbackId;
uint ret = SHAppBarMessage((int)ABMsg.ABM_NEW, ref abd);
HwndSource source = HwndSource.FromHwnd(abd.hWnd);
source.AddHook(new HwndSourceHook(info.WndProc));
}
appbarWindow.WindowStyle = WindowStyle.None;
appbarWindow.ResizeMode = ResizeMode.NoResize;
appbarWindow.Topmost = true;
ABSetPos(info.Edge, appbarWindow);
}
private delegate void ResizeDelegate(Window appbarWindow, Rect rect);
private static void DoResize(Window appbarWindow, Rect rect)
{
appbarWindow.Width = rect.Width;
appbarWindow.Height = rect.Height;
appbarWindow.Top = rect.Top;
appbarWindow.Left = rect.Left;
}
private static void GetActualScreenData(ABEdge edge, Window appbarWindow, ref int leftOffset, ref int topOffset, ref int actualScreenWidth, ref int actualScreenHeight)
{
IntPtr handle = new WindowInteropHelper(appbarWindow).Handle;
IntPtr monitorHandle = MonitorFromWindow(handle, MONITOR_DEFAULTTONEAREST);
MONITORINFO mi = new MONITORINFO();
mi.cbSize = Marshal.SizeOf(mi);
if (GetMonitorInfo(monitorHandle, ref mi))
{
if (mi.dwFlags == MONITORINFOF_PRIMARY)
{
return;
}
leftOffset = mi.rcWork.left;
topOffset = mi.rcWork.top;
actualScreenWidth = mi.rcWork.right - leftOffset;
actualScreenHeight = mi.rcWork.bottom - mi.rcWork.top;
}
}
private static void ABSetPos(ABEdge edge, Window appbarWindow)
{
APPBARDATA barData = new APPBARDATA();
barData.cbSize = Marshal.SizeOf(barData);
barData.hWnd = new WindowInteropHelper(appbarWindow).Handle;
barData.uEdge = (int)edge;
int leftOffset = 0;
int topOffset = 0;
int actualScreenWidth = (int)SystemParameters.PrimaryScreenWidth;
int actualScreenHeight = (int)SystemParameters.PrimaryScreenHeight;
GetActualScreenData(edge, appbarWindow, ref leftOffset, ref topOffset, ref actualScreenWidth, ref actualScreenHeight);
if (barData.uEdge == (int)ABEdge.Left || barData.uEdge == (int)ABEdge.Right)
{
barData.rc.top = topOffset;
barData.rc.bottom = actualScreenHeight;
if (barData.uEdge == (int)ABEdge.Left)
{
barData.rc.left = leftOffset;
barData.rc.right = (int)Math.Round(appbarWindow.ActualWidth) + leftOffset;
}
else
{
barData.rc.right = actualScreenWidth + leftOffset;
barData.rc.left = barData.rc.right - (int)Math.Round(appbarWindow.ActualWidth);
}
}
else
{
barData.rc.left = leftOffset;
barData.rc.right = actualScreenWidth + leftOffset;
if (barData.uEdge == (int)ABEdge.Top)
{
barData.rc.top = topOffset;
barData.rc.bottom = (int)Math.Round(appbarWindow.ActualHeight) + topOffset;
}
else
{
barData.rc.bottom = actualScreenHeight + topOffset;
barData.rc.top = barData.rc.bottom - (int)Math.Round(appbarWindow.ActualHeight);
}
}
SHAppBarMessage((int)ABMsg.ABM_QUERYPOS, ref barData);
SHAppBarMessage((int)ABMsg.ABM_SETPOS, ref barData);
Rect rect = new Rect((double)barData.rc.left, (double)barData.rc.top,
(double)(barData.rc.right - barData.rc.left), (double)(barData.rc.bottom - barData.rc.top));
//This is done async, because WPF will send a resize after a new appbar is added.
//if we size right away, WPFs resize comes last and overrides us.
appbarWindow.Dispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle,
new ResizeDelegate(DoResize), appbarWindow, rect);
}
}
}
As a commercial alternative, see the ready-to-use ShellAppBar component for WPF which supports all cases and secnarios such as taskbar docked to left,right,top,bottom edge, support for multiple monitors, drag-docking, autohide , etc etc. It may save you time and money over trying to handle all these cases yourself.
DISCLAIMER: I work for LogicNP Software, the developer of ShellAppBar.
Sorry, the last code I posted didn't work when the Taskbar is resized. The following code change seems to work better:
SHAppBarMessage((int)ABMsg.ABM_QUERYPOS, ref barData);
if (barData.uEdge == (int)ABEdge.Top)
barData.rc.bottom = barData.rc.top + (int)Math.Round(appbarWindow.ActualHeight);
else if (barData.uEdge == (int)ABEdge.Bottom)
barData.rc.top = barData.rc.bottom - (int)Math.Round(appbarWindow.ActualHeight);
SHAppBarMessage((int)ABMsg.ABM_SETPOS, ref barData);
I've spent some weeks exploring this challenge and finally created a very solid NuGet package delivering this functionality in very friendly way. Simply create a new WPF app then change the main window's class from Window to DockWindow (in the XAML) and that's it!
Get the package here and see the Git repo for a demonstration app.

Categories

Resources