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;
}
}
}
Shell_SecondaryTrayWnd
window class that is used for a second Taskbar. Potentially some other name for a 3rd. Both seem to have a MSTaskListWClass
child window, so I am going to proceed in this direction. If I can get all handles of MSTaskListWClass
and somehow check which monitor they are on, and if it match the monitor I am using, it might be a viable solution. EDIT: MonitorFromWindow
might be the answer. I'm already using this from my main window to determine which monitor dimensions to use. –
Lobbyism DisplayBounds
. –
Popelka 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
}
© 2022 - 2024 — McMap. All rights reserved.
Screen.PrimaryScreen.Bounds
andScreen.PrimaryScreen.WorkingArea
and subtract them. You can useAllScreens[n]
instead ofPrimaryScreen
for multi-monitor systems. – MellicentScreen.PrimaryScreen.Bounds.Right - Screen.PrimaryScreen.WorkingArea.Right
for a vertical taskbar on the right side of my screen. – Chancellor