This is some code that I picked up which I tried to implement. Its purpose is to create a form layer which is transparent, full screen, borderless, clickthrough, and always on top of other windows. It then lets you draw using directx over the top of it remaining otherwise transparent.
The parts that don't work are the click-through part, and the directx render. When I run it I basically have an invisible force field in front of all other windows and have to alt-tab around to visual studio to quickly press ALT F5 and end the debug (so at least the always on top and transparency works). I have been trying to figure out why those parts don't work, but my newbie c# skills fail me. hopefully someone can spot why and provide a modification.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Globalization;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;
using System.Threading;
namespace MinimapSpy
{
public partial class Form1 : Form
{
private Margins marg;
//this is used to specify the boundaries of the transparent area
internal struct Margins
{
public int Left, Right, Top, Bottom;
}
[DllImport("user32.dll", SetLastError = true)]
private static extern UInt32 GetWindowLong(IntPtr hWnd, int nIndex);
[DllImport("user32.dll")]
static extern int SetWindowLong(IntPtr hWnd, int nIndex, IntPtr dwNewLong);
[DllImport("user32.dll")]
static extern bool SetLayeredWindowAttributes(IntPtr hwnd, uint crKey, byte bAlpha, uint dwFlags);
public const int GWL_EXSTYLE = -20;
public const int WS_EX_LAYERED = 0x80000;
public const int WS_EX_TRANSPARENT = 0x20;
public const int LWA_ALPHA = 0x2;
public const int LWA_COLORKEY = 0x1;
[DllImport("dwmapi.dll")]
static extern void DwmExtendFrameIntoClientArea(IntPtr hWnd, ref Margins pMargins);
private Device device = null;
public Form1()
{
//Make the window's border completely transparant
SetWindowLong(this.Handle, GWL_EXSTYLE,
(IntPtr)(GetWindowLong(this.Handle, GWL_EXSTYLE) ^ WS_EX_LAYERED ^ WS_EX_TRANSPARENT));
//Set the Alpha on the Whole Window to 255 (solid)
SetLayeredWindowAttributes(this.Handle, 0, 255, LWA_ALPHA);
//Init DirectX
//This initializes the DirectX device. It needs to be done once.
//The alpha channel in the backbuffer is critical.
PresentParameters presentParameters = new PresentParameters();
presentParameters.Windowed = true;
presentParameters.SwapEffect = SwapEffect.Discard;
presentParameters.BackBufferFormat = Format.A8R8G8B8;
this.device = new Device(0, DeviceType.Hardware, this.Handle,
CreateFlags.HardwareVertexProcessing, presentParameters);
Thread dx = new Thread(new ThreadStart(this.dxThread));
dx.IsBackground = true;
dx.Start();
InitializeComponent();
}
protected override void OnPaint(PaintEventArgs e)
{
//Create a margin (the whole form)
marg.Left = 0;
marg.Top = 0;
marg.Right = this.Width;
marg.Bottom = this.Height;
//Expand the Aero Glass Effect Border to the WHOLE form.
// since we have already had the border invisible we now
// have a completely invisible window - apart from the DirectX
// renders NOT in black.
DwmExtendFrameIntoClientArea(this.Handle, ref marg);
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void dxThread()
{
while (true)
{
//Place your update logic here
device.Clear(ClearFlags.Target, Color.FromArgb(0, 0, 0, 0), 1.0f, 0);
device.RenderState.ZBufferEnable = false;
device.RenderState.Lighting = false;
device.RenderState.CullMode = Cull.None;
device.Transform.Projection = Matrix.OrthoOffCenterLH(0, this.Width, this.Height, 0, 0, 1);
device.BeginScene();
//Place your rendering logic here
device.EndScene();
//device.Present();
}
this.device.Dispose();
Application.Exit();
}
}
Here's a refined full sample code for making a window topmost - click through - transparent (= alpha blended). The sample makes a rotating color wheel which is rendered with DirectX, or actually with XNA 4.0, because I believe Microsoft has discontinued developing the managed directx and favours XNA today.
using System;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using Microsoft.Xna.Framework.Graphics;
namespace ClickThroughXNA
{
public partial class Form1 : Form
{
// Directx graphics device
GraphicsDevice dev = null;
BasicEffect effect = null;
// Wheel vertexes
VertexPositionColor[] v = new VertexPositionColor[100];
// Wheel rotation
float rot = 0;
public Form1()
{
InitializeComponent();
StartPosition = FormStartPosition.CenterScreen;
Size = new System.Drawing.Size(500, 500);
FormBorderStyle = System.Windows.Forms.FormBorderStyle.None; // no borders
TopMost = true; // make the form always on top
Visible = true; // Important! if this isn't set, then the form is not shown at all
// Set the form click-through
int initialStyle = GetWindowLong(this.Handle, -20);
SetWindowLong(this.Handle, -20, initialStyle | 0x80000 | 0x20);
// Create device presentation parameters
PresentationParameters p = new PresentationParameters();
p.IsFullScreen = false;
p.DeviceWindowHandle = this.Handle;
p.BackBufferFormat = SurfaceFormat.Vector4;
p.PresentationInterval = PresentInterval.One;
// Create XNA graphics device
dev = new GraphicsDevice(GraphicsAdapter.DefaultAdapter, GraphicsProfile.Reach, p);
// Init basic effect
effect = new BasicEffect(dev);
// Extend aero glass style on form init
OnResize(null);
}
protected override void OnResize(EventArgs e)
{
int[] margins = new int[] { 0, 0, Width, Height };
// Extend aero glass style to whole form
DwmExtendFrameIntoClientArea(this.Handle, ref margins);
}
protected override void OnPaintBackground(PaintEventArgs e)
{
// do nothing here to stop window normal background painting
}
protected override void OnPaint(PaintEventArgs e)
{
// Clear device with fully transparent black
dev.Clear(new Microsoft.Xna.Framework.Color(0, 0, 0, 0.0f));
// Rotate wheel a bit
rot+=0.1f;
// Make the wheel vertexes and colors for vertexes
for (int i = 0; i < v.Length; i++)
{
if (i % 3 == 1)
v[i].Position = new Microsoft.Xna.Framework.Vector3((float)Math.Sin((i + rot) * (Math.PI * 2f / (float)v.Length)), (float)Math.Cos((i + rot) * (Math.PI * 2f / (float)v.Length)), 0);
else if (i % 3 == 2)
v[i].Position = new Microsoft.Xna.Framework.Vector3((float)Math.Sin((i + 2 + rot) * (Math.PI * 2f / (float)v.Length)), (float)Math.Cos((i + 2 + rot) * (Math.PI * 2f / (float)v.Length)), 0);
v[i].Color = new Microsoft.Xna.Framework.Color(1 - (i / (float)v.Length), i / (float)v.Length, 0, i / (float)v.Length);
}
// Enable position colored vertex rendering
effect.VertexColorEnabled = true;
foreach (EffectPass pass in effect.CurrentTechnique.Passes) pass.Apply();
// Draw the primitives (the wheel)
dev.DrawUserPrimitives(PrimitiveType.TriangleList, v, 0, v.Length / 3, VertexPositionColor.VertexDeclaration);
// Present the device contents into form
dev.Present();
// Redraw immediatily
Invalidate();
}
[DllImport("user32.dll", SetLastError = true)]
static extern int GetWindowLong(IntPtr hWnd, int nIndex);
[DllImport("user32.dll")]
static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);
[DllImport("dwmapi.dll")]
static extern void DwmExtendFrameIntoClientArea(IntPtr hWnd, ref int[] pMargins);
}
}
A little extension/modification to Jaska's code, which the form is transparent
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
this.TopMost = true; // make the form always on top
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None; // hidden border
this.WindowState = FormWindowState.Maximized; // maximized
this.MinimizeBox = this.MaximizeBox = false; // not allowed to be minimized
this.MinimumSize = this.MaximumSize = this.Size; // not allowed to be resized
this.TransparencyKey = this.BackColor = Color.Red; // the color key to transparent, choose a color that you don't use
}
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
// Set the form click-through
cp.ExStyle |= 0x80000 /* WS_EX_LAYERED */ | 0x20 /* WS_EX_TRANSPARENT */;
return cp;
}
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
// draw what you want
e.Graphics.FillEllipse(Brushes.Blue, 30, 30, 100, 100);
}
}
Change your extended window style to only WS_EX_LAYERED, window style to only WS_POPUP (NO WS_SIZEBOX) and make sure to use DwmExtendFrameIntoClientArea with all -1's and this will produce transparent windows with layered support: downside is you need to bltbit with GDI from an offscreen directx rendering. Not optimal but it works. This gives mouse click throughs + directx rendering + transparency. Downside is you'll need to inform GDI anytime, pull the directx buffer (all of it or just the damaged portions) and write them to the screem with bltbit.
Setting the extended window style to WS_EX_COMPOSITED and DwmExtendedFrameIntoClientArea with all -1's (similar as above, WS_POPUP on the regular window style). This you can run directx from but no mouse clickthroughs. You can at this point define irregular paths for the hit mask and pass it to windows, its not perfect but if you know a general (non regular) area that can pass-through it'll work.
Still trying to find a true way of using opengl/directx on mac or windows platforms that can pass through mouse clicks with out having to do a bitblt to a legacy rendering system.
I have a simple way use TransparentKey property and a 1x1 pixel label with the color of Form TransparentKey.
On Form and all control MouseMouse event. Set label position to Mouse location.
private void MoveHole()
{
var newLocation = PointToClient(MousePosition);
lblHole.Location = newLocation;
}
Related
Goal Summary
I'm trying to make a WPF window with a custom window caption (so I can paint the title bar and add controls there). I've been successful in doing this with the WPF WindowChrome class, but it introduces a lot of bugs. I've been able to work around most of them by setting the WindowChrome NoneClientFrameEdges property to any value other than None, but this introduced a new bug in the process. There's an unsightly ~1px thick border where the NonClientFrameEdge property is set. You can see it in the video, but it's very faint. I want to either set the color to transparent or find a way to disable rendering it entirely. The problem is that I can't actually remove the NonClientFrameEdge because it's required to fix the bugs I mentioned earlier (more details below).
Detailed explanation of the problem (as best as I can understand it)
From what I understand, a standard window is separated into at least two parts: the client area and the non-client area. The client area is the part of the window you can easily customize, where you add your controls and other window content. The non-client area is the part of the window that includes the frame, which consists of the resize borders and the caption. Under normal conditions you can’t modify the non-client area.
The WindowChrome class in WPF provides an easy way to customize the non-client area of a WPF window. This enables you to design your own caption and place controls there like you see in modern enterprise apps such as Visual Studio and Microsoft Office. The problem is that using WindowChrome causes many bugs. Here’s a list of the ones I’ve found so far:
When you minimize the window from a maximized state and hover over it in the taskbar, the resulting window preview has ~8 px of empty space on the top and top-left sides. If you click it to bring it to the front, it snaps back to the proper space. Even Visual Studio and Microsoft Office have this bug. (caused by having GlassFrameThickness value of 0 set in WindowChrome class)
The transparent glass rectangle effect you get when hovering over the aero peek button for “peek at desktop” no longer works. You just see empty space, no outline of the app. (caused by having GlassFrameThickness value of 0 set in WindowChrome class)
The window is jittery when resizing from any corner other than the bottom right. I’ve read this is because resizing from these corners forces a position change of the window. (caused by having NonClientFrameEdges value of 0, at least 1 edge needs to be set to avoid this)
The window is distorted and blurry during the focus zoom effect when selecting a minimized window from task view. Even many enterprise apps have this problem. (Adobe Photoshop, Visual Studio, and several game launchers, to name a few). (caused either by GlassFrameThickness value of 0, NonClientFrameEdges value of None, or a combination of the two)
Hacky solution and video before and after
I noticed that issue 1 and 2 can be fixed by setting the GlassFrameThickness to a non-zero value. Issues 3 and 4 can be fixed by setting the NonClientFrameEdges property of the WindowChrome class to any value other than None. This video shows all of the problems listed above, along with how they look before and after changing that property: https://www.youtube.com/watch?v=z7O28aEPygg
Note: The video ends abruptly due to my poor editing skills, but it should show all the necessary issues. You will have to look very closely at the bottom of the app in the second half of the video to see the 1px grey/white NonClientFrameEdge, but it's noticeable. This is what I'm trying to fix.
The problem is that setting NonClientFrameEdges to any value other than None literally adds a 2-3 px edge to your window, and it's visually obvious. Setting GlassFrameThickness to 1 on the same edge as the NonClientFrameEdge reduces the visibility significantly, but it's still a noticeable 1px or so eyesore. For example if NonClientFrameEdge is set to Bottom, GlassFrameThickness should be set to "0, 0, 0, 1".
Minimal Example
You can reproduce the problem by creating a .NET or .NET Core WPF project and adding the following code to the window's XAML view file:
<WindowChrome.WindowChrome>
<WindowChrome GlassFrameThickness="0 0 0 1" CornerRadius="0" CaptionHeight="38" UseAeroCaptionButtons="False" ResizeBorderThickness="5" NonClientFrameEdges="Bottom" />
</WindowChrome.WindowChrome>
Here's a Git Repo with a minimal project for convenience if you want it to test but don't feel like typing all the code. It includes some extra stuff like the boilerplate for the ViewModel, command firing, buttons for min/max/close, and a button to add a border for testing (makes things like the resize jitter more apparent). It also includes a hook into WndProc with Pinvoke just in case you want to experiment with the WindowsAPI: https://github.com/cjfcode/WindowProject
The key to solving this is to wire up a window hook with a handler for the WM_NCCALCSIZE (0x83) and WM_NCPAINT (0x85) messages.
WM_NCPAINT will allow you to remove the single pixel bottom border by calling DwmExtendFrameIntoClientArea. In the code below I've wrapped that call in a method called RemoveFrame.
WM_NCCALCSIZE will allow you to change the size of the client area of the window restoring the extra space that WindowChrome set by using GlassFrameThickness="0,0,0,1" and NonClientFrameEdges="Bottom".
I've wrapped up this functionality into a XAML behavior.
Here is final code that will solve your issue:
WindowChromeLoadedBehavior
using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Interop;
using System.Windows.Shell;
using Microsoft.Xaml.Behaviors;
namespace WpfApp1
{
public class WindowChromeLoadedBehavior : Behavior<FrameworkElement>
{
private Window window;
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.Loaded += OnLoaded;
}
protected override void OnDetaching()
{
base.OnDetaching();
AssociatedObject.Loaded -= OnLoaded;
}
private void OnLoaded(object sender, RoutedEventArgs e)
{
window = Window.GetWindow(AssociatedObject);
if (window == null) return;
Task.Delay(5).ContinueWith(_ =>
{
Dispatcher.Invoke(() =>
{
var oldWindowChrome = WindowChrome.GetWindowChrome(window);
if (oldWindowChrome == null) return;
var newWindowChrome = new WindowChrome
{
CaptionHeight = oldWindowChrome.CaptionHeight,
CornerRadius = oldWindowChrome.CornerRadius,
GlassFrameThickness = new Thickness(0, 0, 0, 1),
NonClientFrameEdges = NonClientFrameEdges.Bottom,
ResizeBorderThickness = oldWindowChrome.ResizeBorderThickness,
UseAeroCaptionButtons = oldWindowChrome.UseAeroCaptionButtons
};
WindowChrome.SetWindowChrome(window, newWindowChrome);
});
});
var hWnd = new WindowInteropHelper(window).Handle;
HwndSource.FromHwnd(hWnd)?.AddHook(WndProc);
}
private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
switch (msg)
{
case NativeMethods.WM_NCPAINT:
RemoveFrame();
handled = false;
break;
case NativeMethods.WM_NCCALCSIZE:
handled = false;
var rcClientArea = (RECT)Marshal.PtrToStructure(lParam, typeof(RECT));
rcClientArea.Bottom += (int)(WindowChromeHelper.WindowResizeBorderThickness.Bottom / 2);
Marshal.StructureToPtr(rcClientArea, lParam, false);
var retVal = IntPtr.Zero;
if (wParam == new IntPtr(1))
{
retVal = new IntPtr((int)NativeMethods.WVR.REDRAW);
}
return retVal;
}
return IntPtr.Zero;
}
private void RemoveFrame()
{
if (Environment.OSVersion.Version.Major >= 6 && NativeMethods.IsDwmAvailable())
{
if (NativeMethods.DwmIsCompositionEnabled() && SystemParameters.DropShadow)
{
NativeMethods.MARGINS margins;
margins.bottomHeight = -1;
margins.leftWidth = 0;
margins.rightWidth = 0;
margins.topHeight = 0;
var helper = new WindowInteropHelper(window);
NativeMethods.DwmExtendFrameIntoClientArea(helper.Handle, ref margins);
}
}
}
[Serializable]
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
public int Left;
public int Top;
public int Right;
public int Bottom;
public static RECT Empty;
public int Width => Math.Abs(Right - Left);
public int Height => (Bottom - Top);
public RECT(int left, int top, int right, int bottom)
{
Left = left;
Top = top;
Right = right;
Bottom = bottom;
}
public RECT(RECT rcSrc)
{
Left = rcSrc.Left;
Top = rcSrc.Top;
Right = rcSrc.Right;
Bottom = rcSrc.Bottom;
}
public RECT(Rectangle rectangle) : this(rectangle.Left, rectangle.Top, rectangle.Right, rectangle.Bottom)
{
}
public bool IsEmpty
{
get
{
if (Left < Right)
{
return (Top >= Bottom);
}
return true;
}
}
public override string ToString()
{
if (this == Empty)
{
return "RECT {Empty}";
}
return string.Concat("RECT { left : ", Left, " / top : ", Top, " / right : ", Right, " / bottom : ", Bottom, " }");
}
public override bool Equals(object obj)
{
return ((obj is RECT) && (this == ((RECT)obj)));
}
public override int GetHashCode()
{
return ((Left.GetHashCode() + Top.GetHashCode()) + Right.GetHashCode()) + Bottom.GetHashCode();
}
public static bool operator ==(RECT rect1, RECT rect2)
{
return ((((rect1.Left == rect2.Left) && (rect1.Top == rect2.Top)) && (rect1.Right == rect2.Right)) && (rect1.Bottom == rect2.Bottom));
}
public static bool operator !=(RECT rect1, RECT rect2)
{
return !(rect1 == rect2);
}
static RECT()
{
Empty = new RECT();
}
}
}
}
WindowChromeHelper
using System;
using System.Runtime.InteropServices;
using System.Windows;
namespace WpfApp1
{
public static class WindowChromeHelper
{
public static Thickness LayoutOffsetThickness => new Thickness(0d, 0d, 0d, SystemParameters.WindowResizeBorderThickness.Bottom);
/// <summary>
/// Gets the properly adjusted window resize border thickness from system parameters.
/// </summary>
public static Thickness WindowResizeBorderThickness
{
get
{
var dpix = GetDpi(GetDeviceCapsIndex.LOGPIXELSX);
var dpiy = GetDpi(GetDeviceCapsIndex.LOGPIXELSY);
var dx = GetSystemMetrics(GetSystemMetricsIndex.CXFRAME);
var dy = GetSystemMetrics(GetSystemMetricsIndex.CYFRAME);
// This adjustment is needed since .NET 4.5
var d = GetSystemMetrics(GetSystemMetricsIndex.SM_CXPADDEDBORDER);
dx += d;
dy += d;
var leftBorder = dx / dpix;
var topBorder = dy / dpiy;
return new Thickness(leftBorder, topBorder, leftBorder, topBorder);
}
}
[DllImport("user32.dll")]
private static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC);
[DllImport("user32.dll")]
private static extern IntPtr GetDC(IntPtr hwnd);
[DllImport("gdi32.dll")]
private static extern int GetDeviceCaps(IntPtr hdc, int nIndex);
private static float GetDpi(GetDeviceCapsIndex index)
{
var desktopWnd = IntPtr.Zero;
var dc = GetDC(desktopWnd);
float dpi;
try
{
dpi = GetDeviceCaps(dc, (int)index);
}
finally
{
ReleaseDC(desktopWnd, dc);
}
return dpi / 96f;
}
private enum GetDeviceCapsIndex
{
LOGPIXELSX = 88,
LOGPIXELSY = 90
}
[DllImport("user32.dll")]
private static extern int GetSystemMetrics(GetSystemMetricsIndex nIndex);
private enum GetSystemMetricsIndex
{
CXFRAME = 32,
CYFRAME = 33,
SM_CXPADDEDBORDER = 92
}
}
}
NativeMethods
using System;
using System.Runtime.InteropServices;
namespace WpfApp1
{
public static class NativeMethods
{
public const int WM_NCCALCSIZE = 0x83;
public const int WM_NCPAINT = 0x85;
[DllImport("kernel32", SetLastError = true, CharSet = CharSet.Ansi, BestFitMapping = false, ThrowOnUnmappableChar = true)]
private static extern IntPtr LoadLibrary(string lpFileName);
[DllImport("dwmapi.dll", PreserveSig = false)]
public static extern bool DwmIsCompositionEnabled();
[DllImport("kernel32", SetLastError = true, ExactSpelling = true, CharSet = CharSet.Ansi, BestFitMapping = false, ThrowOnUnmappableChar = true)]
private static extern IntPtr GetProcAddress(IntPtr hModule, string procName);
[StructLayout(LayoutKind.Sequential)]
public struct MARGINS
{
public int leftWidth;
public int rightWidth;
public int topHeight;
public int bottomHeight;
}
private delegate int DwmExtendFrameIntoClientAreaDelegate(IntPtr hwnd, ref MARGINS margins);
public static int DwmExtendFrameIntoClientArea(IntPtr hwnd, ref MARGINS margins)
{
var hModule = LoadLibrary("dwmapi");
if (hModule == IntPtr.Zero)
{
return 0;
}
var procAddress = GetProcAddress(hModule, "DwmExtendFrameIntoClientArea");
if (procAddress == IntPtr.Zero)
{
return 0;
}
var delegateForFunctionPointer = (DwmExtendFrameIntoClientAreaDelegate)Marshal.GetDelegateForFunctionPointer(procAddress, typeof(DwmExtendFrameIntoClientAreaDelegate));
return delegateForFunctionPointer(hwnd, ref margins);
}
public static bool IsDwmAvailable()
{
if (LoadLibrary("dwmapi") == IntPtr.Zero)
{
return false;
}
return true;
}
internal enum WVR
{
ALIGNTOP = 0x0010,
ALIGNLEFT = 0x0020,
ALIGNBOTTOM = 0x0040,
ALIGNRIGHT = 0x0080,
HREDRAW = 0x0100,
VREDRAW = 0x0200,
VALIDRECTS = 0x0400,
REDRAW = HREDRAW | VREDRAW
}
}
}
i am making an app that overlays other d3d games,
the app is working perfectly except it has a huge performance impact on the cpu
taking 21.4 % of the cpu when rendering only a single line !
i am using slimdx library on c# and here is my full code
OverLay.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using SlimDX.Direct3D11;
using SlimDX.Windows;
using System.Runtime.InteropServices;
using System.Security;
using SlimDX;
using SlimDX.DXGI;
using Device = SlimDX.Direct3D9.Device;
using Resource = SlimDX.Direct3D9.Resource;
using System.Threading;
using D3D = SlimDX.Direct3D9;
namespace OverlayForm
{
public partial class OverLay : RenderForm
{
RenderForm form;
Device device;
// D3D.Sprite sprite;
public OverLay()
{
InitializeComponent();
Paint += OverLay_Paint;
FormBorderStyle = FormBorderStyle.None;
ShowIcon = false;
ShowInTaskbar = false;
TopMost = true;
WindowState = FormWindowState.Maximized;
//Make the window's border completely transparant
//SetWindowLong(Handle , GWL_EXSTYLE , (IntPtr)(GetWindowLong(Handle , GWL_EXSTYLE) ^ WS_EX_LAYERED ^ WS_EX_TRANSPARENT));
SetWindowLong(Handle , GWL_EXSTYLE , (IntPtr)(GetWindowLong(Handle , GWL_EXSTYLE) | WS_EX_LAYERED | WS_EX_TRANSPARENT));
//Set the Alpha on the Whole Window to 255 (solid)
SetLayeredWindowAttributes(Handle , 0 , 255 , LWA_ALPHA);
form = this;
form.FormClosing += Form_FormClosing;
//Init DirectX
//This initializes the DirectX device. It needs to be done once.
//The alpha channel in the backbuffer is critical.
D3D.PresentParameters presentParameters = new D3D.PresentParameters();
presentParameters.Windowed = true;
presentParameters.SwapEffect = D3D.SwapEffect.Discard;
presentParameters.BackBufferFormat = D3D.Format.A8R8G8B8;
device = new Device(new D3D.Direct3D() , 0 , D3D.DeviceType.Hardware , Handle ,
D3D.CreateFlags.HardwareVertexProcessing , presentParameters);
//sprite = new D3D.Sprite(device);
font = new D3D.Font(device , new Font("Arial" , 9 , FontStyle.Regular));
line = new D3D.Line(this.device);
MessagePump.Run(form , new MainLoop(dxThread));
}
private void Form_FormClosing(object sender , FormClosingEventArgs e)
{
device.Dispose();
}
int centerx = Screen.PrimaryScreen.WorkingArea.Width / 2;
int centery = Screen.PrimaryScreen.WorkingArea.Height / 2;
private void OverLay_Paint(object sender , PaintEventArgs e)
{
//Create a margin (the whole form)
marg.Left = 0;
marg.Top = 0;
marg.Right = Width;
marg.Bottom = Height;
//Expand the Aero Glass Effect Border to the WHOLE form.
// since we have already had the border invisible we now
// have a completely invisible window - apart from the DirectX
// renders NOT in black.
DwmExtendFrameIntoClientArea(Handle , ref marg);
}
private static D3D.Font font;
private static D3D.Line line;
private void dxThread()
{
form.TopMost = true;
device.SetRenderState(D3D.RenderState.ZEnable , false);
device.SetRenderState(D3D.RenderState.Lighting , false);
device.SetRenderState(D3D.RenderState.CullMode , D3D.Cull.None);
device.SetTransform(D3D.TransformState.Projection , Matrix.OrthoOffCenterLH(0 , Width , Height , 0 , 0 , 1));
device.BeginScene();
//DrawFilledBox(0 , 0 , 100 , 100 , Color.White);
//font.DrawString( null, "Swag" , 10, 10 , new Color4(Color.White));
//DrawBox(0 , 0 , 10 , 10 , 1 , Color.Green);
DrawLine(0 , 0 , Screen.PrimaryScreen.Bounds.Width , Screen.PrimaryScreen.Bounds.Height , 2 , Color.Pink);
device.EndScene();
device.Present();
}
public static void DrawFilledBox(float x , float y , float w , float h , Color Color)
{
Vector2[] vLine = new Vector2[2];
line.GLLines = true;
line.Antialias = false;
line.Width = w;
vLine[0].X = x + w / 2;
vLine[0].Y = y;
vLine[1].X = x + w / 2;
vLine[1].Y = y + h;
line.Begin();
line.Draw(vLine , new Color4(Color));
line.End();
}
public static void DrawLine(float x1 , float y1 , float x2 , float y2 , float w , Color Color)
{
Vector2[] vLine = new Vector2[2] { new Vector2(x1 , y1) , new Vector2(x2 , y2) };
line.GLLines = true;
line.Antialias = false;
line.Width = w;
line.Begin();
line.Draw(vLine , new Color4(Color));
line.End();
}
public static void DrawBox(float x , float y , float w , float h , float px , System.Drawing.Color Color)
{
DrawFilledBox(x , y + h , w , px , Color);
DrawFilledBox(x - px , y , px , h , Color);
DrawFilledBox(x , y - px , w , px , Color);
DrawFilledBox(x + w , y , px , h , Color);
}
#region Extras
private Margins marg;
//this is used to specify the boundaries of the transparent area
internal struct Margins
{
public int Left, Right, Top, Bottom;
}
[DllImport("user32.dll" , SetLastError = true)]
private static extern UInt32 GetWindowLong(IntPtr hWnd , int nIndex);
[DllImport("user32.dll")]
static extern int SetWindowLong(IntPtr hWnd , int nIndex , IntPtr dwNewLong);
[DllImport("user32.dll")]
static extern bool SetLayeredWindowAttributes(IntPtr hwnd , uint crKey , byte bAlpha , uint dwFlags);
public const int GWL_EXSTYLE = -20;
public const int WS_EX_LAYERED = 0x80000;
public const int WS_EX_TRANSPARENT = 0x20;
public const int LWA_ALPHA = 0x2;
public const int LWA_COLORKEY = 0x1;
[DllImport("dwmapi.dll")]
static extern void DwmExtendFrameIntoClientArea(IntPtr hWnd , ref Margins pMargins);
#endregion
}
}
Program.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace OverlayForm
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
static void Main()
{
using (OverLay x = new OverLay())
{
}
}
}
}
Please note :
i already saw this : Very high CPU usage directx 9
but i am using MessagePump.Run and don't know how to apply the answer.
The reason for the high CPU is that SlimDX is using PeekMessage rather than the more usual GetMessage (that the majority of Windows apps use). The former does not wait for a message to appear in the message pump unlike the latter. In other words, GetMessage() will block the current thread possibly reducing the CPU load which is what you want a well-behaved Windows desktop application to do.
MSDN:
Retrieves a message from the calling thread's message queue. The function dispatches incoming sent messages until a posted message is available for retrieval.
Unlike GetMessage, the PeekMessage function does not wait for a message to be posted before returning More...
Now a typical graceful Windows message pump looks like this:
while( (bRet = GetMessage( &msg, NULL, 0, 0 )) != 0)
{
if (bRet == -1)
{
// handle the error and possibly exit
}
else
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
...however SlimDX uses what some people refer to as an action game loop:
static bool AppStillIdle
{
get
{
Message msg;
return !PeekMessage(out msg, IntPtr.Zero, 0, 0, 0);
}
}
public void MainLoop()
{
// hook the application's idle event
Application.Idle += new EventHandler(OnApplicationIdle);
Application.Run(form);
}
void OnApplicationIdle(object sender, EventArgs e)
{
while (AppStillIdle)
{
// Render a frame during idle time (no messages are waiting)
RenderFrame();
}
}
With nothing to draw you will experience a very tight loop of PeekMessage with no waiting in between!
My suggestion is that you either use one of the MessagePump.Run overloads for idle, or add a sleep as per below:
Change this:
void OnApplicationIdle(object sender, EventArgs e)
{
while (AppStillIdle)
{
// Render a frame during idle time (no messages are waiting)
RenderFrame();
}
}
...to this:
void OnApplicationIdle(object sender, EventArgs e)
{
while (AppStillIdle)
{
// Render a frame during idle time (no messages are waiting)
RenderFrame();
Thread.Sleep(0); // <------------- be graceful
}
}
Note the use of Thread.Sleep(0). This pauses for the minimal amount of time whilst still allowing the thread to be relinquished to the OS.
MSDN:
The number of milliseconds for which the thread is suspended. If the value of the millisecondsTimeout argument is zero, the thread relinquishes the remainder of its time slice to any thread of equal priority that is ready to run. If there are no other threads of equal priority that are ready to run, execution of the current thread is not suspended.
I see in your answer you already had a Thread.Sleep(50) but now it is good to know why SlimDX requires a Sleep in the first place and that 50 is perhaps too high a value.
GetMessage
OP:
i won't need a very active rendering mechanism, because i will only use this to show overlay from my music player about current song playing , next song , etc
Considering this is the case, the most CPU-efficient means is to replace your action loop with a turn-based game loop using GetMessage() instead of PeekMessage(). Then put your rendering in your application's OnIdle() callback.
As Nick Dandoulakis says in Windows Game Loop 50% CPU on Dual Core:
Nick:
That's a standard game loop for action games, where you must update objects positions / game world.
If you are making a board game GetMessage would be a better choice.
It really depends on what game you are making. More...
No Sleep() required.
the problem was that i was using full power of cpu even when not rendering, so adding
Thread.Sleep(50);
at the end of the dxThread method lowered it to only
I want to know if it's possible to set the client area of a borderless form. Say for example I define a form like so:
Code
public class MyForm : Form
{
public MyForm()
{
this.FormBorderStyle = FormBorderStyle.None;
}
}
Result
What I want to do is specify the client area, so that the form has a frame (like the standard windows frame, but custom drawn).
Result
Essentially, the blue area would become the non client area, and the gray area would remain as the client area.
I have tried to set the client area, but this just seems to resize the entire form, thus, is does not leave behind a "non-client" area
Is this possible?
This is possible, however I don't know how well this works with a Windows Form with the WindowStyle set to Borderless. Using PInvoke (Platform Invoke) Functions, you can remove window themes which will give you a very basic looking Windows Form. You can then use various PInvoke functions to manipulate the Non-client area of the windows form.
I recommend that you read through these topics. They're designed for Win32 Applications using C++, but PInvoke is the process of calling these native APIs using Managed Code (C#)
WM_NCCALCSIZE: https://msdn.microsoft.com/en-us/library/windows/desktop/ms632634(v=vs.85).aspx
WM_NCPAINT: https://msdn.microsoft.com/en-us/library/windows/desktop/dd145212(v=vs.85).aspx
GetDCEx: https://msdn.microsoft.com/en-us/library/windows/desktop/dd144873(v=vs.85).aspx
GetWindowDC: https://msdn.microsoft.com/en-us/library/windows/desktop/dd144947(v=vs.85).aspx
SetWindowTheme: https://msdn.microsoft.com/en-us/library/windows/desktop/bb759827(v=vs.85).aspx
This example is very very crude, but it provides basic functionality. I don't know how SetWindowTheme works on Windows 8 or 8.1, but in Windows 7, it gives windows the "classic" theme.
public partial class MyForm : Form
{
//Window Messages
public const uint WM_NCPAINT = 0x85;
public const uint WM_NCCALCSIZE = 0x83;
public const uint WM_NCHITTEST = 0x84;
//GetDCEx Flags
public const int DCX_WINDOW = 0x00000001;
public const int DCX_CACHE = 0x00000002;
public const int DCX_PARENTCLIP = 0x00000020;
public const int DCX_CLIPSIBLINGS = 0x00000010;
public const int DCX_CLIPCHILDREN = 0x00000008;
public const int DCX_NORESETATTRS = 0x00000004;
public const int DCX_LOCKWINDOWUPDATE = 0x00000400;
public const int DCX_EXCLUDERGN = 0x00000040;
public const int DCX_INTERSECTRGN = 0x00000080;
public const int DCX_INTERSECTUPDATE = 0x00000200;
public const int DCX_VALIDATE = 0x00200000;
//RECT Structure
[System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
public struct RECT
{
public int left, top, right, bottom;
}
//WINDOWPOS Structure
[System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
public struct WINDOWPOS
{
public IntPtr hwnd;
public IntPtr hwndinsertafter;
public int x, y, cx, cy;
public int flags;
}
//NCCALCSIZE_PARAMS Structure
[System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
public struct NCCALCSIZE_PARAMS
{
public RECT rgrc0, rgrc1, rgrc2;
public WINDOWPOS lppos;
}
//SetWindowTheme UXtheme Function
[System.Runtime.InteropServices.DllImport("uxtheme.dll", ExactSpelling = true, CharSet = System.Runtime.InteropServices.CharSet.Unicode)]
public static extern int SetWindowTheme(
IntPtr hWnd,
String pszSubAppName,
String pszSubIdList);
//GetWindowRect User32 Function
[System.Runtime.InteropServices.DllImport("user32.dll", ExactSpelling = true)]
[return: System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.Bool)]
public static extern bool GetWindowRect(
IntPtr hwnd,
out RECT lpRect
);
//GetWindowDC User32 Function
[System.Runtime.InteropServices.DllImport("user32.dll", ExactSpelling = true)]
public static extern IntPtr GetWindowDC(
IntPtr hWnd
);
//GetDCEx User32 Function
[System.Runtime.InteropServices.DllImport("user32.dll", ExactSpelling = true)]
public static extern IntPtr GetDCEx(
IntPtr hWnd,
IntPtr hrgnClip,
int flags
);
//Window Procedure Hook
protected override void WndProc(ref Message m)
{
//Don't style window in designer...
if (DesignMode)
base.WndProc(ref m);
//Handle Message
switch ((uint)m.Msg)
{
case WM_NCCALCSIZE: WmNCCalcSize(ref m); break;
case WM_NCPAINT: WmNCPaint(ref m); break;
default: base.WndProc(ref m); break;
}
}
//Handle Creation
protected override void OnHandleCreated(EventArgs e)
{
//Base Procedure...
base.OnHandleCreated(e);
//Remove Theme
SetWindowTheme(this.Handle, string.Empty, string.Empty);
}
//WM_NCCALCSIZE
private void WmNCCalcSize(ref Message m)
{
//Get Window Rect
RECT formRect = new RECT();
GetWindowRect(m.HWnd, out formRect);
//Check WPARAM
if (m.WParam != IntPtr.Zero) //TRUE
{
//When TRUE, LPARAM Points to a NCCALCSIZE_PARAMS structure
var nccsp = (NCCALCSIZE_PARAMS)System.Runtime.InteropServices.Marshal.PtrToStructure(m.LParam, typeof(NCCALCSIZE_PARAMS));
//We're adjusting the size of the client area here. Right now, the client area is the whole form.
//Adding to the Top, Bottom, Left, and Right will size the client area.
nccsp.rgrc0.top += 30; //30-pixel top border
nccsp.rgrc0.bottom -= 4; //4-pixel bottom (resize) border
nccsp.rgrc0.left += 4; //4-pixel left (resize) border
nccsp.rgrc0.right -= 4; //4-pixel right (resize) border
//Set the structure back into memory
System.Runtime.InteropServices.Marshal.StructureToPtr(nccsp, m.LParam, true);
}
else //FALSE
{
//When FALSE, LPARAM Points to a RECT structure
var clnRect = (RECT)System.Runtime.InteropServices.Marshal.PtrToStructure(m.LParam, typeof(RECT));
//Like before, we're adjusting the rectangle...
//Adding to the Top, Bottom, Left, and Right will size the client area.
clnRect.top += 30; //30-pixel top border
clnRect.bottom -= 4; //4-pixel bottom (resize) border
clnRect.left += 4; //4-pixel left (resize) border
clnRect.right -= 4; //4-pixel right (resize) border
//Set the structure back into memory
System.Runtime.InteropServices.Marshal.StructureToPtr(clnRect, m.LParam, true);
}
//Return Zero
m.Result = IntPtr.Zero;
}
//WM_NCPAINT
private void WmNCPaint(ref Message m)
{
//Store HDC
IntPtr HDC = IntPtr.Zero;
Graphics gfx = null;
//Check the WPARAM
if(m.WParam == (IntPtr)1)
{
//For reasons unknown to me, the update region doesn't contain valid data and calling GetDCEx will do nothing.
//So I call GetWindowDC and exclude the area using System.Drawing.Graphics instead.
//Graphics Object from HDC
HDC = GetWindowDC(m.HWnd);
gfx = Graphics.FromHdc(HDC);
//Exclude Client Area
gfx.ExcludeClip(new Rectangle(4, 30, Width - 8, 34)); //Exclude Client Area (GetWindowDC grabs the WHOLE window's graphics handle)
}
else
{
//Graphics Object from HDC
HDC = GetDCEx(m.HWnd, m.WParam, DCX_WINDOW | DCX_INTERSECTRGN);
gfx = Graphics.FromHdc(HDC);
}
//Call Paint
using (PaintEventArgs ncPaintArgs = new PaintEventArgs(gfx, new Rectangle(0, 0, Width, Height)))
MyForm_NCPaint(this, ncPaintArgs);
//Return Zero
m.Result = IntPtr.Zero;
}
public MyForm()
{
InitializeComponent();
}
private void MyForm_NCPaint(object sender, PaintEventArgs e)
{
//Clear
e.Graphics.Clear(Color.Green);
}
}
I am working on creating a simple notebook application. I have been asked to make the input area look like a sheet of notebook paper, with the text sitting on light blue lines. I am trying to make this work, but it seems to be failing miserably.
So far, I have created a transparent RichTextBox that sits on top of a panel. The Text Box is:
using System;
using System.Windows.Forms;
public class TransparentTextBox : RichTextBox
{
public TransparentTextBox()
{
this.SetStyle(ControlStyles.Opaque, true);
this.SetStyle(ControlStyles.OptimizedDoubleBuffer, false);
}
protected override CreateParams CreateParams
{
get
{
CreateParams parms = base.CreateParams;
parms.ExStyle |= 0x20; // Turn on WS_EX_TRANSPARENT
return parms;
}
}
}
The paint code for the panel:
private void paper_Paint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;
g.Clear(Color.White);
g.DrawLine(new Pen(Brushes.LightPink, 2), 20, 0, 20, paper.Height);
int h = TextRenderer.MeasureText("Testj", txtBody.Font).Height;
for (int x = 2 + h; x < paper.Height; x += h)
{
g.DrawLine(new Pen(Brushes.LightSkyBlue, 2), 0, x, paper.Width, x);
}
}
The lines are static, and they will grow to fit any font size/family that is chosen. The problem is when the text box is scrolled. The lines won't move with the text. I have tried to link the handle of the scroll bar to the lines, but they don't seem to be linking properly.
The code to get the current scroll position:
[StructLayout(LayoutKind.Sequential)]
public struct SCROLLINFO
{
public int cbSize;
public uint fMask;
public int nMin;
public int nMax;
public uint nPage;
public int nPos;
public int nTrackPos;
}
public enum ScrollBarDirection
{
SB_HORZ = 0,
SB_VERT = 1,
SB_CTL = 2,
SB_BOTH = 3
}
public enum ScrollInfoMask
{
SIF_RANGE = 0x1,
SIF_PAGE = 0x2,
SIF_POS = 0x4,
SIF_DISABLENOSCROLL = 0x8,
SIF_TRACKPOS = 0x10,
SIF_ALL = SIF_RANGE + SIF_PAGE + SIF_POS + SIF_TRACKPOS
}
...
public partial class Form1 : Form
{
[DllImport("User32.dll", EntryPoint = "GetScrollInfo")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool GetScrollInfo([In]IntPtr hwnd, [In]int fnBar, [In, Out]ref SCROLLINFO lpsi);
...
private void txtBody_VScroll(object sender, EventArgs e)
{
inf.cbSize = Marshal.SizeOf(inf);
inf.fMask = (int)ScrollInfoMask.SIF_ALL;
GetScrollInfo(txtBody.Handle, 1, ref inf);
Console.WriteLine(inf.nTrackPos + ":" + inf.nPos + ":" + TextRenderer.MeasureText("Testj", txtBody.Font).Height);
paper.Invalidate();
}
Then the paint above was modified to use this:
for (int x = inf.nPos % h; x < paper.Height; x += h)
{
g.DrawLine(new Pen(Brushes.LightSkyBlue, 2), 0, x, paper.Width, x);
}
I also tried to use nTrackPos, but neither seemed to follow the text like I want it to. I'm not too familiar with C#, so I wanted to know what I am missing/could do better. I am using Visual Studio 2008, with Visual C# 2008. .Net framework 3.5 SP1
So, here is what I came up with after some intensive googling. I decided to follow more into Gusman's comment on my question and look into drawing on the textbox again. After some playing, I realized I was improperly calculating the position of the start line. So, I reconfigured my custom RichTextBox to look like:
using System;
using System.Drawing;
using System.Windows.Forms;
using System.Runtime.InteropServices;
namespace Journal
{
class CustomRichTextBox : RichTextBox
{
private const int WM_HSCROLL = 0x114;
private const int WM_VSCROLL = 0x115;
private const int WM_MOUSEWHEEL = 0x20A;
private const int WM_PAINT = 0x00F;
private const int EM_GETSCROLLPOS = 0x4DD;
public int lineOffset = 0;
[DllImport("user32.dll")]
public static extern int SendMessage(
IntPtr hWnd,
int Msg,
IntPtr wParam,
ref Point lParam
);
protected override void WndProc(ref Message m)
{
base.WndProc(ref m);
if (m.Msg == WM_PAINT)
{
using (Graphics g = base.CreateGraphics())
{
Point p = new Point();
//get the position of the scrollbar to calculate the offset
SendMessage(this.Handle, EM_GETSCROLLPOS, IntPtr.Zero, ref p);
//draw the pink line on the side
g.DrawLine(new Pen(Brushes.LightPink, 2), 0, 0, 0, this.Height);
//determine how tall the text will be per line
int h = TextRenderer.MeasureText("Testj", this.Font).Height;
//calculate where the lines need to start
lineOffset = h - (p.Y % h);
//draw lines until there is no more box
for (int x = lineOffset; x < Height; x += h)
{
g.DrawLine(new Pen(Brushes.LightSkyBlue, 2), 0, x, Width, x);
}
//force the panel under us to draw itself.
Parent.Invalidate();
}
}
}
public CustomRichTextBox()
{
this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
}
}
}
I then set this box inside of a panel to get the padding I want. The panel is forced to redraw itself with the text box.
I'm trying to make a tooltip for my video player. I use a windows media player embedded into my winform's app on c# (AxWMPLib.AxWindowsMediaPlayer) for playing video. And I have created a control that shows a current position of a media. This control is transparent. Control's code is below:
namespace player.Controls
{
public partial class TransparentToolTip : System.Windows.Forms.UserControl
{
public enum PointerLocation : byte { ... }
#region Private data
// ...
#endregion
Timer Wriggler = new Timer();
int iInterval = 100;
protected void TickHandler(object sender, EventArgs e)
{
this.InvalidateEx();
}
private void _SetStyle()
{
this.SetStyle((ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint | ControlStyles.ResizeRedraw | ControlStyles.CacheText | ControlStyles.ContainerControl), true);
this.SetStyle(ControlStyles.Selectable, false);
this.SetStyle(ControlStyles.SupportsTransparentBackColor | ControlStyles.Opaque, true);
this.UpdateStyles();
}
private void _SetTimer(int _Interval)
{
Wriggler.Tick += new EventHandler(TickHandler);
this.Wriggler.Interval = _Interval;
this.Wriggler.Enabled = true;
}
public TransparentToolTip()
{
InitializeComponent();
_SetStyle();
_SetTimer(iInterval);
}
public TransparentToolTip(System.ComponentModel.IContainer container)
{
container.Add(this);
InitializeComponent();
_SetStyle();
_SetTimer(iInterval);
}
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
cp.ExStyle |= (0x00000020 | 0x00000008); // WS_EX_TRANSPARENT = 0x00000020, WS_EX_TOPMOST = 0x00000008
return cp;
}
}
#region Extra Properties
// ...
#endregion
// Drawing
protected void InvalidateEx()
{
if (Parent == null)
return;
Rectangle rc = new Rectangle(this.Location, this.Size);
Parent.Invalidate(rc, true);
}
protected override void OnPaintBackground(PaintEventArgs pevent)
{
}
protected override void OnPaint(PaintEventArgs pe)
{
pe.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
pe.Graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
pe.Graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;
pe.Graphics.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality;
Rectangle rect = new Rectangle(this.ClientRectangle.X, this.ClientRectangle.Y, this.ClientRectangle.Width - 1, this.ClientRectangle.Height - 1);
SolidBrush brushBackground = new SolidBrush(ColorBackground);
SolidBrush brushBorder = new SolidBrush(ColorBorder);
using (GraphicsPath graphicsPath = ToolTipBody(...))
{
using (Pen p = new Pen(brushBorder, BorderSize))
{
pe.Graphics.FillPath(brushBackground, graphicsPath); // background
pe.Graphics.DrawPath(p, graphicsPath); // borders
}
}
TextFormatFlags flags = // some flags;
TextRenderer.DrawText(pe.Graphics, ToolTipText, Font, new Rectangle(this.ClientRectangle.X, this.ClientRectangle.Y, this.ClientRectangle.Width, this.ClientRectangle.Height - TriangleSizeSide), ToolTipColor, System.Drawing.Color.Transparent, flags);
brushBorder.Dispose();
brushBackground.Dispose();
base.OnPaint(pe);
}
// Form mapping tips
private GraphicsPath ToolTipBody(...)
{
// some code
return graphicsPath;
}
}
I try to show this tooltip over the axWindowsMediaPlayer object. But my control is overlapped by the media player. I've tried to use SetWindowPos but this does not work:
static readonly IntPtr HWND_TOPMOST = new IntPtr(-1);
const int SWP_NOSIZE = 0x0001;
const int SWP_NOMOVE = 0x0002;
const int SWP_SHOWWINDOW = 0x0040;
//...
[DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int x, int y, int cx, int cy, int flags);
//...
SetWindowPos(transparentToolTip1.Handle, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
Note: If do not change CreateParams of a new control it draws over the media player. But it becomes opaque.
Is any ideas to do it correct using winforms?
My guess is that the transparent thing is happening because you're defining the control style as ControlStyles.Opaque. Then, setting the flag to WS_EX_TRANSPARENT you're setting it to transparent style again.
About the overlapped issue: If you designed the form using the Visual Studio Windows Forms designer, maybe your control is zOrdered down the AxWMPLib.AxWindowsMediaPlayer component. Try to bring to front or control the ChildrenIndex in your control collection before show it.
Hope it helps.