C# Desktop background displaying incorrect location maybe? - c#

Hi I am currently creating an animated desktop background that uses axWindowsMideaPlayer to play video files in a loop I take the WMP and set its handle to the desktop handle, it works great on single display monitor setup but fails with multiple monitors. Why it is failing is because monitors can have their own set position like left/right/topleft/topright/top/bottom/bottomleft and bottom right of the primary screen which puts their position in the negatives and so forth.. My question is how can I position each WMP correctly on each monitor? Here is what I have so far,
this is how I get each monitor...
public class DisplayInfo
{
public bool isPrimary { get; set; }
public int ScreenHeight { get; set; }
public int ScreenWidth { get; set; }
public Rect MonitorArea { get; set; }
public Rect WorkArea { get; set; }
public string DeviceName { get; set; }
}
/// <summary>
/// Collection of display information
/// </summary>
public class DisplayInfoCollection : List<DisplayInfo>
{
// size of a device name string
private const int CCHDEVICENAME = 32;
/// <summary>
/// The MONITORINFOEX structure contains information about a display monitor.
/// The GetMonitorInfo function stores information into a MONITORINFOEX structure or a MONITORINFO structure.
/// The MONITORINFOEX structure is a superset of the MONITORINFO structure. The MONITORINFOEX structure adds a string member to contain a name
/// for the display monitor.
/// </summary>
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
internal struct MONITORINFOEX
{
/// <summary>
/// The size, in bytes, of the structure. Set this member to sizeof(MONITORINFOEX) (72) before calling the GetMonitorInfo function.
/// Doing so lets the function determine the type of structure you are passing to it.
/// </summary>
public int Size;
/// <summary>
/// A RECT structure that specifies the display monitor rectangle, expressed in virtual-screen coordinates.
/// Note that if the monitor is not the primary display monitor, some of the rectangle's coordinates may be negative values.
/// </summary>
public Rect Monitor;
/// <summary>
/// A RECT structure that specifies the work area rectangle of the display monitor that can be used by applications,
/// expressed in virtual-screen coordinates. Windows uses this rectangle to maximize an application on the monitor.
/// The rest of the area in rcMonitor contains system windows such as the task bar and side bars.
/// Note that if the monitor is not the primary display monitor, some of the rectangle's coordinates may be negative values.
/// </summary>
public Rect WorkArea;
/// <summary>
/// The attributes of the display monitor.
///
/// This member can be the following value:
/// 1 : MONITORINFOF_PRIMARY
/// </summary>
public uint Flags;
/// <summary>
/// A string that specifies the device name of the monitor being used. Most applications have no use for a display monitor name,
/// and so can save some bytes by using a MONITORINFO structure.
/// </summary>
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCHDEVICENAME)]
public string DeviceName;
public void Init()
{
this.Size = 40 + 2 * CCHDEVICENAME;
this.DeviceName = string.Empty;
}
}
[DllImport("user32.dll")]
private static extern bool EnumDisplayMonitors(IntPtr hdc, IntPtr lprcClip,
EnumMonitorsDelegate lpfnEnum, IntPtr dwData);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern bool GetMonitorInfo(IntPtr hMonitor, ref MONITORINFOEX lpmi);
/*
[DllImport("user32.dll")]
static extern bool GetMonitorInfo(IntPtr hMonitor, ref MONITORINFO lpmi);
*/
/// <summary>
/// Returns the number of Displays using the Win32 functions
/// </summary>
/// <returns>collection of Display Info</returns>
public static DisplayInfoCollection GetDisplays()
{
DisplayInfoCollection col = new DisplayInfoCollection();
EnumDisplayMonitors(IntPtr.Zero, IntPtr.Zero,
delegate (IntPtr hMonitor, IntPtr hdcMonitor, ref Rect lprcMonitor, IntPtr dwData)
{
MONITORINFOEX mi = new MONITORINFOEX();
mi.Size = (int)Marshal.SizeOf(mi);
bool success = GetMonitorInfo(hMonitor, ref mi);
if (success)
{
DisplayInfo di = new DisplayInfo();
di.ScreenWidth = (mi.Monitor.Right - mi.Monitor.Left);
di.ScreenHeight = (mi.Monitor.Bottom - mi.Monitor.Top);
di.MonitorArea = mi.Monitor;
di.WorkArea = mi.WorkArea;
di.isPrimary = Convert.ToBoolean(mi.Flags);
di.DeviceName = mi.DeviceName;
col.Add(di);
}
return true;
}, IntPtr.Zero);
return col;
}
}
this is how I have tried to call it and use it but it puts WMP all over the place depending on where the monitors are positioned.
DisplayInfoCollection dic = DisplayInfoCollection.GetDisplays();
int count = 0;
int totalPosX = 0;
int totalPosY = 0;
DisplayInfo dInfo = null;
List<DisplayInfo> di = dic.OrderByDescending(d => d.isPrimary).ToList();
var or = SystemInformation.VirtualScreen;
foreach (DisplayInfo dm in di)
{
bool zeroOutX = false;
bool zeroOutY = false;
if (dm.isPrimary)
{
totalPosX = or.Left > 0 ? or.Left : -or.Left;
totalPosY = or.Top > 0 ? or.Top : -or.Top;
dInfo = dm;
}
else
{
bool left = false;
bool top = false;
bool right = false;
bool bottom = false;
bool topLeft = false;
bool topRight = false;
bool bottomLeft = false;
bool bottomRight = false;
int posY = dm.MonitorArea.Top > 0 ? dm.MonitorArea.Top : -dm.MonitorArea.Top;
if (dm.MonitorArea.Left < 0)
{
left = true;
}
else
right = dm.MonitorArea.Left > 0;
if (dm.MonitorArea.Top < 0)
{
top = true;
}
else
bottom = dm.MonitorArea.Top > 0;
bool center = (dm.MonitorArea.Left > 0 ?
dm.MonitorArea.Left : -dm.MonitorArea.Left) > 0 ||
(dm.MonitorArea.Left > 0 ?
dm.MonitorArea.Left : -dm.MonitorArea.Left) < dInfo.ScreenWidth;
topLeft = left && top;
topRight = right && top;
bottomLeft = left && bottom;
bottomRight = right && bottom;
if (topLeft || topRight || bottomLeft || bottomRight || left || right)
{
if (dm.MonitorArea.Left < 0)
zeroOutX = true;
else
totalPosX += dInfo.ScreenWidth;
if (dm.MonitorArea.Top < 0)
zeroOutY = true;
else
totalPosY += dm.MonitorArea.Top;
}
dInfo = dm;
}
Display display = new Display(dm.DeviceName, dm.ScreenWidth,
dm.ScreenHeight, zeroOutX ? 0 : totalPosX,
zeroOutY ? 0 : totalPosY, Controls, count);
Displays.Add(display);
count++;
}
I cant find much help on this matter and have tried numerous ways to do this its a c# windows form and I am new-ish to programming my knowledge is limited in this any help will be appreciated thanks in advance.

According to the EnumDisplaySettings:
The EnumDisplaySettings function sets values for the following five
DEVMODE members:
dmBitsPerPel
dmPelsWidth
dmPelsHeight
dmDisplayFlags
dmDisplayFrequency
(Excluding dmPosition), You should try to use EnumDisplaySettingsEx, and specify DM_POSITION to get the correct dmPosition value.

thanks for all that helped I found a solution to my problem here it is if anyone need to know.
var or = SystemInformation.VirtualScreen;
foreach (DisplayInfo dm in dic)
{
int x = or.Left > 0 ? or.Left : -or.Left;
int y = or.Top > 0 ? or.Top : -or.Top;
if (dm.isPrimary)
{
Rect rect = new Rect();
rect.Left = x;
rect.Top = y;
rect.Right = rect.Left + dm.ScreenWidth;
rect.Bottom = rect.Top + dm.ScreenHeight;
dm.MonitorArea = rect;
}
else
{
Rect rect = new Rect();
rect.Left = x + dm.MonitorArea.Left;
rect.Top = y + dm.MonitorArea.Top;
rect.Right = rect.Left + dm.ScreenWidth;
rect.Bottom = rect.Top + dm.ScreenHeight;
dm.MonitorArea = rect;
}

Related

Get volumes files from multi part rar file c#

I have multi-part RAR files in the same folder and I want to check if all parts of the RAR exist. I am using SharpCompress v0.24, here is my code:
using (var archive = RarArchive.Open("D:/Ontology tool.part1.rar"))
{
foreach (RarVolume vol in archive.Volumes)
{
MessageBox.Show(vol.IsMultiVolume + " \n"+ vol.IsFirstVolume+"\n"+ vol.IsSolidArchive);
}
}
But I cannot get the volume full file name.
I then use SevenZipSharp v 0.64 and 7z.dll version 19, here is my code:
using (var extractor = new SevenZipExtractor("D:/Ontology tool.part1.rar"))
{
MessageBox.Show(extractor.VolumeFileNames[0]+"");
}
But then I get the error:
Invalid archive open/read error! Is it encrypted and a wrong
password was provided? If your archive is an exotic one, it is
possible that SevenZipSharp has no signature for its format and thus
decided it is TAR by mistake
Note that the WinRAR program seems it make changes of RAR file formats because I create a RAR file with version 5.71 and when I try to open it with old WinRAR version it not opened ok, I mean files are not in the correct format.
The same applies to SevenZipSharp. If I open an old RAR file I created since 2014 it opens ok, but when I open a RAR file created with WinRAR v 5.71 it raises this error.
So now how can I get all parts files names of multi part RAR file?
Thanks for your help.
I end up using winrar.exe , send command to test file like this:
[DllImport("User32.dll")]
static extern int SetForegroundWindow(IntPtr point);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool GetWindowPlacement(IntPtr hWnd, ref WINDOWPLACEMENT lpwndpl);
[DllImport("user32.dll")]
static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
private const int SW_MAXIMIZE = 3;
private const int SW_MINIMIZE = 6;
private struct WINDOWPLACEMENT
{
public int length;
public int flags;
public int showCmd;
public System.Drawing.Point ptMinPosition;
public System.Drawing.Point ptMaxPosition;
public System.Drawing.Rectangle rcNormalPosition;
}
private enum WindowShowStyle : uint
{
/// <summary>Hides the window and activates another window.</summary>
/// <remarks>See SW_HIDE</remarks>
Hide = 0,
/// <summary>Activates and displays a window. If the window is minimized
/// or maximized, the system restores it to its original size and
/// position. An application should specify this flag when displaying
/// the window for the first time.</summary>
/// <remarks>See SW_SHOWNORMAL</remarks>
ShowNormal = 1,
/// <summary>Activates the window and displays it as a minimized window.</summary>
/// <remarks>See SW_SHOWMINIMIZED</remarks>
ShowMinimized = 2,
/// <summary>Activates the window and displays it as a maximized window.</summary>
/// <remarks>See SW_SHOWMAXIMIZED</remarks>
ShowMaximized = 3,
/// <summary>Maximizes the specified window.</summary>
/// <remarks>See SW_MAXIMIZE</remarks>
Maximize = 3,
/// <summary>Displays a window in its most recent size and position.
/// This value is similar to "ShowNormal", except the window is not
/// actived.</summary>
/// <remarks>See SW_SHOWNOACTIVATE</remarks>
ShowNormalNoActivate = 4,
/// <summary>Activates the window and displays it in its current size
/// and position.</summary>
/// <remarks>See SW_SHOW</remarks>
Show = 5,
/// <summary>Minimizes the specified window and activates the next
/// top-level window in the Z order.</summary>
/// <remarks>See SW_MINIMIZE</remarks>
Minimize = 6,
/// <summary>Displays the window as a minimized window. This value is
/// similar to "ShowMinimized", except the window is not activated.</summary>
/// <remarks>See SW_SHOWMINNOACTIVE</remarks>
ShowMinNoActivate = 7,
/// <summary>Displays the window in its current size and position. This
/// value is similar to "Show", except the window is not activated.</summary>
/// <remarks>See SW_SHOWNA</remarks>
ShowNoActivate = 8,
/// <summary>Activates and displays the window. If the window is
/// minimized or maximized, the system restores it to its original size
/// and position. An application should specify this flag when restoring
/// a minimized window.</summary>
/// <remarks>See SW_RESTORE</remarks>
Restore = 9,
/// <summary>Sets the show state based on the SW_ value specified in the
/// STARTUPINFO structure passed to the CreateProcess function by the
/// program that started the application.</summary>
/// <remarks>See SW_SHOWDEFAULT</remarks>
ShowDefault = 10,
/// <summary>Windows 2000/XP: Minimizes a window, even if the thread
/// that owns the window is hung. This flag should only be used when
/// minimizing windows from a different thread.</summary>
/// <remarks>See SW_FORCEMINIMIZE</remarks>
ForceMinimized = 11
}
[DllImport("user32.dll")]
private static extern int GetWindowText(IntPtr hWnd, StringBuilder text, int count);
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll")]
public static extern IntPtr GetParent(IntPtr hWnd);
const int SW_RESTORE = 9;
[DllImport("user32.dll")]
static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
[DllImport("user32.dll")]
static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);
[DllImport("user32.dll")]
static extern bool MoveWindow(IntPtr Handle, int x, int y, int w, int h, bool repaint);
static readonly int GWL_STYLE = -16;
static readonly int WS_VISIBLE = 0x10000000;
....
private string GetMissingCompressedFile(string FilePath, string WinRARPath)
{
string MissingCompressedFile = "";
Process p = new Process();
p.StartInfo.FileName = WinRARPath;
p.EnableRaisingEvents = true;
// -ibck -inul
p.StartInfo.Arguments = " t " + " \"" + FilePath + "\"";
//p.StartInfo.UseShellExecute = false;
// p.StartInfo.RedirectStandardOutput = true;
//p.StartInfo.RedirectStandardError = true;
//p.StartInfo.CreateNoWindow = true;
p.StartInfo.WindowStyle = ProcessWindowStyle.Minimized;
IntPtr PanelHandler=new IntPtr();
WinRARPanel_Ref.Invoke((MethodInvoker)delegate
{
PanelHandler = WinRARPanel_Ref.Handle;
});
p.Start();
IntPtr h = p.MainWindowHandle;
while (true)
{
if (p.HasExited)
break;
if (p.MainWindowHandle != IntPtr.Zero)
break;
Thread.Sleep(100); // Don't hog the CPU
p.Refresh(); // You need this since `MainWindowHandle` is cached
}
if (!p.HasExited)
{
SetParent(p.MainWindowHandle, PanelHandler);
//SetWindowLong(p.MainWindowHandle, GWL_STYLE, WS_VISIBLE);
//MoveWindow(p.MainWindowHandle, 0, 0, WinRARPanel_Ref.Width, WinRARPanel_Ref.Height, true);
}
while (!p.HasExited)
{
var _windowHandle = FindWindow(null, "Next volume is required");
var _parent = GetParent(_windowHandle);
if (_parent == p.MainWindowHandle)
{
if (!p.HasExited)
{
SetParent(_windowHandle, WinRARPanel_Ref.Handle);
}
Thread.Sleep(1000);
if (!p.HasExited)
{
MainForm_Ref.Invoke((MethodInvoker)delegate
{
Clipboard.Clear();
ShowWindow(h, SW_RESTORE);
SetForegroundWindow(h);
SendKeys.SendWait("^(c)");
string ClipboardText = Clipboard.GetText();
if (!string.IsNullOrEmpty(ClipboardText))
{
try
{
string n = new FileInfo(ClipboardText).Name;
MissingCompressedFile = ClipboardText;
if (!p.HasExited)
{
p.Kill();
}
}
catch
{
if (ClipboardText.Contains("You need to start extraction from a previous volume to unpack"))
{
MissingCompressedFile = "";
p.Kill();
}
}
Clipboard.Clear();
}
});
}
}
else
{
if (!p.HasExited)
{
MainForm_Ref.Invoke((MethodInvoker)delegate
{
Clipboard.Clear();
if (!p.HasExited)
{
ShowWindow(h, SW_RESTORE);
SetForegroundWindow(h);
}
SendKeys.SendWait("^(c)");
string ClipboardText = Clipboard.GetText();
if (!string.IsNullOrEmpty(ClipboardText))
{
try
{
string n = new FileInfo(ClipboardText).Name;
MissingCompressedFile = ClipboardText;
if (!p.HasExited)
{
p.Kill();
}
}
catch
{
if (ClipboardText.Contains("You need to start extraction from a previous volume to unpack"))
{
MissingCompressedFile = "";
p.Kill();
}
}
Clipboard.Clear();
}
});
}
}
}
if (!p.HasExited)
{
p.Kill();
}
p = null;
return MissingCompressedFile;
}

Simulate Windows Dragging Effect in a Winform FlowLayoutPanel

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 ...

Unable to load most image formats as textures in OpenGL

I'm attempting to load a variety of image formats to display with alpha blending in OpenGL for our company's software, but I seem to have no luck on this. I'm trying to load Bitmaps like this:
private Bitmap LoadBitmap(string filename)
{
Bitmap original = new Bitmap(filename);
Bitmap toReturn = new Bitmap(original.Width, original.Height, PixelFormat.Format24bppRgb);
using (Graphics gr = Graphics.FromImage(toReturn))
{
gr.DrawImage(original, new Rectangle(0, 0, toReturn.Width, toReturn.Height));
}
original.Dispose();
return toReturn;
}
Loading it into a texture like this:
private uint[] GetTexture(Bitmap convertToTexture)
{
// Setup return value
uint[] toReturn = null;
// Make sure the device and the bitmap exists
if (convertToTexture != null)
{
// Dispose the imagery first
DisposeTexture();
mImageryTexture = new uint[1];
// Setup the bitmap
Rectangle rect = new Rectangle(Point.Empty, convertToTexture.Size);
BitmapData bitmapdata = convertToTexture.LockBits(rect, ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
// Bind the texture
glGenTextures(1, mImageryTexture);
glBindTexture(GL_TEXTURE_2D, mImageryTexture[0]);
glTexImage2D(GL_TEXTURE_2D, 0, (int)GL_RGB8, convertToTexture.Width, convertToTexture.Height, 0, GL_BGR_EXT, GL_UNSIGNED_BYTE, bitmapdata.Scan0);
// Linear Filtering
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// Clamp
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
// Release the bitmap
convertToTexture.UnlockBits(bitmapdata);
// Set the return value to this imagery
toReturn = mImageryTexture;
}
return toReturn;
}
And using the texture like this:
public bool DrawGLScene()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
// Test bitmap
Rectangle rect = new Rectangle();
rect.Size = testBitmap.Size;
RenderQuad(testBitmap, rect);
RenderLines(rect);
rect.X += rect.Width;
// Test JPEG
rect.Size = testJpeg.Size;
RenderQuad(testJpeg, rect);
RenderLines(rect);
rect.X += rect.Width;
// Test PNG
rect.Size = testPng.Size;
RenderQuad(testPng, rect);
RenderLines(rect);
rect.X += rect.Width;
// Test GIF
rect.Size = testGif.Size;
RenderQuad(testGif, rect);
RenderLines(rect);
return true;
}
private void RenderQuad(Bitmap image, Rectangle rect)
{
uint[] imageryTexture = GetTexture(image);
// Color all the vertices white with transparency
glEnable(GL_TEXTURE_2D);
glColor4ub(255, 255, 255, 255);
///////////////////////////////////////////////////
// Draw the image of the area.
///////////////////////////////////////////////////
// Fill the path with the background transparent color.
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
glBindTexture(GL_TEXTURE_2D, imageryTexture[0]);
// Building a quad of the image
glBegin(GL_QUADS);
// top left of texture
glTexCoord2f(0, 1);
glVertex2i(rect.Left, rect.Top);
// top right of texture
glTexCoord2f(1, 1);
glVertex2i(rect.Right, rect.Top);
// bottom right of texture
glTexCoord2f(1, 0);
glVertex2i(rect.Right, rect.Bottom);
// bottom left of texture
glTexCoord2f(0, 0);
glVertex2i(rect.Left, rect.Bottom);
glEnd();
}
private void RenderLines(Rectangle rect)
{
// Fill the path with the background transparent color.
// Color all the vertices transparent gray
Color LineColor = Color.Tomato;
glColor4ub(LineColor.R, LineColor.G, LineColor.B, 255);
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
// Building a quad of the image
glBegin(GL_QUADS);
// top left of texture
glVertex2i(rect.Left, rect.Top);
// top right of texture
glVertex2i(rect.Right, rect.Top);
// bottom right of texture
glVertex2i(rect.Right, rect.Bottom);
// bottom left of texture
glVertex2i(rect.Left, rect.Bottom);
glEnd();
}
What am I doing wrong? I can load some bitmap files, but not others. PNGs, JPEGs, and GIFs don't work at all. All I get are white squares (from the vertex colors).
Here's the full code below. I'm using NeHe's lesson 2 code as basis for this code (since I can't post the company software code in its entirety):
public class OpenGLForm : Form
{
#region Member Variables
private const string AbsolutePath = #"C:\<path-to-images>\";
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.Container components = null;
private Bitmap testBitmap = null;
private Bitmap testJpeg = null;
private Bitmap testPng = null;
private Bitmap testGif = null;
private uint[] mImageryTexture = null;
private static uint _hwnd = 0;
private static uint _hDC = 0;
private static uint _hRC = 0;
private bool _appActive = true;
private bool _done = true;
public bool Done
{
get
{
return _done;
}
set
{
_done = value;
}
}
#endregion
// Lots of OpenGL function and constant declaration here
#region Win32 Interop
// Constant values were found in the "WinUser.h" header file.
public const int WM_ACTIVATEAPP = 0x001C;
public const int WA_ACTIVE = 1;
public const int WA_CLICKACTIVE = 2;
public const int CDS_FULLSCREEN = 0x00000004; // Flag for ChangeDisplaySettings
public const int DISP_CHANGE_SUCCESSFUL = 0; // Return value for ChangeDisplaySettings
// Constant values were found in the "WinGDI.h" header file.
public const int CCHDEVICENAME = 32; // size of a device name string
public const int CCHFORMNAME = 32; // size of a form name string
public const int DM_BITSPERPEL = 0x40000;
public const int DM_PELSWIDTH = 0x80000;
public const int DM_PELSHEIGHT = 0x100000;
public const int BITSPIXEL = 12; // number of bits per pixel
public const uint PFD_DOUBLEBUFFER = 0x00000001; // PIXELFORMATDESCRIPTOR flag
public const uint PFD_DRAW_TO_WINDOW = 0x00000004; // PIXELFORMATDESCRIPTOR flag
public const uint PFD_SUPPORT_OPENGL = 0x00000020; // PIXELFORMATDESCRIPTOR flag
public const uint PFD_TYPE_RGBA = 0; // pixel type
public const uint PFD_MAIN_PLANE = 0; // layer type
[StructLayout(LayoutKind.Sequential)]
public struct PIXELFORMATDESCRIPTOR
{
public ushort nSize;
public ushort nVersion;
public uint dwFlags;
public byte iPixelType;
public byte cColorBits;
public byte cRedBits;
public byte cRedShift;
public byte cGreenBits;
public byte cGreenShift;
public byte cBlueBits;
public byte cBlueShift;
public byte cAlphaBits;
public byte cAlphaShift;
public byte cAccumBits;
public byte cAccumRedBits;
public byte cAccumGreenBits;
public byte cAccumBlueBits;
public byte cAccumAlphaBits;
public byte cDepthBits;
public byte cStencilBits;
public byte cAuxBuffers;
public byte iLayerType;
public byte bReserved;
public uint dwLayerMask;
public uint dwVisibleMask;
public uint dwDamageMask;
}
// by marking the structure with CharSet.Auto, the structure will get marshaled as Unicode characters
// on Unicode platforms, if not the name fields would always get marshaled as arrays of ANSI characters
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public class DEVMODE
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = CCHDEVICENAME)]
public char[] dmDeviceName;
public short dmSpecVersion;
public short dmDriverVersion;
public short dmSize;
public short dmDriverExtra;
public int dmFields;
public DEVMODE_UNION u;
public short dmColor;
public short dmDuplex;
public short dmYResolution;
public short dmTTOption;
public short dmCollate;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = CCHFORMNAME)]
public char[] dmFormName;
public short dmLogPixels;
public int dmBitsPerPel;
public int dmPelsWidth;
public int dmPelsHeight;
public int dmDisplayFlagsOrdmNup; // union of dmDisplayFlags and dmNup
public int dmDisplayFrequency;
public int dmICMMethod;
public int dmICMIntent;
public int dmMediaType;
public int dmDitherType;
public int dmReserved1;
public int dmReserved2;
public int dmPanningWidth;
public int dmPanningHeight;
}
// modeling a union in C#, each possible struct data type starts at FieldOffset 0
[StructLayout(LayoutKind.Explicit)]
public struct DEVMODE_UNION
{
[FieldOffset(0)]
public short dmOrientation;
[FieldOffset(2)]
public short dmPaperSize;
[FieldOffset(4)]
public short dmPaperLength;
[FieldOffset(6)]
public short dmPaperWidth;
[FieldOffset(8)]
public short dmScale;
[FieldOffset(10)]
public short dmCopies;
[FieldOffset(12)]
public short dmDefaultSource;
[FieldOffset(14)]
public short dmPrintQuality;
[FieldOffset(0)]
public int dmPosition_x;
[FieldOffset(4)]
public int dmPosition_y;
[FieldOffset(0)]
public int dmDisplayOrientation;
[FieldOffset(0)]
public int dmDisplayFixedOutput;
}
#endregion
#region OpenGLSetup
private bool SetupPixelFormat(ref uint hdc)
{
PIXELFORMATDESCRIPTOR pfd = new PIXELFORMATDESCRIPTOR();
ushort pfdSize = (ushort)Marshal.SizeOf(typeof(PIXELFORMATDESCRIPTOR)); // sizeof(PIXELFORMATDESCRIPTOR)
pfd.nSize = pfdSize; // size of pfd
pfd.nVersion = 1; // version number
pfd.dwFlags = (PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW | PFD_DOUBLEBUFFER); // flags
pfd.iPixelType = (byte)PFD_TYPE_RGBA; // RGBA type
pfd.cColorBits = (byte)GetDeviceCaps(hdc, BITSPIXEL); // color depth
pfd.cRedBits = 0; // color bits ignored
pfd.cRedShift = 0;
pfd.cGreenBits = 0;
pfd.cGreenShift = 0;
pfd.cBlueBits = 0;
pfd.cBlueShift = 0;
pfd.cAlphaBits = 0; // no alpha buffer
pfd.cAlphaShift = 0; // shift bit ignored
pfd.cAccumBits = 0; // no accumulation buffer
pfd.cAccumRedBits = 0; // accum bits ignored
pfd.cAccumGreenBits = 0;
pfd.cAccumBlueBits = 0;
pfd.cAccumAlphaBits = 0;
pfd.cDepthBits = 32; // 32-bit z-buffer
pfd.cStencilBits = 0; // no stencil buffer
pfd.cAuxBuffers = 0; // no auxiliary buffer
pfd.iLayerType = (byte)PFD_MAIN_PLANE; // main layer
pfd.bReserved = 0; // reserved
pfd.dwLayerMask = 0; // layer masks ignored
pfd.dwVisibleMask = 0;
pfd.dwDamageMask = 0;
int pixelformat = ChoosePixelFormat(hdc, ref pfd);
if (pixelformat == 0) // Did Windows Find A Matching Pixel Format?
{
MessageBox.Show("Can't Find A Suitable PixelFormat.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
return false;
}
if (SetPixelFormat(hdc, pixelformat, ref pfd) == 0) // Are We Able To Set The Pixel Format?
{
MessageBox.Show("Can't Set The PixelFormat.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
return false;
}
return true;
}
private bool InitGL() // All Setup For OpenGL Goes Here
{
glShadeModel(GL_SMOOTH); // Enable Smooth Shading
glClearColor(0.0f, 0.0f, 0.0f, 0.5f); // Black Background
glClearDepth(1.0f); // Depth Buffer Setup
// Add alpha blending support
glDepthFunc(GL_LEQUAL);
glDisable(GL_DEPTH_TEST);
glEnable(GL_TEXTURE_2D);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
return true; // Initialization Went OK
}
public bool SetupRenderingContext()
{
if (!CreateGLWindow())
{
return false; // initialization failed, quit
}
_hwnd = (uint)((this.Handle).ToInt32());
_hDC = GetDC(_hwnd);
if (_hDC == 0)
{
MessageBox.Show("Can't Create A GL Device Context", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
return false;
}
// not doing the following wglSwapBuffers() on the DC will result in a failure to subsequently create the RC
wglSwapBuffers(_hDC);
if (!SetupPixelFormat(ref _hDC))
{
return false;
}
// create the rendering context and make it current
_hRC = wglCreateContext(_hDC);
if (_hRC == 0) // Are We Able To Get A Rendering Context?
{
MessageBox.Show("Can't Create A GL Rendering Context.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
return false;
}
if (!wglMakeCurrent(_hDC, _hRC)) // Try To Activate The Rendering Context
{
MessageBox.Show("Can't Activate The GL Rendering Context.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
return false;
}
OpenGLForm_Resize(this, new EventArgs()); // Set up the perspective GL screen
return InitGL(); // Initialize Our Newly Created GL Window
}
#endregion
#region FormSetup
private bool CreateGLWindow()
{
Resize += new EventHandler(OpenGLForm_Resize);
TopMost = false;
WindowState = System.Windows.Forms.FormWindowState.Normal;
FormBorderStyle = System.Windows.Forms.FormBorderStyle.Sizable;
// The cursor is displayed only if the display count is greater than or equal to 0
do
{
}while (ShowCursor(true) < 0);
return true;
}
#endregion
#region Constructor/Destructor
public OpenGLForm()
{
//
// Required for Windows Form Designer support
//
InitializeComponent();
testBitmap = LoadBitmap(AbsolutePath + "bitmap.bmp");
testJpeg = LoadBitmap(AbsolutePath + "jpeg.jpg");
testPng = LoadBitmap(AbsolutePath + "png.png");
testGif = LoadBitmap(AbsolutePath + "gif.gif");
}
/// <summary>
/// Clean up any resources being used.
/// </summary>
protected override void Dispose(bool disposing)
{
if (disposing)
{
if (components != null)
{
components.Dispose();
}
if (_hRC != 0) // Do We Have A Rendering Context?
{
if (!wglMakeCurrent(0, 0)) // Are We Able To Release The DC And RC Contexts?
{
MessageBox.Show("Release Of DC And RC Failed.", "Shutdown Error", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
if (!wglDeleteContext(_hRC)) // Are We Able To Delete The RC?
{
MessageBox.Show("Release Rendering Context Failed.", "Shutdown Error", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
}
if (_hDC != 0 && ReleaseDC(_hwnd, _hDC) == 0) // Are We Able To Release The DC
{
MessageBox.Show("Release Device Context Failed.", "Shutdown Error", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
}
base.Dispose(disposing);
}
private Bitmap LoadBitmap(string filename)
{
Bitmap original = new Bitmap(filename);
Bitmap toReturn = new Bitmap(original.Width, original.Height, PixelFormat.Format24bppRgb);
using (Graphics gr = Graphics.FromImage(toReturn))
{
gr.DrawImage(original, new Rectangle(0, 0, toReturn.Width, toReturn.Height));
}
original.Dispose();
return toReturn;
}
#endregion
#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()
{
//
// OpenGLForm
//
this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
this.ClientSize = new System.Drawing.Size(632, 453);
this.KeyPreview = true;
this.Name = "OpenGLForm";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
this.Text = "\"NeHe\'s First Polygon Tutorial\"";
this.KeyUp += new System.Windows.Forms.KeyEventHandler(this.OpenGLForm_KeyUp);
}
#endregion
#region Events
protected override void WndProc(ref System.Windows.Forms.Message m)
{
// Listen for operating system messages.
switch (m.Msg)
{
// The WM_ACTIVATEAPP message occurs when the application
// becomes the active application or becomes inactive.
case WM_ACTIVATEAPP:
{
// The WParam value identifies what is occurring.
_appActive = ((int)m.WParam == WA_ACTIVE || (int)m.WParam == WA_CLICKACTIVE);
// Invalidate to get new scene painted.
Invalidate();
break;
}
default:
{
break;
}
}
base.WndProc(ref m);
}
/*!
This will stop the display from flickering on Paint event
*/
protected override void OnPaintBackground(PaintEventArgs e)
{
}
protected override void OnPaint(PaintEventArgs e)
{
// make sure the app is active
if (_appActive)
{
DrawGLScene();
wglSwapBuffers(_hDC);
Invalidate();
}
}
private void OpenGLForm_KeyUp(object sender, System.Windows.Forms.KeyEventArgs e)
{
switch (e.KeyCode)
{
case Keys.Escape:
{
Close();
break;
}
default:
{
break;
}
}
}
private void OpenGLForm_Resize(object sender, EventArgs e) // Resize And Initialize The GL Window
{
int width = ClientRectangle.Width;
int height = ClientRectangle.Height;
if (height == 0) // Prevent A Divide By Zero By
{
height = 1; // Making Height Equal One
}
// Switch to projection mode
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
// Change to ortho-graphic camera, with top left as (0, 0) coordinate,
// and bottom right as the control size
gluOrtho2D(0, ClientRectangle.Width, ClientRectangle.Height, 0);
glViewport(0, 0, ClientRectangle.Width, ClientRectangle.Height);
// Switch back to model view
glMatrixMode(GL_MODELVIEW);
glLoadIdentity(); // Reset The Modelview Matrix
}
#endregion
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
while (true)
{
OpenGLForm form = new OpenGLForm(); // create the form
if (!form.SetupRenderingContext()) // setup form and OpenGL
{
break; // initialization failed, quit
}
Application.Run(form);
if (form.Done) // Was There A Quit Received?
{
form.DisposeTexture();
break;
}
}
}
public bool DrawGLScene()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
// Test bitmap
Rectangle rect = new Rectangle();
rect.Size = testBitmap.Size;
RenderQuad(testBitmap, rect);
RenderLines(rect);
rect.X += rect.Width;
// Test JPEG
rect.Size = testJpeg.Size;
RenderQuad(testJpeg, rect);
RenderLines(rect);
rect.X += rect.Width;
// Test PNG
rect.Size = testPng.Size;
RenderQuad(testPng, rect);
RenderLines(rect);
rect.X += rect.Width;
// Test GIF
rect.Size = testGif.Size;
RenderQuad(testGif, rect);
RenderLines(rect);
return true;
}
private void RenderQuad(Bitmap image, Rectangle rect)
{
uint[] imageryTexture = GetTexture(image);
// Color all the vertices white with transparency
glEnable(GL_TEXTURE_2D);
glColor4ub(255, 255, 255, 255);
///////////////////////////////////////////////////
// Draw the image of the area.
///////////////////////////////////////////////////
// Fill the path with the background transparent color.
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
glBindTexture(GL_TEXTURE_2D, imageryTexture[0]);
// Building a quad of the image
glBegin(GL_QUADS);
// top left of texture
glTexCoord2f(0, 1);
glVertex2i(rect.Left, rect.Top);
// top right of texture
glTexCoord2f(1, 1);
glVertex2i(rect.Right, rect.Top);
// bottom right of texture
glTexCoord2f(1, 0);
glVertex2i(rect.Right, rect.Bottom);
// bottom left of texture
glTexCoord2f(0, 0);
glVertex2i(rect.Left, rect.Bottom);
glEnd();
}
private void RenderLines(Rectangle rect)
{
// Fill the path with the background transparent color.
// Color all the vertices transparent gray
Color LineColor = Color.Tomato;
glColor4ub(LineColor.R, LineColor.G, LineColor.B, 255);
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
// Building a quad of the image
glBegin(GL_QUADS);
// top left of texture
glVertex2i(rect.Left, rect.Top);
// top right of texture
glVertex2i(rect.Right, rect.Top);
// bottom right of texture
glVertex2i(rect.Right, rect.Bottom);
// bottom left of texture
glVertex2i(rect.Left, rect.Bottom);
glEnd();
}
// Creates a texture from a given bitmap.
private uint[] GetTexture(Bitmap convertToTexture)
{
// Setup return value
uint[] toReturn = null;
// Make sure the device and the bitmap exists
if (convertToTexture != null)
{
// Dispose the imagery first
DisposeTexture();
mImageryTexture = new uint[1];
// Setup the bitmap
Rectangle rect = new Rectangle(Point.Empty, convertToTexture.Size);
BitmapData bitmapdata = convertToTexture.LockBits(rect, ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
// Bind the texture
glGenTextures(1, mImageryTexture);
glBindTexture(GL_TEXTURE_2D, mImageryTexture[0]);
glTexImage2D(GL_TEXTURE_2D, 0, (int)GL_RGB8, convertToTexture.Width, convertToTexture.Height, 0, GL_BGR_EXT, GL_UNSIGNED_BYTE, bitmapdata.Scan0);
// Linear Filtering
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// Clamp
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
// Release the bitmap
convertToTexture.UnlockBits(bitmapdata);
// Set the return value to this imagery
toReturn = mImageryTexture;
}
return toReturn;
}
// Disposes the currently held texture.
public void DisposeTexture()
{
if (mImageryTexture != null)
{
glDeleteTextures(1, mImageryTexture);
mImageryTexture = null;
}
}
}
It turns out my code does work if the image dimensions are a power of two. It seems like I'll have to generate a larger image with the dimensions set to power of two, and change the UV values to remove the newly added dimensions.

How do I get the taskbar's position and size?

I want to know how to get the rectangle (bottom, top, left, and right) that the taskbar occupies. How do I go about doing this in C#?
private enum TaskBarLocation { TOP, BOTTOM, LEFT, RIGHT}
private TaskBarLocation GetTaskBarLocation()
{
TaskBarLocation taskBarLocation = TaskBarLocation.BOTTOM;
bool taskBarOnTopOrBottom = (Screen.PrimaryScreen.WorkingArea.Width == Screen.PrimaryScreen.Bounds.Width);
if (taskBarOnTopOrBottom)
{
if (Screen.PrimaryScreen.WorkingArea.Top > 0) taskBarLocation = TaskBarLocation.TOP;
}
else
{
if (Screen.PrimaryScreen.WorkingArea.Left > 0)
{
taskBarLocation = TaskBarLocation.LEFT;
}
else
{
taskBarLocation = TaskBarLocation.RIGHT;
}
}
return taskBarLocation;
}
It's actually way more complicated than is shown above. For one thing, the task bar doesn't have to be on the primary screen, it can be dragged to any screen. For another, in theory there could be something docked on each edge of each given screen. The code above incorrectly assumes that finding something docked to one edge excludes all other edges.
The only way the location of the task bar could be definitively derived from bounds vs workingarea, would be if only one edge out of all screens had something docked to it.
The following function returns an array of Rectangles, each representing a docked task bar, and writes the count to its byref parameter. If that count is 1, element 0 of the returned array is the Rectangle occupied by the task bar. If greater than 1, time to guess?
Public Function FindDockedTaskBars(ByRef DockedRectCounter As Integer) As Rectangle()
Dim TmpScrn As Screen = Nothing
Dim LeftDockedWidth As Integer = 0
Dim TopDockedHeight As Integer = 0
Dim RightDockedWidth As Integer = 0
Dim BottomDockedHeight As Integer = 0
Dim DockedRects(Screen.AllScreens.Count * 4) As Rectangle
DockedRectCounter = 0
For Each TmpScrn In Screen.AllScreens
If Not TmpScrn.Bounds.Equals(TmpScrn.WorkingArea) Then
LeftDockedWidth = Math.Abs(Math.Abs(TmpScrn.Bounds.Left) - Math.Abs(TmpScrn.WorkingArea.Left))
TopDockedHeight = Math.Abs(Math.Abs(TmpScrn.Bounds.Top) - Math.Abs(TmpScrn.WorkingArea.Top))
RightDockedWidth = (TmpScrn.Bounds.Width - LeftDockedWidth) - TmpScrn.WorkingArea.Width
BottomDockedHeight = (TmpScrn.Bounds.Height - TopDockedHeight) - TmpScrn.WorkingArea.Height
If LeftDockedWidth > 0 Then
DockedRects(DockedRectCounter).X = TmpScrn.Bounds.Left
DockedRects(DockedRectCounter).Y = TmpScrn.Bounds.Top
DockedRects(DockedRectCounter).Width = LeftDockedWidth
DockedRects(DockedRectCounter).Height = TmpScrn.Bounds.Height
DockedRectCounter += 1
End If
If RightDockedWidth > 0 Then
DockedRects(DockedRectCounter).X = TmpScrn.WorkingArea.Right
DockedRects(DockedRectCounter).Y = TmpScrn.Bounds.Top
DockedRects(DockedRectCounter).Width = RightDockedWidth
DockedRects(DockedRectCounter).Height = TmpScrn.Bounds.Height
DockedRectCounter += 1
End If
If TopDockedHeight > 0 Then
DockedRects(DockedRectCounter).X = TmpScrn.WorkingArea.Left
DockedRects(DockedRectCounter).Y = TmpScrn.Bounds.Top
DockedRects(DockedRectCounter).Width = TmpScrn.WorkingArea.Width
DockedRects(DockedRectCounter).Height = TopDockedHeight
DockedRectCounter += 1
End If
If BottomDockedHeight > 0 Then
DockedRects(DockedRectCounter).X = TmpScrn.WorkingArea.Left
DockedRects(DockedRectCounter).Y = TmpScrn.WorkingArea.Bottom
DockedRects(DockedRectCounter).Width = TmpScrn.WorkingArea.Width
DockedRects(DockedRectCounter).Height = BottomDockedHeight
DockedRectCounter += 1
End If
End If
Next
Return DockedRects
End Function
Or for those of you who prefer C#... (Note: this ported code is untested)
using System.Drawing;
using System.Windows.Forms;
public Rectangle[] FindDockedTaskBars(ref int DockedRectCounter)
{
int LeftDockedWidth = 0;
int TopDockedHeight = 0;
int RightDockedWidth = 0;
int BottomDockedHeight = 0;
Rectangle[] DockedRects = new Rectangle[Screen.AllScreens.Count() * 4];
DockedRectCounter = 0;
foreach (Screen TmpScrn in Screen.AllScreens)
{
if (!TmpScrn.Bounds.Equals(TmpScrn.WorkingArea))
{
LeftDockedWidth = Math.Abs(Math.Abs(TmpScrn.Bounds.Left) - Math.Abs(TmpScrn.WorkingArea.Left));
TopDockedHeight = Math.Abs(Math.Abs(TmpScrn.Bounds.Top) - Math.Abs(TmpScrn.WorkingArea.Top));
RightDockedWidth = (TmpScrn.Bounds.Width - LeftDockedWidth) - TmpScrn.WorkingArea.Width;
BottomDockedHeight = (TmpScrn.Bounds.Height - TopDockedHeight) - TmpScrn.WorkingArea.Height;
if (LeftDockedWidth > 0)
{
DockedRects[DockedRectCounter].X = TmpScrn.Bounds.Left;
DockedRects[DockedRectCounter].Y = TmpScrn.Bounds.Top;
DockedRects[DockedRectCounter].Width = LeftDockedWidth;
DockedRects[DockedRectCounter].Height = TmpScrn.Bounds.Height;
DockedRectCounter += 1;
}
if (RightDockedWidth > 0)
{
DockedRects[DockedRectCounter].X = TmpScrn.WorkingArea.Right;
DockedRects[DockedRectCounter].Y = TmpScrn.Bounds.Top;
DockedRects[DockedRectCounter].Width = RightDockedWidth;
DockedRects[DockedRectCounter].Height = TmpScrn.Bounds.Height;
DockedRectCounter += 1;
}
if (TopDockedHeight > 0)
{
DockedRects[DockedRectCounter].X = TmpScrn.WorkingArea.Left;
DockedRects[DockedRectCounter].Y = TmpScrn.Bounds.Top;
DockedRects[DockedRectCounter].Width = TmpScrn.WorkingArea.Width;
DockedRects[DockedRectCounter].Height = TopDockedHeight;
DockedRectCounter += 1;
}
if (BottomDockedHeight > 0)
{
DockedRects[DockedRectCounter].X = TmpScrn.WorkingArea.Left;
DockedRects[DockedRectCounter].Y = TmpScrn.WorkingArea.Bottom;
DockedRects[DockedRectCounter].Width = TmpScrn.WorkingArea.Width;
DockedRects[DockedRectCounter].Height = BottomDockedHeight;
DockedRectCounter += 1;
}
}
}
return DockedRects;
}
Based on David's answer, here is a better implementation that uses P/Invoke to correctly determine the placement and size of the taskbar. The only limitation I know of so far is that it does not return the correct bounds when multiple monitors are set to display in extended mode.
The code with all subsequent updates is available as a gist at https://git.io/v9bCx.
/******************************************************************************
* Name: Taskbar.cs
* Description: Class to get the taskbar's position, size and other properties.
* Author: Franz Alex Gaisie-Essilfie
* based on code from https://winsharp93.wordpress.com/2009/06/29/find-out-size-and-position-of-the-taskbar/
*
* Change Log:
* Date | Description
* -------------|--------------------------------------------------------------
* 2017-05-16 | Initial design
*/
using System;
using System.Drawing;
using System.Runtime.InteropServices;
namespace System.Windows.Forms
{
public enum TaskbarPosition
{
Unknown = -1,
Left,
Top,
Right,
Bottom,
}
public static class Taskbar
{
private enum ABS
{
AutoHide = 0x01,
AlwaysOnTop = 0x02,
}
////private enum ABE : uint
private enum AppBarEdge : uint
{
Left = 0,
Top = 1,
Right = 2,
Bottom = 3
}
////private enum ABM : uint
private enum AppBarMessage : uint
{
New = 0x00000000,
Remove = 0x00000001,
QueryPos = 0x00000002,
SetPos = 0x00000003,
GetState = 0x00000004,
GetTaskbarPos = 0x00000005,
Activate = 0x00000006,
GetAutoHideBar = 0x00000007,
SetAutoHideBar = 0x00000008,
WindowPosChanged = 0x00000009,
SetState = 0x0000000A,
}
private const string ClassName = "Shell_TrayWnd";
private static APPBARDATA _appBarData;
/// <summary>Static initializer of the <see cref="Taskbar" /> class.</summary>
static Taskbar()
{
_appBarData = new APPBARDATA
{
cbSize = (uint)Marshal.SizeOf(typeof(APPBARDATA)),
hWnd = FindWindow(Taskbar.ClassName, null)
};
}
/// <summary>
/// Gets a value indicating whether the taskbar is always on top of other windows.
/// </summary>
/// <value><c>true</c> if the taskbar is always on top of other windows; otherwise, <c>false</c>.</value>
/// <remarks>This property always returns <c>false</c> on Windows 7 and newer.</remarks>
public static bool AlwaysOnTop
{
get
{
int state = SHAppBarMessage(AppBarMessage.GetState, ref _appBarData).ToInt32();
return ((ABS)state).HasFlag(ABS.AlwaysOnTop);
}
}
/// <summary>
/// Gets a value indicating whether the taskbar is automatically hidden when inactive.
/// </summary>
/// <value><c>true</c> if the taskbar is set to auto-hide is enabled; otherwise, <c>false</c>.</value>
public static bool AutoHide
{
get
{
int state = SHAppBarMessage(AppBarMessage.GetState, ref _appBarData).ToInt32();
return ((ABS)state).HasFlag(ABS.AutoHide);
}
}
/// <summary>Gets the current display bounds of the taskbar.</summary>
public static Rectangle CurrentBounds
{
get
{
var rect = new RECT();
if (GetWindowRect(Handle, ref rect))
return Rectangle.FromLTRB(rect.Left, rect.Top, rect.Right, rect.Bottom);
return Rectangle.Empty;
}
}
/// <summary>Gets the display bounds when the taskbar is fully visible.</summary>
public static Rectangle DisplayBounds
{
get
{
if (RefreshBoundsAndPosition())
return Rectangle.FromLTRB(_appBarData.rect.Left,
_appBarData.rect.Top,
_appBarData.rect.Right,
_appBarData.rect.Bottom);
return CurrentBounds;
}
}
/// <summary>Gets the taskbar's window handle.</summary>
public static IntPtr Handle
{
get { return _appBarData.hWnd; }
}
/// <summary>Gets the taskbar's position on the screen.</summary>
public static TaskbarPosition Position
{
get
{
if (RefreshBoundsAndPosition())
return (TaskbarPosition)_appBarData.uEdge;
return TaskbarPosition.Unknown;
}
}
/// <summary>Hides the taskbar.</summary>
public static void Hide()
{
const int SW_HIDE = 0;
ShowWindow(Handle, SW_HIDE);
}
/// <summary>Shows the taskbar.</summary>
public static void Show()
{
const int SW_SHOW = 1;
ShowWindow(Handle, SW_SHOW);
}
private static bool RefreshBoundsAndPosition()
{
//! SHAppBarMessage returns IntPtr.Zero **if it fails**
return SHAppBarMessage(AppBarMessage.GetTaskbarPos, ref _appBarData) != IntPtr.Zero;
}
#region DllImports
[DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool GetWindowRect(IntPtr hWnd, ref RECT lpRect);
[DllImport("shell32.dll", SetLastError = true)]
private static extern IntPtr SHAppBarMessage(AppBarMessage dwMessage, [In] ref APPBARDATA pData);
[DllImport("user32.dll")]
private static extern int ShowWindow(IntPtr hwnd, int command);
#endregion DllImports
[StructLayout(LayoutKind.Sequential)]
private struct APPBARDATA
{
public uint cbSize;
public IntPtr hWnd;
public uint uCallbackMessage;
public AppBarEdge uEdge;
public RECT rect;
public int lParam;
}
[StructLayout(LayoutKind.Sequential)]
private struct RECT
{
public int Left;
public int Top;
public int Right;
public int Bottom;
}
}
}
private enum TaskBarLocation { TOP, BOTTOM, LEFT, RIGHT }
private TaskBarLocation GetTaskBarLocation()
{
//System.Windows.SystemParameters....
if (SystemParameters.WorkArea.Left > 0)
return TaskBarLocation.LEFT;
if (SystemParameters.WorkArea.Top > 0)
return TaskBarLocation.TOP;
if (SystemParameters.WorkArea.Left == 0
&& SystemParameters.WorkArea.Width < SystemParameters.PrimaryScreenWidth)
return TaskBarLocation.RIGHT;
return TaskBarLocation.BOTTOM;
}
This is the answer from Mark McGinty in C#.
This code brings back all of the task bars as a list of rectanges:
0 rectangles means the taskbar is hidden;
1 rectangle is the position of the taskbar;
2+ is very rare, it means that we have multiple monitors, and we are not using Extend these displays to create a single virtual desktop.
Works in every situation
It works well with:
Windows 7 (will almost certainly work on Windows 8.1 and Windows 10).
All combinations of settings.
C# Code
public static List<Rectangle> FindDockedTaskBars()
{
List<Rectangle> dockedRects = new List<Rectangle>();
foreach (var tmpScrn in Screen.AllScreens)
{
if (!tmpScrn.Bounds.Equals(tmpScrn.WorkingArea))
{
Rectangle rect = new Rectangle();
var leftDockedWidth = Math.Abs((Math.Abs(tmpScrn.Bounds.Left) - Math.Abs(tmpScrn.WorkingArea.Left)));
var topDockedHeight = Math.Abs((Math.Abs(tmpScrn.Bounds.Top) - Math.Abs(tmpScrn.WorkingArea.Top)));
var rightDockedWidth = ((tmpScrn.Bounds.Width - leftDockedWidth) - tmpScrn.WorkingArea.Width);
var bottomDockedHeight = ((tmpScrn.Bounds.Height - topDockedHeight) - tmpScrn.WorkingArea.Height);
if ((leftDockedWidth > 0))
{
rect.X = tmpScrn.Bounds.Left;
rect.Y = tmpScrn.Bounds.Top;
rect.Width = leftDockedWidth;
rect.Height = tmpScrn.Bounds.Height;
}
else if ((rightDockedWidth > 0))
{
rect.X = tmpScrn.WorkingArea.Right;
rect.Y = tmpScrn.Bounds.Top;
rect.Width = rightDockedWidth;
rect.Height = tmpScrn.Bounds.Height;
}
else if ((topDockedHeight > 0))
{
rect.X = tmpScrn.WorkingArea.Left;
rect.Y = tmpScrn.Bounds.Top;
rect.Width = tmpScrn.WorkingArea.Width;
rect.Height = topDockedHeight;
}
else if ((bottomDockedHeight > 0))
{
rect.X = tmpScrn.WorkingArea.Left;
rect.Y = tmpScrn.WorkingArea.Bottom;
rect.Width = tmpScrn.WorkingArea.Width;
rect.Height = bottomDockedHeight;
}
else
{
// Nothing found!
}
dockedRects.Add(rect);
}
}
if (dockedRects.Count == 0)
{
// Taskbar is set to "Auto-Hide".
}
return dockedRects;
}
This is a simple example using winforms with wpf and multi screen support:
Screen sc = Screen.FromHandle(new WindowInteropHelper(this).Handle);
if (sc.WorkingArea.Top > 0)
{
// TASKBAR TOP
}
else if (sc.WorkingArea.Left != sc.Bounds.X)
{
// TASKBAR LEFT
}
else if ((sc.Bounds.Height - sc.WorkingArea.Height) > 0)
{
// TASKBAR BOTTOM
}
else if (sc.WorkingArea.Right != 0)
{
// TASKBAR RIGHT
}
else
{
// TASKBAR NOT FOUND
}
[StructLayout(LayoutKind.Sequential)]
public struct RECT { public Int32 left; public Int32 top; public Int32 right; public Int32 bottom; }
[StructLayout(LayoutKind.Sequential)]
public struct APPBARDATA { public UInt32 cbSize; public IntPtr hWnd; public UInt32 uCallbackMessage; public UInt32 uEdge; public RECT rc; public IntPtr lParam; }
[DllImport("shell32.dll")]
public static extern IntPtr SHAppBarMessage(UInt32 dwMessage, ref APPBARDATA pData);
private void Form1_Load(object sender, EventArgs e)
{
APPBARDATA msgData = new APPBARDATA();
msgData.cbSize = (UInt32)Marshal.SizeOf(msgData);
// get taskbar position
SHAppBarMessage((UInt32)0x00000005, ref msgData);
RECT taskRect = msgData.rc;
Console.WriteLine("top:" + taskRect.top + "; left:" + taskRect.left + "; bottom:" + taskRect.bottom + "; right:" + taskRect.right);
Console.WriteLine("width:" + (taskRect.right - taskRect.left) + "; height:" + (taskRect.bottom - taskRect.top));
}
Output: top:1040; left:0; bottom:1080; right:1920 width:1920;
height:40
This is how to get the Taskbar's Height (using WPF)
int PSBH = System.Windows.Forms.Screen.PrimaryScreen.Bounds.Height
int TaskBarHeight = PSBH - System.Windows.Forms.Screen.PrimaryScreen.WorkingArea.Height;
If you want to account for dpi
int PSH = SystemParameters.PrimaryScreenHeight;
int PSBH = System.Windows.Forms.Screen.PrimaryScreen.Bounds.Height;
double ratio = PSH / PSBH;
int TaskBarHeight = PSBH - System.Windows.Forms.Screen.PrimaryScreen.WorkingArea.Height;
TaskBarHeight *= ratio;
I hate to say this, but one of the best and most consistent methods for setting your window size when using a WindowStyle of none is to create a temporary window that is created, maximized, Width and Height recorded, and then destroyed.
private (double height, double width) GetVirtualWindowSize()
{
Window virtualWindow = new Window();
virtualWindow.Show();
virtualWindow.WindowState = WindowState.Maximized;
double returnHeight = virtualWindow.Height;
double returnWidth = virtualWindow.Width;
virtualWindow.Close();
return (returnHeight, returnWidth);
}
You can then set the MaxHeight and MaxWidth properties of your window using the values returned within the tuple. This "sacrificial" window will use the standard window frame, and so Windows knows how to properly maximize it, thus returning accurate values every time.
You can do this at startup so that you only have to deal with the window creation once, or you can recheck the dimensions on every maximize.
I know it isn't pretty, and there is a brief flash. But it always works for me, no matter where the taskbar is, no matter the scaling, and no matter the changes that have taken place since the application started.
This will work on Windows and macOS. It will also work with multiple monitors. As is, it is for electron applications, but you can easily understand what's going on.
type TaskBarPos int
const (
UNKNOWN TaskBarPos = -1
LEFT TaskBarPos = 0
RIGHT TaskBarPos = 1
TOP TaskBarPos = 2
BOTTOM TaskBarPos = 3
)
type Rect struct {
top int
bottom int
width int
height int
left int
right int
}
func (r Rect) centerX() int {
return r.left + ((r.right - r.left) / 2)
}
func (r Rect) centerY() int {
return r.top + ((r.bottom - r.top) / 2)
}
func taskbar(tray *js.Object) TaskBarPos {
// Step 1 - Get relevant display
display := screen.Call("getDisplayNearestPoint", screen.Call("getCursorScreenPoint")) // Replace with primary monitor or a secondary monitor. This line as is grabs the monitor that the mouse cursor is on.
// Step 2 - Determine taskbar bounds relative to the display
bounds := display.Get("bounds")
workArea := display.Get("workArea")
var tb *Rect
d := Rect{
top: bounds.Get("y").Int(),
bottom: bounds.Get("y").Int() + bounds.Get("height").Int(),
width: bounds.Get("width").Int(),
height: bounds.Get("height").Int(),
left: bounds.Get("x").Int(),
right: bounds.Get("x").Int() + bounds.Get("width").Int(),
}
wa := Rect{
top: workArea.Get("y").Int(),
bottom: workArea.Get("y").Int() + workArea.Get("height").Int(),
width: workArea.Get("width").Int(),
height: workArea.Get("height").Int(),
left: workArea.Get("x").Int(),
right: workArea.Get("x").Int() + workArea.Get("width").Int(),
}
if tray != nil {
tBounds := tray.Call("getBounds")
tb = &Rect{
top: tBounds.Get("y").Int(),
bottom: tBounds.Get("y").Int() + tBounds.Get("height").Int(),
width: tBounds.Get("width").Int(),
height: tBounds.Get("height").Int(),
left: tBounds.Get("x").Int(),
right: tBounds.Get("x").Int() + tBounds.Get("width").Int(),
}
}
// Step 3 - Determine Position of Taskbar
if wa.top > d.top {
return TOP
} else if wa.bottom < d.bottom {
return BOTTOM
} else if wa.left > d.left {
return LEFT
} else if wa.right < d.right {
return RIGHT
}
if tb == nil {
return UNKNOWN
}
// Check which corner tray is closest to
if ((*tb).top - d.top) < (d.bottom - (*tb).bottom) {
return TOP
}
if ((*tb).left - d.left) < (d.right - (*tb).right) {
return LEFT
}
if d.bottom-(*tb).centerY() < d.right-(*tb).centerX() {
return BOTTOM
}
return RIGHT
}

How to make my Windows Form app snap to screen edges?

Anyone out there know how to make your .net windows form app sticky/snappy like Winamp so it snaps to the edges of the screen?
The target framework would be .NET 2.0 Windows Form written in C#, using VS08. I am looking to add this functionality to a custom user control, but I figured more people would benefit from having it described for the application and its main form.
Thank you.
This worked pretty well, works on multiple monitors, observes the taskbar:
public partial class Form1 : Form {
public Form1() {
InitializeComponent();
}
private const int SnapDist = 100;
private bool DoSnap(int pos, int edge) {
int delta = pos - edge;
return delta > 0 && delta <= SnapDist;
}
protected override void OnResizeEnd(EventArgs e) {
base.OnResizeEnd(e);
Screen scn = Screen.FromPoint(this.Location);
if (DoSnap(this.Left, scn.WorkingArea.Left)) this.Left= scn.WorkingArea.Left;
if (DoSnap(this.Top, scn.WorkingArea.Top)) this.Top = scn.WorkingArea.Top;
if (DoSnap(scn.WorkingArea.Right, this.Right)) this.Left = scn.WorkingArea.Right - this.Width;
if (DoSnap(scn.WorkingArea.Bottom, this.Bottom)) this.Top = scn.WorkingArea.Bottom - this.Height;
}
}
The accepted answer only snaps the window after finishing the drag, whereas I wanted the form to continuously snap to the screen edges while dragging. Here's my solution, loosely based off the Paint.NET source code:
using System;
using System.ComponentModel;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace Whatever
{
/// <summary>
/// Managed equivalent of the Win32 <code>RECT</code> structure.
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public struct LtrbRectangle
{
public int Left;
public int Top;
public int Right;
public int Bottom;
public LtrbRectangle(int left, int top, int right, int bottom)
{
Left = left;
Top = top;
Right = right;
Bottom = bottom;
}
public Rectangle ToRectangle()
{
return Rectangle.FromLTRB(Left, Top, Right, Bottom);
}
public static LtrbRectangle FromRectangle(Rectangle rect)
{
return new LtrbRectangle(rect.X, rect.Y, rect.X + rect.Width, rect.Y + rect.Height);
}
public override string ToString()
{
return "{Left=" + Left + ",Top=" + Top + ",Right=" + Right + ",Bottom=" + Bottom + "}";
}
}
/// <summary>
/// A form that "snaps" to screen edges when moving.
/// </summary>
public class AnchoredForm : Form
{
private const int WmEnterSizeMove = 0x0231;
private const int WmMoving = 0x0216;
private const int WmSize = 0x0005;
private SnapLocation _snapAnchor;
private int _dragOffsetX;
private int _dragOffsetY;
/// <summary>
/// Flags specifying which edges to anchor the form at.
/// </summary>
[Flags]
public enum SnapLocation
{
None = 0,
Left = 1 << 0,
Top = 1 << 1,
Right = 1 << 2,
Bottom = 1 << 3,
All = Left | Top | Right | Bottom
}
/// <summary>
/// How far from the screen edge to anchor the form.
/// </summary>
[Browsable(true)]
[DefaultValue(10)]
[Description("The distance from the screen edge to anchor the form.")]
public virtual int AnchorDistance { get; set; } = 10;
/// <summary>
/// Gets or sets how close the form must be to the
/// anchor point to snap to it. A higher value gives
/// a more noticable "snap" effect.
/// </summary>
[Browsable(true)]
[DefaultValue(20)]
[Description("The maximum form snapping distance.")]
public virtual int SnapDistance { get; set; } = 20;
/// <summary>
/// Re-snaps the control to its current anchor points.
/// This can be useful for re-positioning the form after
/// the screen resolution changes.
/// </summary>
public void ReSnap()
{
SnapTo(_snapAnchor);
}
/// <summary>
/// Forces the control to snap to the specified edges.
/// </summary>
/// <param name="anchor">The screen edges to snap to.</param>
public void SnapTo(SnapLocation anchor)
{
Screen currentScreen = Screen.FromPoint(Location);
Rectangle workingArea = currentScreen.WorkingArea;
if ((anchor & SnapLocation.Left) != 0)
{
Left = workingArea.Left + AnchorDistance;
}
else if ((anchor & SnapLocation.Right) != 0)
{
Left = workingArea.Right - AnchorDistance - Width;
}
if ((anchor & SnapLocation.Top) != 0)
{
Top = workingArea.Top + AnchorDistance;
}
else if ((anchor & SnapLocation.Bottom) != 0)
{
Top = workingArea.Bottom - AnchorDistance - Height;
}
_snapAnchor = anchor;
}
private bool InSnapRange(int a, int b)
{
return Math.Abs(a - b) < SnapDistance;
}
private SnapLocation FindSnap(ref Rectangle effectiveBounds)
{
Screen currentScreen = Screen.FromPoint(effectiveBounds.Location);
Rectangle workingArea = currentScreen.WorkingArea;
SnapLocation anchor = SnapLocation.None;
if (InSnapRange(effectiveBounds.Left, workingArea.Left + AnchorDistance))
{
effectiveBounds.X = workingArea.Left + AnchorDistance;
anchor |= SnapLocation.Left;
}
else if (InSnapRange(effectiveBounds.Right, workingArea.Right - AnchorDistance))
{
effectiveBounds.X = workingArea.Right - AnchorDistance - effectiveBounds.Width;
anchor |= SnapLocation.Right;
}
if (InSnapRange(effectiveBounds.Top, workingArea.Top + AnchorDistance))
{
effectiveBounds.Y = workingArea.Top + AnchorDistance;
anchor |= SnapLocation.Top;
}
else if (InSnapRange(effectiveBounds.Bottom, workingArea.Bottom - AnchorDistance))
{
effectiveBounds.Y = workingArea.Bottom - AnchorDistance - effectiveBounds.Height;
anchor |= SnapLocation.Bottom;
}
return anchor;
}
protected override void WndProc(ref Message m)
{
switch (m.Msg)
{
case WmEnterSizeMove:
case WmSize:
// Need to handle window size changed as well when
// un-maximizing the form by dragging the title bar.
_dragOffsetX = Cursor.Position.X - Left;
_dragOffsetY = Cursor.Position.Y - Top;
break;
case WmMoving:
LtrbRectangle boundsLtrb = Marshal.PtrToStructure<LtrbRectangle>(m.LParam);
Rectangle bounds = boundsLtrb.ToRectangle();
// This is where the window _would_ be located if snapping
// had not occurred. This prevents the cursor from sliding
// off the title bar if the snap distance is too large.
Rectangle effectiveBounds = new Rectangle(
Cursor.Position.X - _dragOffsetX,
Cursor.Position.Y - _dragOffsetY,
bounds.Width,
bounds.Height);
_snapAnchor = FindSnap(ref effectiveBounds);
LtrbRectangle newLtrb = LtrbRectangle.FromRectangle(effectiveBounds);
Marshal.StructureToPtr(newLtrb, m.LParam, false);
m.Result = new IntPtr(1);
break;
}
base.WndProc(ref m);
}
}
}
And here's what it looks like:
Just retrieve the current pixel height/width of the monitor you're on...
How to determine active monitor of the current cursor location
... and process the location changed/moved events for the form. When you get within, say 25 pixels or so of an edge (your main form's Location.Left + form width) or height (your main form's Location.Top + form height), then go ahead and set the .Left and .Top properties so that your application "docks" in the corners.
Edit: One other note - when you actually do the "snapping" you may also want to move the cursor position the relative distance to make it stay on the same point on the window bar. Otherwise your form may become a giant ping pong ball between the cursor position and your "snappy" functionality as the MouseMove and form location changed events fight against each other.
I don't know if you found your solution, but I created a small component for just that: http://www.formsnapper.net - it snaps accross the process boundaries!
https://github.com/stax76/staxrip
Protected Overrides Sub WndProc(ByRef m As Message)
Snap(m)
MyBase.WndProc(m)
End Sub
Private IsResizing As Boolean
Sub Snap(ByRef m As Message)
Select Case m.Msg
Case &H214 'WM_SIZING
IsResizing = True
Case &H232 'WM_EXITSIZEMOVE
IsResizing = False
Case &H46 'WM_WINDOWPOSCHANGING
If Not IsResizing Then Snap(m.LParam)
End Select
End Sub
Sub Snap(handle As IntPtr)
Dim workingArea = Screen.FromControl(Me).WorkingArea
Dim newPos = DirectCast(Marshal.PtrToStructure(handle, GetType(WindowPos)), WindowPos)
Dim snapMargin = Control.DefaultFont.Height
Dim border As Integer
If OSVersion.Current >= OSVersion.Windows8 Then border = (Width - ClientSize.Width) \ 2 - 1
If newPos.Y <> 0 Then
If Math.Abs(newPos.Y - workingArea.Y) < snapMargin AndAlso Top > newPos.Y Then
newPos.Y = workingArea.Y
ElseIf Math.Abs(newPos.Y + Height - (workingArea.Bottom + border)) < snapMargin AndAlso Top < newPos.Y Then
newPos.Y = (workingArea.Bottom + border) - Height
End If
End If
If newPos.X <> 0 Then
If Math.Abs(newPos.X - (workingArea.X - border)) < snapMargin AndAlso Left > newPos.X Then
newPos.X = workingArea.X - border
ElseIf Math.Abs(newPos.X + Width - (workingArea.Right + border)) < snapMargin AndAlso Left < newPos.X Then
newPos.X = (workingArea.Right + border) - Width
End If
End If
Marshal.StructureToPtr(newPos, handle, True)
End Sub

Categories

Resources