How Can I use Graphics.CopyFromScreen to enable alpha transparent controls? - vb.net

I am trying to use (and this may be my problem already) e.Graphics.CopyFromScreen() in the OnPaint() method of my control to prepare a background onto which I can draw the content of the control. The goal is a tool-tip with rounded corners and other specific features to replace the default Windows tool tip.
Ideally the tool tip could be antialiased and blended into the backdrop, however at a pinch I could tolerate an aliased boundary. This may also be necessary as the control will often be covering a control with an OpenGL overlay and the whole blending procedure may end up a nightmare.
I have created a custom user control, a persistent instance of which belonds to my applications's main form, overriding the OnPaint and OnPaintBackground methods. The application of transparency however is challenging. As I understand, the graphics object passed to the paint method starts with a copy of the current VisibleClipBounds, where generally the first move is to clear the control with a solid colour and start drawing the main features. My first thought was to bypass the fill and start drawing on the captured background. For the first redraw this works fine, but subsequent redraws keep the previous content instead of re-capturing the backdrop. I hoped that CopyFromScreen would specifically eliminate the currently drawing control from its capture so as to start from a clean base but this is not the case, it captures the rectangle of the new location including the image of the previous location if it is in the bounds.
Other solutions and permutations thereof such as
SetStyle(ControlStyles.SupportsTransparentBackColor, True)
Me.BackColor = Color.Transparent
e.Graphics.Clear(Color.FromArgb(0,0,0,0))
have to-date yielded no appropriate results.
I started with a transparent form using the transparent key colour. This worked with aliasing but using a form as a tool tip seemed excessive and caused annoying artifacts (some of which could be overcome) such as the temporary presence of the taskbar button, focus loss of underlying form with subsequent visual feedback in the host form's title bar colour. The control is much lighter weight but I am struggling to make it work even as well as the form.
At this point I simply ask how to achieve the desired result and whether CopyFromScreen is a part of that solution.

That's a lot of text to read - but addressing transparency to achieve a custom-shaped form/control... Control.Region can do the same thing:
[DllImport("Gdi32.dll", EntryPoint = "CreateRoundRectRgn")]
private static extern IntPtr CreateRoundRectRgn
(
int nLeftRect, // x-coordinate of upper-left corner
int nTopRect, // y-coordinate of upper-left corner
int nRightRect, // x-coordinate of lower-right corner
int nBottomRect, // y-coordinate of lower-right corner
int nWidthEllipse, // height of ellipse
int nHeightEllipse // width of ellipse
);
private void CreateRegion()
{
this.Region = System.Drawing.Region.FromHrgn(CreateRoundRectRgn(0, 0, Width, Height, 40, 40));
}

If your control is set as a top-level window and you're running it on Windows 7 or 8, check into DwmEnableBlurBehindWindow. In the parameter structure, I set enable to true and I create a region 1x1 pixels for the blur. With BackColor and TransparencyKey set to Black, this gives me a entirely transparent window (complete with mouse pass-through) that I can paint 32-bit images to with full alpha transparency (using Graphics.FromHwnd). A bonus to this method is that if the window moves or the background changes, Windows DWM does all the work--I don't have to update a thing.

Related

Transparent window and view using Apple's Metal

I want to create a window which is fully transparent and which can be used for drawing (On-Screen-Display). It's actually quite simple to create such a window, the one has to set the opaque property to false and fill the corresponding view with a clear color. After that the CGImage can be drawn using the graphical context to update the view and the corresponding regions with alpha channel will be transparent / semi-transparent.
I decided to make the same thing but using the Metal API, i.e. I replaced my view with a MTKView and used textures instead of CGImage to draw onto the window. I read the corresponding chapters from Apple Documentation, then I took one of the examples ("Basic Texturing") and decided to modify it in order to test if it would work:
I modifed the example by settings the opaque property of the window to NO.
In the first line of drawInMTKView function I changed the clearColor in the following way: view.clearColor = MTLClearColorMake(0.0, 0.0, 0.0, 0.0); (as shown "Devices and Commands" tutorial from docs).
Then I changed the alpha channel value of the loaded image in AAPLImage.h from dstImageData[dstPixelIndex + 3] = 255; to dstImageData[dstPixelIndex + 3] = 125;.
After all these change, I would expect that my view is fully transparent (the clear color is a fully transparent color) and that the parts of the texture with the alpha channel would have the corresponding transparency level. However the view is black by default and I cannot make parts of the view (or the whole view) transparent.
I may assume that I have to add some additional configuration to the rendering pipeline in order to make it work. However it could be that such things are not possible with Metal, but I'm not sure about that.
Also I tried to follow the suggestions from Metal MTLTexture replaces semi-transparent areas with black when alpha values that aren't 1 or 0 , but unfortunately it did not help.
There is also an isOpaque property on the view class that may need to be false. In iOS (UIView), you can simply set it, but in macOS (NSView) you need to override it to return false.

Make Form Maximized, but not Full Screen

I would like to make my Form1 Maximized, but not Full Screen, as differentiated by Maximized taking the entirety of the screen while leaving space dedicated to the Taskbar at the bottom. To clarify, I'd like my application to work how other applications traditionally work, such as Chrome or the MS Office suite when maximized. Definitely nothing fancy.
To make my application resize itself dynamically, I've calculated where buttons will fit relative to the height and width of the application. However, I discovered quickly that my application, in Maximized state, actually continued underneath the task bar (or, in reverse, the taskbar is entirely cut off by the application - it is Full Screen). My bottom-most buttons are actually cut off by the taskbar. After experimenting with different Form settings, I discovered this appears to be entirely the result of WindowState = FormWindowState.Maximized setting.
I've tried various routes to counteract this behavior. First, I attempted to resize the maximum bounds programmatically, while leaving the Form's MaximumSize set to "0,0" (the default "resize me as needed" setting). I have this code in the Form_Load event:
Me.MaximizedBounds = Screen.PrimaryScreen.WorkingArea
and
Me.MaximizedBounds = Screen.FromHandle(Me.Handle).WorkingArea
However, these did not have any affect, even though the working area height is confirmed smaller than the form height. In fact, upon further testing, I have not been able to programmatically change the maximizedbounds at all, apart from manually in the form properties, setting the MaximumSize to 100,100. Does anyone have suggestions here?
I also tried to change the WindowsState = FormWindowState.Manual and readjust the form height this way:
Width = Screen.PrimaryScreen.WorkingArea.Width
Height = Screen.PrimaryScreen.WorkingArea.Height
Which makes the form closer to the dedicated dimensions, but makes the window movable and no longer covers the entirety of a traditionally maximized screen (it leaves about 20 pixels padding on all sides).
In the end, I'm at a bit of a loss on how to do this and would really appreciate some clarification. I have attempted other code I'd be glad to share, as well as similar questions on the forum, but none have helped me get my targeted behavior. I have a locked Taskbar on the most recent update to Windows 10, if those details make any difference.

Make a responsive form in design view (not programmatically)

I am doing a WinForms program which should have a fully responsive design in a full screen.
I get an approach which works more or less well. It consists into calculate a ratio between display screen and original form size.
Then I apply this ratio to the width, left, height, top properties of each control inside the form.
My doubt is about to use a native way for doing this, since, using anchors, the controls keep their same distances with parent control borders, but I doesn't do proportionally, for instance:
Form with 100x100
Button 20x20 located in (10,10)
If I resized the form to 200x200 (multiply by 2), the best approach I can do in design view is keeping the four anchors to the button, so button size will be 120x120 at the same position (10,10), while what I need is a button with size 40x40, at position (20,20), since form size was multiply by 2.
Is it possible with winforms native operations in design view? (Avoiding to make calculations)
Yes it is possible.
Using the Property Dock = Fill you can ask for a component to take all the room in its container.
Now using a TableLayoutPanel, you can define cells to put your components in. And giving cell a percentage size, you can make sure the sizes will change when the form is resized...
Here are more information on these things :
Dock Property
TableLayoutPanel Class
TableLayoutPanel Tutorial

How to shrink wrap image in wxScrolledWindow

I have a frame that has some stuff at the top, and a wxScrolledWindow below. The scrolled window contains an image selected by the user. I want the frame to shrink-wrap it so that background never shows in the scrolled window. I am using a BoxSizer in the frame. Everything works perfectly until the user drags a border to increase the viewing area. He takes it too far, and background shows. I have tried at least a dozen methods, but I cannot get the window's border to snap back when the user drags the border beyond where the scrollbars disappear. When the image is loaded, the scrolled window calls SetMaxClientSize() with the right numbers, but it has no effect. A couple of pictures will help. I have just dragged the right border to the right, increasing the viewing area. Here is what it looks like:
I want the border to snap back so it looks like this:
Windows 7 x64, wxWidgets 2.9.4
Re-implement the size event handler. If the new size is larger than you want, modify it to the largest you accept. Then call the base implementation.
void MyFrame::OnSize(wxSizeEvent& event)
{
wxSize new_size = event.GetSize()
wxSize max_size = ... calculate max size ....
if ( new_size.GetHeight() > max_size.GetHeight() {
new_size.SetHeight( max_size.GetHeight() );
if ( new_size.GetWidth() > max_size.GetWidth() {
new_size.SetWidth( max_size.GetWidth() );
event.SetSize( new_size );
wxFrame::OnSize( event );
Also, be aware of this note in the documentation:
Important : Sizers ( see Sizers Overview ) rely on size events to
function correctly. Therefore, in a sizer-based layout, do not forget
to call Skip on all size events you catch (and don't catch size events
at all when you don't need to).
Ravenspoint's answer doesn't seem to work (using wxWidgets 3.0.3 or 2.8.12). The frame is resized larger than desired even when the event size is reduced and propagated as you suggested. Here's an example:

What is a general solution to creating a transparent control?

So having looked this up for a while now, I have read probably twenty different implementations but have not yet seen a solid, control-based, framework-based general solution to implementing truly transparent controls that works.
This possibly oversimplified approach doesn't do anything at all and creates a routine (InvalidateEx) that never gets used: http://bytes.com/topic/c-sharp/answers/248836-need-make-user-control-transparent
This seems to be hinting at what the previous answer was getting at: https://web.archive.org/web/20141227200000/http://bobpowell.net/transcontrols.aspx
however it uses a timer tick to execute the InvalidateEx method.
This appears to be abandoning multi-layered controls and handles all drawing on a single layered panel, while glossing over how the label object appears to be implementing its own transparency: http://www.codeproject.com/Articles/25048/How-to-Use-Transparent-Images-and-Labels-in-Window
This is a completely non-framework based method: http://www.codeproject.com/Articles/30135/General-solution-for-transparent-controls
The accepted solution in this answer simply does not work for me: Transparent Control on Transparent control?
The control I get is a generic solid colour (white) with the transparent image drawn over it.
So, I start on my own implementation.
I'd rather not rely on bottom-up GDI drawing, i.e. perform all drawing in the parent-most form, I'd rather not do pre-rendering to transparent bitmaps, I'd rather not inherit from something other than Forms.Control that is performing some clever tricks of its own to short-cut transparency. I really want to get to the bottom of how to draw on a control surface with complete transparency support. Evidently the label control for instance has a full implementation inside itself, how does it work?
Logically, a transparent control cannot rely on simple 1-bit clip regions and needs to stay mostly complete. Multi-layering of controls needs to be handled correctly, i.e. correct drawing order etc. without causing infinite loops.
The first and most common recommendation is this:
SetStyle(ControlStyles.SupportsTransparentBackColor, True)
Me.BackColor = Color.Transparent
where the first line I understand is synonymous with
Protected Overrides ReadOnly Property CreateParams As System.Windows.Forms.CreateParams
Get
Dim result As System.Windows.Forms.CreateParams = MyBase.CreateParams
result.ExStyle = result.ExStyle Or WindowsMessages.WS_EX_TRANSPARENT
Return result
End Get
End Property
Which has very different behaviours depending on which class is being inherited. In the case of Forms.Control, OnPaintBackground chooses to clear the clip rectangle with the parent control's background colour. Not too clever.
Overriding and removing the base OnPaintBackground call causes the control to stay 'dirty' having never overwritten what came below it. Clearing using a transparent colour (optimistic approach) yields a field of black transparent pixels and causes the control to appear completely black.
Logically this makes sense, underlying controls in a bid for efficiency do not redraw the area they anticipate this control with draw over. That area of the form then never gets updated and stays whatever came before the form existed. The painting of a transparent colour onto this is a little confusing, since it implies that the graphics buffer does not even contain the underlying pixels but actual nothings that are replaced with black transparent somethings to make the control appear black.
The first challenge appears to be handling when the control itself redraws, getting all underlying controls to redraw themselves in that region (without automatically invalidating the control itself to start a stack overflow), then painting itself over the underlying and up-to-date pixels. I haven't found a way to do this yet. The closest I have come is through digging around the .net code and finding the ButtonRenderer.DrawParentBackground method. This largely seems to work, though a quick test showed that z order of overlapping components was still not handled correctly, with underlying controls drawing over the top of this control.
The second challenge then is to have the control properly redraw itself when an underlying control changes. By disconnecting the automatic invalidation in challenge one, it makes it difficult to notify the control to invalidate itself and not invalidate its parents.
If someone has solved this problem completely, please provide the code, if not, please provide some experience in how I might handle one or several of the problem's parts.
Many thanks.
Edit:
It appears the assumed detail about WS_EX_TRANSPARENT as per WS_EX_TRANSPARENT - What does it actually do?
Research Update: ButtonRenderer.DrawParentBackground is actually just used by Control itself, only if Application.EnableVisualStyles() is used and simply forces the background draw of the parent. It appears to be a synonym of
InvokePaintBackground(Parent, e.Graphics)
Which will reproduce all background drawn details of the parent control.
I have created the required general transparency control, the solution was a detailed breakdown of the control and all of its siblings in the parent, and preparing a recursive drawing routine on the OnPaintBackground() method relying on the InvokePaint() and InvokePaintBackground() methods. By defining successively smaller clips and intersecting with lower controls, this method minimises drawing overhead.
The complete control includes a method of detecting and responding to sibling controls' Invalidate methods, efficiently drawing the stack of transparent controls and effectively allowing full alpha channel overlays on animated underlying controls.
The control can be interleaved with all permutations of child and parent controls while giving visually accurate transparency.
Hit testing has not been considered in the control.
This control will not overdraw controls whose drawing is not performed during paint events. This is a big limitation.
To use the control one simply inherits from the base and overrides the OnPaint method to perform custom drawing. It is also possible to override the OnPaintBackground method so long as a call to the base method is called first.
Finally, if anyone would like the full code including invalidation handling, let me know. If you have a solution to system drawn components let me know that too. Also if you have a more trivial solution that implies I wasted a bunch of time on this, I wouldn't be upset about receiving that either!
An excerpt of the control on the OnPaintBackground method is presented:
''' <summary>
''' Recursively paint all underlying controls in their relevant regions, stacking drawing operations as necessary.
''' </summary>
''' <param name="pevent"></param>
''' <remarks></remarks>
Protected Overrides Sub OnPaintBackground(pevent As System.Windows.Forms.PaintEventArgs)
'Store clip and transform for reversion
Dim initialClip As Region = pevent.Graphics.Clip
Dim initialTransform As Drawing2D.Matrix = pevent.Graphics.Transform
'Develop list of underlying controls
Dim submarinedControls As New List(Of Control)
For Each Control As Control In m_Siblings
If Control.Visible AndAlso Above(Control) AndAlso Me.ClientRectangle.IntersectsWith(Control.RelativeClientRectangle(Me)) Then submarinedControls.Add(Control)
Next
'Prepare clip for parent draw
Dim parentClip As System.Drawing.Region = New System.Drawing.Region(initialClip.GetRegionData)
For Each Control As Control In submarinedControls
parentClip.Exclude(Control.RelativeClientRectangle(Me))
Next
pevent.Graphics.Clip = parentClip
'Evaluate control relationship to parent, temporarily adjusting transformation for parent redraw. This translation must be relative since the incoming graphics may already have a meaningful transform applied.
Dim translation As Point = Parent.RelationTo(Me)
pevent.Graphics.Transform = New Drawing2D.Matrix(1, 0, 0, 1, initialTransform.OffsetX + translation.X, initialTransform.OffsetY + translation.Y)
'Fully draw parent background
InvokePaintBackground(Parent, pevent)
InvokePaint(Parent, pevent)
'Reset transform for sibling drawing
pevent.Graphics.Transform = initialTransform
'Develop initial clip of submarined siblings
Dim siblingClip As System.Drawing.Region = New System.Drawing.Region(initialClip.GetRegionData)
siblingClip.Exclude(parentClip)
For Each Control As Control In submarinedControls
'Define relative position of submarined sibling to self
translation = Control.RelationTo(Me)
'Define and apply clip *before* transformation
Dim intersectionClip As New Region(Control.RelativeClientRectangle(Me))
intersectionClip.Intersect(siblingClip)
pevent.Graphics.Clip = intersectionClip
'Apply transformation
pevent.Graphics.Transform = New Drawing2D.Matrix(1, 0, 0, 1, initialTransform.OffsetX + translation.X, initialTransform.OffsetY + translation.Y)
'Raise sibling control's paint events
InvokePaintBackground(Control, pevent)
InvokePaint(Control, pevent)
'Revert transformation and exclude region
pevent.Graphics.Transform = initialTransform
siblingClip.Exclude(intersectionClip)
Next
'Revert transform and clip to pre-drawing state
pevent.Graphics.Transform = initialTransform
pevent.Graphics.Clip = initialClip
End Sub
Me.Visible = False
OR
Me.Opacity = 0
OR
Me.Hide()