I like to have my form borderless in C#. So I used this code:
FormBorderStyle = FormBorderStyle.None;
But it removes the aero effect of windows 8. The form opens suddenly like a blink.
How can i bring the aero effect back?
I am answering my own question using the C++ reference
I wrote a class inherited from Form Class. Each form in application should be inherited from this class instead of Form Class.
public class AeroForm : Form
{
int _w = 100, _h = 100;
bool aero = false;
[StructLayout(LayoutKind.Sequential)]
struct MARGINS { public int Left, Right, Top, Bottom; }
[DllImport("dwmapi.dll", PreserveSig = false)]
static extern void DwmExtendFrameIntoClientArea
(IntPtr hwnd, ref MARGINS margins);
[DllImport("dwmapi.dll", PreserveSig = false)]
static extern bool DwmIsCompositionEnabled();
public AeroForm()
{
aero = IsCompositionEnabled();
}
public AeroForm(int width, int height)
: this()
{
_w = width;
_h = height;
Size = new Size(width, height);
}
protected override void WndProc(ref Message m)
{
const int WM_NCCALCSIZE = 0x0083;
switch (m.Msg)
{
case WM_NCCALCSIZE:
if (aero)
return;
break;
}
base.WndProc(ref m);
}
//this is for checking the OS's functionality.
//Windows XP does not have dwmapi.dll
//also, This corrupts the designer...
//so i used the Release/Debug configuration
bool IsCompositionEnabled()
{
#if !DEBUG
return File.Exists(Environment.SystemDirectory + "\\dwmapi.dll")
&& DwmIsCompositionEnabled();
#else
return false;
#endif
}
//this one is used for a shadow when aero is not available
protected override CreateParams CreateParams
{
get
{
const int CS_DROPSHADOW = 0x00020000;
const int WS_MINIMIZEBOX = 0x20000;
const int CS_DBLCLKS = 0x8;
CreateParams cp = base.CreateParams;
if (!aero)
cp.ClassStyle |= CS_DROPSHADOW;
cp.Style |= WS_MINIMIZEBOX;
cp.ClassStyle |= CS_DBLCLKS;
return cp;
}
}
//this is for aero shadow and border configurations
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
if (aero)
{
FormBorderStyle = FormBorderStyle.FixedSingle;
ControlBox = false;
MinimizeBox = false;
MaximizeBox = false;
Size = new Size(_w, _h);
MARGINS _glassMargins = new MARGINS()
{
Top = 5,
Left = 5,
Bottom = 5,
Right = 5
};
DwmExtendFrameIntoClientArea(this.Handle, ref _glassMargins);
}
else
FormBorderStyle = FormBorderStyle.None;
}
//When you minimize and restore, the size will change.
//this override is for preventing this unwanted resize.
protected override void OnPaint(PaintEventArgs e)
{
if (aero)
Size = new Size(_w, _h);
base.OnPaint(e);
}
}
with Show() and Application.Run() it works great!
but it has some regressions when the form opens with ShowDialog(). Closing the form, you will see a border that closes after your client content hides.
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 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);
}
}
In my form, I'm using a tabcontrol. I want to hide the tab headers and border both. I can do either one, if I try to hide the headers then the border becomes visible. Can anyone help me, please? thanks and here's my code:
public Form3()
{
InitializeComponent();
this.NativeTabControl1 = new NativeTabControl();
this.NativeTabControl1.AssignHandle(this.tabControl1.Handle);
}
private NativeTabControl NativeTabControl1;
private class NativeTabControl : NativeWindow
{
protected override void WndProc(ref Message m)
{
if ((m.Msg == TCM_ADJUSTRECT))
{
RECT rc = (RECT)m.GetLParam(typeof(RECT));
//Adjust these values to suit, dependant upon Appearance
rc.Left -= 3;
rc.Right += 3;
rc.Top -= 3;
rc.Bottom += 3;
Marshal.StructureToPtr(rc, m.LParam, true);
}
base.WndProc(ref m);
}
private const Int32 TCM_FIRST = 0x1300;
private const Int32 TCM_ADJUSTRECT = (TCM_FIRST + 40);
private struct RECT
{
public Int32 Left;
public Int32 Top;
public Int32 Right;
public Int32 Bottom;
}
private void Form3_Load(object sender, EventArgs e)
{
//hides tabcontrol headers
tabControl1.Appearance = TabAppearance.Buttons;
tabControl1.ItemSize = new Size(0, 1);
tabControl1.SizeMode = TabSizeMode.Fixed;
}
}
There is another thread on Stackoverflow, which provides some ideas to hide the tab row of the TabControl in Windows Forms.
My favorite one is to override WndProc and set the Multiline property to true.
public partial class TabControlWithoutHeader : TabControl
{
public TabControlWithoutHeader()
{
if (!this.DesignMode) this.Multiline = true;
}
protected override void WndProc(ref Message m)
{
if (m.Msg == 0x1328 && !this.DesignMode)
m.Result = new IntPtr(1);
else
base.WndProc(ref m);
}
}
I tested the code on Windows 8.1 and see neither the tabs nor a border line. So I think you do not need to use code like you posted.
I would usually suggest doing this in the xaml not C# (I'm a WPF developer) I believe you can do it in the C# as well by naming both the TabControl as well as each Tab itself.
tabControlName.BorderBrush = null;
///^Gets rid of the TabControl's border.
tabName1.Height = 0;
tabName2.Height = 0;
tabNameETC.Height = 0;
///^Removes the tabs(headers) if you have the TabControl.TabStripPlacement set to left
/// or right, then use the following instead:
tabName1.Width = 0
tabName2.Width = 0
tabNameETC.Width = 0
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;
}
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.