I'm attempting to create a window with the following properties:
Completely invisible except for a small, moving region
Can be clicked through to windows below it, EXCEPT if you click in that small region
Doesn't show up on the taskbar (although this one isn't as pressing at the moment)
I can figure out how to make the WHOLE window transparent (partially or entirely) and click-through using WS_EX_LAYERED and WS_EX_TRANSPARENT, setting TopMost, and changing the opacity, but I can't figure out how to make these requirements true for the window EXCEPT for a limited part. Any idea where to start?
EDIT: It's been pointed out to me that making a huge, invisible window is rather pointless, and it would be smarter to create a small window and use FormBorderStyle.FixedToolWindow and ShowInTaskbar to make it borderless and not present on the task bar, then move it around. I still need to figure out how to make the "background" of the window transparent and click-through, so I'm leaving this question up.
So, I managed to figure it out through trial and error, and I apologize for taking people's time.
The solution here is to:
Override OnPaintBackground to be an empty method, so nothing actually gets painted on the back
Set the style so it supports Transparent as a back color, then set the back color and TransparencyKey to Transparent
Set FormBorderStyle to None, TopMost to True, ControlBox to False, ShowInTaskbar to False, and Text to String.Empty
End result: A form that is completely invisible and that can be clicked through, while still letting the user interact with any controls placed on the form.
Related
I'm trying to place a transparent and borderless child (WinForm) form on top of another child (WinForm) form that is opaque, but I'd like to retain the ability to directly click on the transparent form. There are a few answers on the web regarding making a transparent form that can be clicked through, but I want to make one that I can click on.
I've found this answer, which shows that setting my transparent form's BackColor and TransparencyKey to something specific like Color.Red achieves the desired behavior. However based on this answer, it seems this behavior between certain Colors and TransparencyKey may actually be a long-running bug.
Ideally I don't want to rely on a bug to achieve a desired effect. What would be a more "appropriate" approach for making a clickable, transparent, and borderless (WinForm) form?
Update (Additional Context):
I'm basically creating a screen pixel previewer for color data extraction.
Overlay forms containing captured bitmap(s) of the screen area.
Another form that gives a visual indicator for the pixel area being previewed (small black box in the below snapshot). This form is placed above the bitmap forms. I have this form as transparent (to see through to the below bitmaps), but I still want to be able to click on it for event processing.
Without the TransparencyKey = BackColor = Color.Red trick, clicking within the small black box causes focus to move to the below bitmap form, which then covers up the small box form and the preview window showing the zoomed view. The purpose of the click is to capture the cursor position for additional processing. I can work around this by immediately giving focus back to the small box + preview forms, but that occasionally causes flicker.
It seams to be a bug, if you change a Form size to a previously transparent region, mouse events will no longer be detected on it.
How to reproduce it:
Create a new Windows form project
Set Form1 TransparencyKey to Magenta
Add a Panel to Form1 and config it as:
Dock fill
BackColor to Magenta
Run, now try to change the Form size, if you reduce it (enough so the new border will be placed over a current transparent region), the mouse will no longer "detect" the window border on that side. If you minimize/restore the window, it will work again. How can I fix that? I tried Refresh on Form Layout/Resize event, but it didn't work.
Just to confirm, yes this is a bug in the current Aero implementation on Windows 8.1. Possibly before. Been around for quite a while, seems like it is a pretty structural problem. It is part of a set of bugs related to layered windows with the transparency key set, and getting the mouse to be transparent to such a window, it also doesn't work properly with certain color selections for the key. In this specific case, it inappropriately makes the frame transparent to clicks as well.
Hard to give specific advice, this does require calling Microsoft Support to get ahead. Technically you can take advantage of another bug, the window is never transparent to mouse clicks when you pick, say, Red as the transparency key:
public Form1() {
InitializeComponent();
this.TransparencyKey = panel1.BackColor = Color.Red;
}
Solves the bug you are dealing with, but of course disables mouse transparency completely. A workaround you almost definitely won't like is:
protected override void OnResizeEnd(EventArgs e) {
base.OnResizeEnd(e);
this.RecreateHandle();
}
Too noticeable. Programmers are starting to take advantage of these bugs, sometimes they want such lack of transparency intentionally. Makes you wonder how Microsoft is ever going to get this fixed without breaking some program. Not pretty.
I have a winforms C# application that opens multiple dialog boxes. To suit the style of the application, I have removed the default title bars for each window and created my own (with control buttons and drag-to-move function).
The problem that now faces me is that without a titlebar, the user has no way of telling which window is the 'active' window when they are manually moved apart (so they are not overlapping).
In any windows application (that uses titlebars), when you try to navigate away from a dialog box back to the main program (without closing the dialog box) - it wont let you. The border of the dialog box flashes and you hear a windows error sound. Some kind of equivalent visual feedback would be great without needing to have the default titlebars - and tinting an entire window darker seems like it would do the trick nicely.
Something like this in pseudo-code, which would nicely tint the parent window whilst a dialog is open:
// tint window now
window.ShowDialog();
// un-tint window
I have tried to place a panel covering everything with colour set to 'transparent' (with the intention of later controlling the opacity of the panel) but the transparency does not seem to work. Any other ideas of accomplishing this? Or does anyone have a better suggestion to achieve the same level of visual feedback?
Summary:
Is there any way to tint an entire window, or overlay it with a colour? If not, could anyone suggest an alternate method of making the window appear 'inactive'?
I would suggest you to create a method in forms you want to disable:
void DisableForm()
{
//some fancy color
this.BackColor = System.Drawing.Color.Khaki;
//and disable all controls owned by form, just to be sure
foreach (var s in this.Controls)
{
((Control)s).Enabled = false;
}
}
and functions which enables back those forms of course.
edit.
also you can set visibility property of controls to false
I've created a semi-transparent form (60% opacity with black background color) that my app launches, maximized, over the entire screen. Basically, it casts a gray color on the entire desktop.
When the user mouses-over a window on the desktop, I want to get that window's handle (hWnd).
The easy way to do this, which is working for me, is:
Temporarily hide my form (OR, temporarily set my form's opacity to 0.0)
Call [GetCursorPos][1]
Call [WindowFromPoint][2]
Show my form again
The problem with this approach is that my form / the screen blinks, which I don't like.
I've tried to fix this in two ways:
I figure there should be a way to get the hWnd of the window directly underneath my form by calling ChildWindowFromPointEx (passing-in the hWnd of the desktop and CWP_SKIPTRANSPARENT), but it doesn't seem to work. I also played with [ChildWindowFromPoint][4] and [RealChildWindowFromPoint][5] with no success. (P.S. Raymond Chen discusses the differences between these calls, here and it seems to me that ChildWindowFromPointEx is designed to do exactly what I need)
I tried preventing the entire desktop from refreshing (kind of "freezing" the screen momentarily) by using (1) SendMessage(GetDesktopWindow(), WM_SETREDRAW, false, 0) before I hide my form and (2) SendMessage(GetDesktopWindow(), WM_SETREDRAW, true, 0) after I hide my form. This didn't work quite right: some areas of the screen would freeze, some weird black blocks would appear, etc. I do know, however, that (1) does work, because one time I called (1) and didn't call (2) and my desktop appeared completely frozen (had to reboot, even TaskMgr didn't render correctly). I also tried using SuspendLayout and ResumeLayout on my form, but I don't think they are meant to handle my case.
Any help would be greatly appreciated.
You can do the checking yourself since your need to customise beyond that what the standard functions offer.
Call EnumWindows() to get a list of top-level windows.
Remove your semi-transparent window from this list.
For each window in the list use PtInRegion() to determine whether or not the mouse is over the window. Remove any windows that don't fit the bill.
Use GetNextWindow(), starting from one of the remaining windows to walk the z-order and find out which of the candidates is at the top.
In my project, I create a form with the opacity controlled by the user. If the form was fully transparent the mouse events 'fell through' (without my intervention), otherwise my form handled them.
After reading this question and overriding the CreateParams property to set the WS_EX_TRANSPARENT flag on my form, it now allows mouse events to fall through when the opacity is any value <255.
This is exactly what I want, but it concerns me that I don't understand why it works.
From what I've read, WS_EX_TRANSPARENT is meant to make the form appear transparent by 'stealing bits' from the form below it in its Paint method, therefore:
The Paint method of my form and all the controls in it should never be called, right? As WS_EX_TRANSPARENT should cause Windows to override them, so why does it affect input handling but not the drawing of my form?
The opacity should have no impact on the handling of mouse events, as if Paint is being overridden the 'local' opacity should not matter, no?
Could someone explain, what this flag really does? How does it work?
WS_EX_TRANSPARENT makes your events (like mouse clicks) fall through your window, amongst other things. Opacity is a separate concept, it instructs window manager to apply alphablending when drawing your form. Those two things are not related, but when you combine them you get the effect you need in your case.
So:
Paint method of your form is called normally as it should, opacity has nothing to do with it.
Opacity does not have anything to do with mouse events, as described in the first paragraph.
It makes the window invisible to mouse events, or -as Microsoft puts it- it doesn't obscure the windows below. I believe it doesn't actually steal pixels from the windows below, but Windows itself will probably blend those two pictures together, using the level of transparancy you supplied.
Transparent windows can be useful for showing some progress or a splash screen, but you'll have to program a way to close them, because just click the X won't work since the mouse click will pass through it.
So not only does it change the level of visual transparency, but it modifies the behaviour too. I wonder where you would have read otherwise.
[edit]
Don't Windows in C# just have an opacity property?