Why does GetWindowRect include the title bar in my WPF window? - c#

I'm trying to get caret position using GetWindowRect() (and GetGUIThreadInfo()):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Runtime.InteropServices;
namespace WpfApplication1
{
public class CaretInfo
{
public double Width { get; private set; }
public double Height { get; private set; }
public double Left { get; private set; }
public double Top { get; private set; }
public CaretInfo(double width, double height, double left, double top)
{
Width = width;
Height = height;
Left = left;
Top = top;
}
}
public class CaretHelper
{
public static CaretInfo GetCaretPosition()
{
// Get GUI info containing caret poisition
var guiInfo = new GUITHREADINFO();
guiInfo.cbSize = (uint)Marshal.SizeOf(guiInfo);
GetGUIThreadInfo(0, out guiInfo);
// Get width/height
double width = guiInfo.rcCaret.right - guiInfo.rcCaret.left;
double height = guiInfo.rcCaret.bottom - guiInfo.rcCaret.top;
// Get left/top relative to screen
RECT rect;
GetWindowRect(guiInfo.hwndFocus, out rect);
double left = guiInfo.rcCaret.left + rect.left + 2;
double top = guiInfo.rcCaret.top + rect.top + 2;
int capacity = GetWindowTextLength(guiInfo.hwndFocus) * 2;
StringBuilder stringBuilder = new StringBuilder(capacity);
GetWindowText(guiInfo.hwndFocus, stringBuilder, stringBuilder.Capacity);
Console.WriteLine("Window: " + stringBuilder);
Console.WriteLine("Caret: " + guiInfo.rcCaret.left + ", " + guiInfo.rcCaret.top);
Console.WriteLine("Rect : " + rect.left + ", " + rect.top);
return new CaretInfo(width, height, left, top);
}
[DllImport("user32.dll", EntryPoint = "GetGUIThreadInfo")]
public static extern bool GetGUIThreadInfo(uint tId, out GUITHREADINFO threadInfo);
[DllImport("user32.dll")]
public static extern bool ClientToScreen(IntPtr hWnd, out Point position);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GetWindowRect(IntPtr handle, out RECT lpRect);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool GetClientRect(IntPtr hWnd, ref Rect rect);
[StructLayout(LayoutKind.Sequential)]
public struct GUITHREADINFO
{
public uint cbSize;
public uint flags;
public IntPtr hwndActive;
public IntPtr hwndFocus;
public IntPtr hwndCapture;
public IntPtr hwndMenuOwner;
public IntPtr hwndMoveSize;
public IntPtr hwndCaret;
public RECT rcCaret;
};
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
public int left;
public int top;
public int right;
public int bottom;
}
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern int GetWindowTextLe
For Notepad and almost anywhere else the coordinates are correctly fetched:
In my WPF (and any other WPF) window, however, GetWindowRect() decides to include the title bar and offsetting the caret top position by the height of the title bar:
Any idea why?
I tried using DwmGetWindowAttribute() as well, but it gets the same coordinates for the WPF window as GetWindowRect().
Edit:
With the answer from Brian Reichle I've been able to determine a way to get the coordinate of the client area:
[DllImport("user32.dll")]
public static extern bool ClientToScreen(IntPtr hWnd, ref System.Drawing.Point lpPoint);
System.Drawing.Point point = new System.Drawing.Point(0, 0);
ClientToScreen(guiInfo.hwndFocus, ref point)
0,0 is the top left coordinate of the client area of the window specified by guiInfo.hwndFocus and it's always 0,0 because it's relative to the window's client area. ClientToScreen() converts the coordinates to be relative to screen instead (has to be System.Drawing.Point, System.Windows.Point won't work).

The title bar is included because its part of the window, if you don't want the non-client area then you need to request the client area rect (GetClientRect).
The confusion from notepad is probably because you are using the window handle of the text box rather than of the window itself. Remember, WPF uses a single handle for the overall window while win32 will often (but not always) use a separate handle for each control within the window.
In a comment you mentioned that GetClientRect 'returned' 0,0, did you check if it returned true (success) or false (failure)? If it returned false, did you check the result of GetLastError()?

Related

GetClientRect is not giving the correct Rectangle

I am trying to create an overlay form that overlays the contents of an external window (excluding the borders etc). I believe that GetClientRect is the correct winapi for that purpose however it does not seem to be working.
I created an example where I load up a form as a black box and display it over an open Notepad.
using System.Runtime.InteropServices;
using System.Diagnostics;
namespace WinFormsApp2
{
public partial class Form1 : Form
{
private Process? targetProc = null;
public struct RECT
{
public int Left;
public int Top;
public int Right;
public int Bottom;
}
[DllImport("user32.dll", SetLastError = true)]
public static extern bool GetClientRect(IntPtr hwnd, out RECT lpRect);
[DllImport("user32.dll")]
public static extern bool ClientToScreen(IntPtr hWnd, ref Point lpPoint);
public static Rectangle GetWindowRectangle(IntPtr hWnd)
{
Point point = new Point();
GetClientRect(hWnd, out RECT rect);
ClientToScreen(hWnd, ref point);
return new Rectangle(point.X, point.Y, rect.Right, rect.Bottom);
}
private IntPtr notepadhWnd;
public Form1()
{
InitializeComponent();
this.FormBorderStyle = FormBorderStyle.None;
this.WindowState = FormWindowState.Normal;
this.BackColor = Color.Black;
StartPosition = FormStartPosition.Manual;
targetProc = Process.GetProcessesByName("notepad").FirstOrDefault(p => p != null);
try
{
if (targetProc != null)
{
notepadhWnd = targetProc.MainWindowHandle;
if (notepadhWnd != IntPtr.Zero)
{
Rectangle rect = GetWindowRectangle(notepadhWnd);
Location = new Point(rect.Left, rect.Top);
Size = new Size(rect.Width, rect.Height);
}
}
}
catch (Exception ex)
{
// Log and message or
throw;
}
}
}
}
The output of this is:
I expected the output to be:
From all my searches I believe that the GetClientRect API should only return the client area but not sure what I am missing.
Grab coffee, this will be a long answer.
First, take a look at the image of notepad below. From the wording of your question it sounds like you are expecting GetClientRect to return the area marked in red. GetClientRect returns the dimensions of the window handle you provide but without the borders or scroll bars. In other words, it will give you the interior area of the green rectangle.
There is no single "client window" - in the screenshot below the menu bar is a child window. So is the status bar at the bottom of the screen. So is the editor space which seems to be what you are interested in.
So, how do you get the hWnd of the editor space? I'm not aware of any answer to that question that doesn't rely on the dark magic. It is all fraught with peril... do you iterate through all the child windows and pick the biggest? Pick a point in the dead center of the window and use API calls to find the hWnd at that location? No matter what you do, it will not be an exact science. For purposes of this question, though, I'll show one approach.
Every window has a Class name associated with it. In the image below I'm using a tool called Spy++ to reveal information about the various windows. As you can see, the window that makes up the editor space has a class name of "RichEditD2DPT"
I want to stress again how brittle this solution is. You could have more than one child window of the given class. The application developer could change to a different control with a different class name without warning. Nevertheless, below is code to enumerate through the child windows until the desired class is found. After that, I'm simply passing that hWnd into the code you already had and it seems to work.
public partial class Form1 : Form
{
private readonly StringBuilder _buffer = new StringBuilder(1024);
private IntPtr _notepadMainhWnd;
private IntPtr _notepadEditorhWnd;
public struct RECT
{
public int Left;
public int Top;
public int Right;
public int Bottom;
}
[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GetClientRect(IntPtr hwnd, out RECT lpRect);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool ClientToScreen(IntPtr hWnd, ref Point lpPoint);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool EnumChildWindows(IntPtr hwnd, WindowEnumProc func, IntPtr lParam);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
private static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);
private delegate bool WindowEnumProc(IntPtr hwnd, IntPtr lparam);
public static Rectangle GetWindowRectangle(IntPtr hWnd)
{
Point point = new Point();
GetClientRect(hWnd, out RECT rect);
bool ret = ClientToScreen(hWnd, ref point);
Debug.WriteLine($"{point.X},{point.Y}");
return new Rectangle(point.X, point.Y, rect.Right - rect.Left, rect.Bottom - rect.Top);
}
public Form1()
{
InitializeComponent();
this.FormBorderStyle = FormBorderStyle.None;
this.WindowState = FormWindowState.Normal;
this.BackColor = Color.Black;
StartPosition = FormStartPosition.Manual;
var targetProcess = Process.GetProcessesByName("notepad").FirstOrDefault(p => p != null);
if (targetProcess != null)
{
// Get the main application window
_notepadMainhWnd = targetProcess.MainWindowHandle;
if (_notepadMainhWnd != IntPtr.Zero)
{
// Looks for child windows having the class name "RichEditD2DPT"
EnumChildWindows(_notepadMainhWnd, callback, IntPtr.Zero);
if (_notepadEditorhWnd != IntPtr.Zero)
{
Rectangle rect = GetWindowRectangle(_notepadEditorhWnd);
Location = new Point(rect.Left, rect.Top);
Size = new Size(rect.Width, rect.Height);
}
}
}
}
private bool callback(IntPtr hWnd, IntPtr lParam)
{
if (GetClassName(hWnd, _buffer, _buffer.Capacity) != 0)
{
if (string.CompareOrdinal(_buffer.ToString(), "RichEditD2DPT") == 0)
{
_notepadEditorhWnd = hWnd;
return false;
}
}
return true;
}
}

WPF Window startup location for Per-Monitor-DPI

Struggling to get a WPF Window showing up on secondary screen with mixed DPI monitors. Reproducible in .NET Framework 4.8 as well as .NET Standard 2.0
Setup:
Primary monitor : 4K, 250%
Secondary monitor: 1080p, 100%
Step 1:
add a Manifest for PerMonitorV2
<?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings>
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2, PerMonitor</dpiAwareness>
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware>
</windowsSettings>
</application>
</assembly>
Step 2:
public MainWindow()
{
SourceInitialized += (_, __) =>
{
WindowStartupLocation = WindowStartupLocation.Manual;
WindowState = WindowState.Normal;
Width = 1920;
Height = 1050;
Left = -1920;
Top = 0;
};
InitializeComponent();
}
Result:
MainWindow is indeed showing up on secondary screen, but with wrong Left/Top and using DPI of the Primary screen. Only Width and Height are correct.
References:
The only references that I found are with regards to Notepad, are written in MFC:
https://blogs.windows.com/windowsdeveloper/2016/10/24/high-dpi-scaling-improvements-for-desktop-applications-and-mixed-mode-dpi-scaling-in-the-windows-10-anniversary-update/#jwYiMyGKQRTHkBP7.97
https://github.com/Microsoft/Windows-classic-samples/tree/main/Samples/DPIAwarenessPerWindow
Discussion on GitHub (WPF workarounds)
https://github.com/dotnet/wpf/issues/4127
It is saying something about SetThreadDpiAwarenessContext but it is unclear to me how to make it work in C#....
DPI_AWARENESS_CONTEXT previousDpiContext =
SetThreadDpiAwarenessContext(DPI_AWARENESS_CONTEXT_UNAWARE);
BOOL ret = SetWindowPlacement(hwnd, wp);
SetThreadDpiAwarenessContext(previousDpiContext);
You can move the window to the center of any monitor. It is just a matter of calculation.
using System;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Interop;
public static class WindowHelper
{
public static void MoveToCenter(Window window)
{
if (!GetCursorPos(out POINT cursorPoint))
return;
IntPtr monitorHandle = MonitorFromPoint(cursorPoint, MONITOR_DEFAULTTO.MONITOR_DEFAULTTONULL);
MONITORINFO monitorInfo = new() { cbSize = (uint)Marshal.SizeOf<MONITORINFO>() };
if (!GetMonitorInfo(monitorHandle, ref monitorInfo))
return;
IntPtr windowHandle = new WindowInteropHelper(window).EnsureHandle();
if (!GetWindowPlacement(windowHandle, out WINDOWPLACEMENT windowPlacement))
return;
int left = monitorInfo.rcWork.left + Math.Max(0, (int)((monitorInfo.rcWork.Width - windowPlacement.rcNormalPosition.Width) / 2D));
int top = monitorInfo.rcWork.top + Math.Max(0, (int)((monitorInfo.rcWork.Height - windowPlacement.rcNormalPosition.Height) / 2D));
windowPlacement.rcNormalPosition = new RECT(left, top, windowPlacement.rcNormalPosition.Width, windowPlacement.rcNormalPosition.Height);
SetWindowPlacement(windowHandle, ref windowPlacement);
}
[DllImport("User32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool GetCursorPos(out POINT lpPoint);
[DllImport("User32.dll")]
private static extern IntPtr MonitorFromPoint(POINT pt, MONITOR_DEFAULTTO dwFlags);
private enum MONITOR_DEFAULTTO : uint
{
MONITOR_DEFAULTTONULL = 0x00000000,
MONITOR_DEFAULTTOPRIMARY = 0x00000001,
MONITOR_DEFAULTTONEAREST = 0x00000002,
}
[DllImport("User32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool GetMonitorInfo(IntPtr hMonitor, ref MONITORINFO lpmi);
[StructLayout(LayoutKind.Sequential)]
private struct MONITORINFO
{
public uint cbSize;
public RECT rcMonitor;
public RECT rcWork;
public uint dwFlags;
}
[DllImport("User32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool GetWindowPlacement(IntPtr hWnd, out WINDOWPLACEMENT lpwndpl);
[DllImport("User32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool SetWindowPlacement(IntPtr hWnd, [In] ref WINDOWPLACEMENT lpwndpl);
[StructLayout(LayoutKind.Sequential)]
private struct WINDOWPLACEMENT
{
public uint length;
public uint flags;
public uint showCmd;
public POINT ptMinPosition;
public POINT ptMaxPosition;
public RECT rcNormalPosition;
}
[StructLayout(LayoutKind.Sequential)]
private struct POINT
{
public int x;
public int y;
}
[StructLayout(LayoutKind.Sequential)]
private struct RECT
{
public int left;
public int top;
public int right;
public int bottom;
public int Width => right - left;
public int Height => bottom - top;
public RECT(int x, int y, int width, int height)
{
left = x;
top = y;
right = x + width;
bottom = y + height;
}
}
}
using System.Windows;
public partial class MainWindow : Window
{
public MainWindow()
{
WindowStartupLocation = WindowStartupLocation.CenterScreen;
InitializeComponent();
}
private bool _isMoved;
protected override Size ArrangeOverride(Size arrangeBounds)
{
if (!_isMoved)
{
_isMoved = true;
WindowHelper.MoveToCenter(this);
}
return base.ArrangeOverride(arrangeBounds);
}
}
But I found that the title bar's DPI remains same as that of primary monitor. In general, non-client area's DPI is hard to fix. So this hack is not so practical unless the default title bar is removed.

How to get the resolution of the screen in which a user control is opened?

I wanted to know the resolution of the screen and I have read this question:Get and Set Screen Resolution
However, it seems this give the resolution of the primary screen, but I would like to know if there is some way to get the resolution of the screen in which is shown the window.
For example, I have to screens, the laptop screen and a monitor. The laptop has a resolution of 1366x768 and the monitor has a resolution of 1920x1080. The main screen is the monitor, and the screen of the laptop is the secondary screen.
I would like to have a simple application that it has a button, and I would like than when I click the button, it gives the resolution of the monitor in which I am seeing the window. If I drag the window to the other monitor, then it should give me the resolution of the other monitor.
Thanks.
You can accomplish it by various ways but I like straightforward way using MonitorFromRect and GetMonitorInfo functions. Assuming you set proper app.manifest for Per-Monitor DPI, the following extension method will work to get screen Rect from a FrameworkElement.
Regarding the point jwdonahue raised, this method will return the Rect of screen which has the largest intersection with the specified FrameworkElement.
using System;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Media;
public static class ScreenHelper
{
public static Rect ToScreenRect(this FrameworkElement element) => GetMonitorRect(GetElementRect(element));
private static Rect GetElementRect(FrameworkElement element)
{
var point = element.PointToScreen(new Point(0, 0));
var dpi = VisualTreeHelper.GetDpi(element);
return new Rect(point, new Size(element.ActualWidth * dpi.DpiScaleX, element.ActualHeight * dpi.DpiScaleY));
}
private static Rect GetMonitorRect(Rect elementRect)
{
RECT rect = elementRect;
var monitorHandle = MonitorFromRect(ref rect, MONITOR_DEFAULTTONULL);
if (monitorHandle != IntPtr.Zero)
{
var monitorInfo = new MONITORINFO { cbSize = (uint)Marshal.SizeOf<MONITORINFO>() };
if (GetMonitorInfo(monitorHandle, ref monitorInfo))
{
return monitorInfo.rcMonitor;
}
}
return Rect.Empty;
}
[DllImport("User32.dll")]
private static extern IntPtr MonitorFromRect(ref RECT lprc, uint dwFlags);
private const uint MONITOR_DEFAULTTONULL = 0x00000000;
[DllImport("User32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool GetMonitorInfo(IntPtr hMonitor, ref MONITORINFO lpmi);
[StructLayout(LayoutKind.Sequential)]
private struct MONITORINFO
{
public uint cbSize;
public RECT rcMonitor;
public RECT rcWork;
public uint dwFlags;
}
[StructLayout(LayoutKind.Sequential)]
private struct RECT
{
public int left;
public int top;
public int right;
public int bottom;
public static implicit operator Rect(RECT rect)
{
return new Rect(
rect.left,
rect.top,
rect.right - rect.left,
rect.bottom - rect.top);
}
public static implicit operator RECT(Rect rect)
{
return new RECT
{
left = (int)rect.X,
top = (int)rect.Y,
right = (int)rect.Right,
bottom = (int)rect.Bottom
};
}
}
}

DwmGetWindowAttribute doesn't get the correct Rect size for MediaPlayer if it's in full screen mode

I am trying to get application size using this code :
[DllImport(#"dwmapi.dll")]
private static extern int DwmGetWindowAttribute(IntPtr hwnd, int dwAttribute, out Rect pvAttribute, int cbAttribute);
[Serializable, StructLayout(LayoutKind.Sequential)]
private struct Rect
{
public int Left;
public int Top;
public int Right;
public int Bottom;
public Rectangle ToRectangle()
{
return Rectangle.FromLTRB(Left, Top, Right, Bottom);
}
}
private static bool DWMWA_EXTENDED_FRAME_BOUNDS(IntPtr handle, out Rectangle rectangle)
{
Rect rect;
var result = DwmGetWindowAttribute(handle, (int)Dwmwindowattribute.DwmwaExtendedFrameBounds,
out rect, Marshal.SizeOf(typeof(Rect)));
rectangle = rect.ToRectangle();
return result >= 0;
}
it's working fine for all running applications but if it's Media Player in fullscreen mode I didn't get the right Rect size.
Windows Media Player is weird in full screen mode such that the main window handle doesn't correspond to the full screen window displayed. The full screen window still has a handle but a little more work is needed to get to it.
First you'd need to declare some WinAPI functions and structs:
delegate bool EnumWindowsProc(IntPtr hwnd, IntPtr lParam);
[DllImport("User32.dll")]
static extern bool EnumWindows(EnumWindowsProc lpEnumFunc, IntPtr lParam);
[DllImport("User32.dll")]
static extern bool GetMonitorInfo(IntPtr hMonitor, ref MonitorInfo lpmi);
[DllImport("User32.dll")]
static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
[DllImport("User32.dll")]
static extern IntPtr MonitorFromWindow(IntPtr hwnd, uint dwFlags);
[StructLayout(LayoutKind.Sequential)]
struct MonitorInfo
{
public uint Size;
public Rect Monitor;
public Rect Work;
public uint Flags;
}
// You seem to have this one already
[StructLayout(LayoutKind.Sequential)]
struct Rect
{
public int Left;
public int Top;
public int Right;
public int Bottom;
}
From there, the method looks like this:
// Pass Windows Media Player's main window handle here.
static bool GetWmpFullScreenHandle(IntPtr mainHandle, out IntPtr fullScreenHandle)
{
IntPtr tempHandle = IntPtr.Zero;
// Getting WMP's PID from the main window handle.
GetWindowThreadProcessId(mainHandle, out uint wmpProcessId);
// Optionally, check if the PID resolves to a WMP process.
if (System.Diagnostics.Process.GetProcessById(wmpProcessId).ProcessName != "wmplayer")
{
fullScreenHandle = IntPtr.Zero;
return false;
}
// This iterates through all the open window handles on the machine
// and passes them to the callback below.
EnumWindows((hWnd, lParam) =>
{
// Getting the window handle's PID.
GetWindowThreadProcessId(hWnd, out uint windowProcessId);
// Checking if the window handle belongs to the WMP process.
if (windowProcessId == wmpProcessId)
{
var monitorInfo = new MonitorInfo
{
Size = Convert.ToUInt32(Marshal.SizeOf(typeof(MonitorInfo)))
};
// Getting the dimensions of the monitor the window is displayed on,
// as well as the window dimensions.
if (GetMonitorInfo(MonitorFromWindow(hWnd, 0), ref monitorInfo) &&
GetWindowRect(hWnd, out Rect windowRect))
{
Rect monitorRect = monitorInfo.Monitor;
// If the window dimensions are the same as its monitor's
// dimensions, then we found a hidden full-screen window!
if (windowRect.Left == monitorRect.Left &&
windowRect.Top == monitorRect.Top &&
windowRect.Right == monitorRect.Right &&
windowRect.Bottom == monitorRect.Bottom)
{
tempHandle = hWnd;
}
}
}
return true;
},
IntPtr.Zero);
fullScreenHandle = tempHandle;
// Returns true if the hidden full-screen handle was found, false otherwise.
return fullScreenHandle != IntPtr.Zero;
}
If found, you can then pass the resulting handle to DWMWA_EXTENDED_FRAME_BOUNDS to get the Rectangle.

Drawing text inverting the existing colour [duplicate]

I have a ProgressBar control like the following two:
The first is painted properly. As you can see, the second only has one 0, it's supposed to have two but the other cannot be seen because ProgressBar's ForeColor is the same as the TextColor. Is there a way I can paint the text in black when the ProgressBar below is painted in Lime and paint the text in Lime when the background is black?
You can first draw the background and text, then draw the foreground lime rectangle using PatBlt method with PATINVERT parameter to combine foreground drawing with background drawing:
using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms;
public class MyProgressBar : Control
{
public MyProgressBar()
{
DoubleBuffered = true;
Minimum = 0; Maximum = 100; Value = 50;
}
public int Minimum { get; set; }
public int Maximum { get; set; }
public int Value { get; set; }
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
Draw(e.Graphics);
}
private void Draw(Graphics g)
{
var r = this.ClientRectangle;
using (var b = new SolidBrush(this.BackColor))
g.FillRectangle(b, r);
TextRenderer.DrawText(g, this.Value.ToString(), this.Font, r, this.ForeColor);
var hdc = g.GetHdc();
var c = this.ForeColor;
var hbrush = CreateSolidBrush(((c.R | (c.G << 8)) | (c.B << 16)));
var phbrush = SelectObject(hdc, hbrush);
PatBlt(hdc, r.Left, r.Y, (Value * r.Width / Maximum), r.Height, PATINVERT);
SelectObject(hdc, phbrush);
DeleteObject(hbrush);
g.ReleaseHdc(hdc);
}
public const int PATINVERT = 0x005A0049;
[DllImport("gdi32.dll")]
public static extern bool PatBlt(IntPtr hdc, int nXLeft, int nYLeft,
int nWidth, int nHeight, int dwRop);
[DllImport("gdi32.dll")]
public static extern IntPtr SelectObject(IntPtr hdc, IntPtr hgdiobj);
[DllImport("gdi32.dll", EntryPoint = "DeleteObject")]
public static extern bool DeleteObject(IntPtr hObject);
[DllImport("gdi32.dll")]
public static extern IntPtr CreateSolidBrush(int crColor);
}
Note: The controls is just for demonstrating the paint logic. For a real world application, you need to add some validation on Minimum, Maximum and Value properties.

Categories

Resources