aehimself 396 Posted March 30, 2021 Hello, I am trying to "dim" a whole tabsheet so a notification can be clearly shown - blocking the sheet itself, but not blocking the main form. I guess I'd need an alpha blended panel or a semi-transparent bitmap drawn on a TImage... never really worked with imaging until now so I'm not sure about the terms. Other than the standard components I have Graphics32 installed; can this be achieved with a custom TPanel descendant or with Graphics32 somehow? I would like not to install any 3rd party component just for this if not needed. Worst case scenario I'll capture the tabsheet as a bitmap and draw it on a alClient panel with Canvas.Draw - which supports opacity. Using Delphi 10.4.2 if it matters. Share this post Link to post
Renate Schaaf 64 Posted March 30, 2021 Hi, You can use the code in https://www.delphipraxis.net/1486219-post6.html to make a TWincontrol (semi)transparent (not just a stringgrid). Use it for a black panel, set its parentbackground to false and alignClient it on the tabsheet. I tried it with Delphi 10.3.3 CE. Quote 1 1 Share this post Link to post
aehimself 396 Posted March 30, 2021 NICE!!!! And it even works with VCL Styles. Thank you, @Renate Schaaf! Share this post Link to post
aehimself 396 Posted March 30, 2021 ...one small question, though. Is it possible to make the panel transparent, but not the components on it? Share this post Link to post
Guest Posted March 30, 2021 (edited) you can use "TransparentZZZZZ properties in your form" in an easy way. for another component, you can try to use the same color in the Form or in any other hack! no need any code at all... hug Edited March 30, 2021 by Guest Share this post Link to post
Renate Schaaf 64 Posted March 30, 2021 (edited) 2 hours ago, aehimself said: ...one small question, though. Is it possible to make the panel transparent, but not the components on it? As you see, the code I referred to isn't mine, I just found it useful. But let me try 🙂 Edit: The short answer seems to be that it isn't supported. When one tries to remove the layered style from the child controls of the panel they either get invisible or one gets a Win-Error. For the long answer I guess you have to go back to your bitmap-idea. This: https://www.codeguru.com/csharp/csharp/cs_controls/tutorials/article.php/c12323/Creating-NonTransparent-Controls-on-a-SemiTransparent-Window.htm is old, but looks good. Edited March 30, 2021 by Renate Schaaf Share this post Link to post
Remy Lebeau 1403 Posted March 31, 2021 7 hours ago, Renate Schaaf said: You can use the code in https://www.delphipraxis.net/1486219-post6.html to make a TWincontrol (semi)transparent (not just a stringgrid). Do note that solution is applying the WS_EX_LAYERED window style to a child window, which is supported only in Windows 8 onwards, and only if the app manifest specifies Windows 8 as a <supportedOS>. 1 Share this post Link to post
aehimself 396 Posted March 31, 2021 9 hours ago, Renate Schaaf said: When one tries to remove the layered style from the child controls of the panel they either get invisible or one gets a Win-Error. I was afraid so. I got WinErrors when I tried to do so. 9 hours ago, Renate Schaaf said: For the long answer I guess you have to go back to your bitmap-idea. This: https://www.codeguru.com/csharp/csharp/cs_controls/tutorials/article.php/c12323/Creating-NonTransparent-Controls-on-a-SemiTransparent-Window.htm is old, but looks good. While the bitmap screenshot idea will work, it feels really hacky. Makes me a bit more comfortable that others got to the same idea to this problem, though. It's just a little bit strange that "dimming" is this complicated to achieve. Thanks anyway, I'll start with the bitmap idea 🙂 Share this post Link to post
Renate Schaaf 64 Posted March 31, 2021 (edited) 3 hours ago, aehimself said: While the bitmap screenshot idea will work, it feels really hacky. Here's a different kind of hack: Design another form with a completely transparent background (transparentColor = true) and borderless. Place the controls you would have on the semitransparent panel on this form instead. When you want to dim your tabsheet, place a blank semitransparent panel as before. Then place the second form on top of the tabsheet and show it. If so desired, you can have the transparent form size and move (WM_Move) with the main form. (In the handler for WM_Move check that the transparent form is assigned, as it is called very early for the main form) Event handlers for the controls on the 2nd form can be assigned in the code of the main form to avoid circular reference. It works so far for me, you can certainly figure out how to deal with the rest of the nooks and crannies. This is fun! Edit: I forgot the styles: you need to remove seClient and seBorder from the style elements of the transparent form Edited March 31, 2021 by Renate Schaaf forgot the styles Share this post Link to post
aehimself 396 Posted March 31, 2021 So, the "screenshot" and repaint dimmed works... almost. The bitmap is captured correctly (saved to a file, shows perfectly) but drawing it back causes some issues... I have a TPanel descendant, like... TDimPanel= Class(TPanel) protected Procedure Paint; Override; Procedure Resize; Override; End; Procedure TDimPanel.Resize; Var dc: HWND; Begin inherited; _bitmap.SetSize(0, 0); // Clear the bitmap _bitmap.SetSize(Self.Parent.Width, Self.Parent.Height); // Self.Parent.PaintTo(_bitmap.Canvas.Handle, 0, 0); // Does not capture everything, leaves some components out... dc := GetDC(Self.Parent.Handle); BitBlt(_bitmap.Canvas.Handle, 0, 0, _bitmap.Width, _bitmap.Height, dc, 0, 0, SRCCOPY); _bitmap.SaveToFile('C:\shot.bmp'); End; Procedure TDimPanel.Paint; Begin inherited; Self.Canvas.Draw(0, 0, _bitmap, 128); // Self.Canvas.Ellipse(0, Self.Height - 20, 20, Self.Height); End; But only the upper half of the bitmap is drawn on the panel, bottom half is empty. If ellipse drawing is uncommented, it shows up properly. The funny thing is that if I use Self.Canvas.Draw(0, 0, _bitmap); all is drawn perfectly, but I loose opacity... I guess it will have something to do in how the bitmap is set up...? At the moment I have the following in the constructor: _bitmap := Vcl.Graphics.TBitMap.Create; _bitmap.Transparent := False; _bitmap.PixelFormat := pf32bit; Moving the code out of my project to a TForm and using it's canvas to paint the bitmap to has the same behaviour. Any help is greatly appreciated, these imaging-things are way out of my league. Share this post Link to post
FredS 138 Posted March 31, 2021 7 minutes ago, aehimself said: Any help is greatly appreciated I'm confused, what's the reason for wanting your controls to look Un-Dimmed? ..and if that is the case why not custom draw just your Tabsheet/Panel background dimmed? 1 Share this post Link to post
aehimself 396 Posted March 31, 2021 3 minutes ago, FredS said: I'm confused, what's the reason for wanting your controls to look Un-Dimmed? ..and if that is the case why not custom draw just your Tabsheet/Panel background dimmed? I want to display a progress / warning indicator on a tabsheet which will block all actions to the components on the tabsheet... imagine like the UAC "popup". Everything in the background is dimmed as they are unavailable at the moment, only the important stuff (buttons, labels, etc) are shown with full visibility. Share this post Link to post
aehimself 396 Posted March 31, 2021 One more thing, this is not really going to work when resizing, as the parent of my dimmed control is the tabsheet (which I need the image of). When I take an image of the tabsheet while the dimmed panel is visible, it's image will be seen on the picture, slowly fading out everything in the process. I need to think think through. Share this post Link to post
Attila Kovacs 629 Posted March 31, 2021 2 hours ago, aehimself said: Everything in the background is dimmed as they are unavailable at the moment, only the important stuff (buttons, labels, etc) are shown with full visibility. Could you define the symmetric difference of the sets "Everything" and "important stuff" ? We have no idea what else could be on the page which would make sense to dim but not the "important stuff". Are you putting unimportant stuff onto the forms? 🙂 Enemy deception? 🙂 Share this post Link to post
aehimself 396 Posted April 1, 2021 7 hours ago, Attila Kovacs said: Could you define the symmetric difference of the sets "Everything" and "important stuff" ? We have no idea what else could be on the page which would make sense to dim but not the "important stuff". Are you putting unimportant stuff onto the forms? 🙂 Enemy deception? 🙂 I don't see why it is that important, but let's say you have a connection to a server on each tab. It can be a web page, RDP, or simply some data aware controls. Once the connection is broken, the tabsheed would go dimmed, with a clear message "Reconnecting" (and an abort button) would be shown. This is just one example. Or let's say each tab allows you to manipulate data like... financial records. One tab = one person. There is a button which calculates some averages but since it takes so long, it is being done in a background thread. Until the thread is running no data must be modified as it can cause incorrect results. So, the tab sheet goes dimmed, with a marquee progress bar and a "Please wait" text. Or, you have a chat application, like IRC. One tab = one channel. If you are kicked from a channel, the tab goes dimmed, saying "You have been kicked from the channel" and a button to close the tabsheet. The things dimmed are not important at that stage because they can not be used; let them be a MsTSCAX control, TWebBrowser, TDBEdit or just a TButton - this is "everything". Important stuff means everything that the tab wants you to know at this point, a message maybe with some controls to interact with. Hope this helps to clear the desired outcome 🙂 Share this post Link to post
Attila Kovacs 629 Posted April 1, 2021 @aehimself I see. I'd add a client aligned panel on the top of it, drawing a dimmed/blurred image from the things below and adding some controls to this panel to interact with. You just have to make sure that the underlying controls can't be triggered/selected with pressing tab. Perhaps you need an intermediate container tab on each sheet to be able to disable the whole sheet but not the dimmed/blurred one. 1 Share this post Link to post
Fr0sT.Brutal 900 Posted April 1, 2021 44 minutes ago, Attila Kovacs said: @aehimself I see. I'd add a client aligned panel on the top of it, drawing a dimmed/blurred image from the things below and adding some controls to this panel to interact with. You just have to make sure that the underlying controls can't be triggered/selected with pressing tab. Perhaps you need an intermediate container tab on each sheet to be able to disable the whole sheet but not the dimmed/blurred one. +1 Semi-transparent topmost Panel-dimmer with progress/label/cancel button on it, that's all. Usually it's invisible and appears only when needed. Share this post Link to post
aehimself 396 Posted April 1, 2021 47 minutes ago, Attila Kovacs said: @aehimself I see. I'd add a client aligned panel on the top of it, drawing a dimmed/blurred image from the things below and adding some controls to this panel to interact with. That is exactly what I am trying to achieve now. The problem I'm getting is why the TPanel's Self.Canvas.Draw(0, 0, _bitmap); works while Self.Canvas.Draw(0, 0, _bitmap, 128); draws only the top half of the panel... see 50 minutes ago, Attila Kovacs said: You just have to make sure that the underlying controls can't be triggered/selected with pressing tab. Perhaps you need an intermediate container tab on each sheet to be able to disable the whole sheet but not the dimmed/blurred one. Good point, thank you! Did not think about this! Share this post Link to post
Renate Schaaf 64 Posted April 1, 2021 (edited) 4 hours ago, aehimself said: Self.Canvas.Draw(0, 0, _bitmap, 128); draws only the top half of the panel. Maybe the bottom half gets alphablended away? I noticed that black vanishes, which I don't understand. Everything is fine, when drawing a colored background first. Quote One more thing, this is not really going to work when resizing, as the parent of my dimmed control is the tabsheet (which I need the image of). When I take an image of the tabsheet while the dimmed panel is visible, it's image will be seen on the picture, slowly fading out everything in the process. On resize: You can send the panel to back, redraw its parent, capture the parent's bitmap, send the panel back to front. Surprisingly, there's hardly any flicker when doing this. ------------------------------- unless you use styles Edited April 1, 2021 by Renate Schaaf Share this post Link to post
aehimself 396 Posted April 1, 2021 (edited) 29 minutes ago, Renate Schaaf said: Maybe the bottom half gets alphablended away? I noticed that black vanishes, which I don't understand. Everything is fine, when drawing a colored background first. Progress. If I open the saved bitmap with Irfanview and re-save it, drawing it with opacity works. Size drops from 1,9 to 1,4 MB, bit depth drops from 32 to 24 bit. So yes, something is wrong with the bitmap format 🙂 Edit: damn it. Creating the bitmap like _bitmap := Vcl.Graphics.TBitMap.Create; // _bitmap.Transparent := False; _bitmap.PixelFormat := pf24bit; // _bitmap.AlphaFormat := afDefined; repainting with opacity works like a charm. It does not work with pfDevice or pf32bit. Time to put my components back on it 🙂 Edited April 1, 2021 by aehimself Share this post Link to post
aehimself 396 Posted April 1, 2021 55 minutes ago, Renate Schaaf said: You can send the panel to back, redraw its parent, capture the parent's bitmap, send the panel back to front. Surprisingly, there's hardly any flicker when doing this. It works and surely the flicker is neglectable...unless you have VCL Styles active. Then it flickers like madness 🙂 Guess I'll just have to live with it 😄 For anyone interested, this is how the relevant code looks like: TDimPanel = Class(TPanel) strict private _bitmap: TBitMap; protected Procedure Paint; Override; Procedure Resize; Override; Procedure VisibleChanging; Override; End; Procedure TDimPanel.Paint; Begin Self.Canvas.Draw(0, 0, _bitmap, 80); End; Procedure TDimPanel.Resize; Begin inherited; If Self.Visible Then Self.UpdateBitmap; End; Procedure TDimPanel.UpdateBitmap; Var dc: HWND; Begin If Self.Visible Then Begin Self.SendToBack; Self.Parent.Repaint; End; Try _bitmap.SetSize(Self.Parent.Width, Self.Parent.Height); dc := GetDC(Self.Parent.Handle); BitBlt(_bitmap.Canvas.Handle, 0, 0, _bitmap.Width, _bitmap.Height, dc, 0, 0, SRCCOPY); Finally If Self.Visible Then Self.BringToFront; End; End; Procedure TDimPanel.VisibleChanging; Begin inherited; If Not Self.Visible Then Begin Self.UpdateBitmap; Self.Repaint; End Else _bitmap.SetSize(0, 0); // clear bitmap to free up memory End; ...just make sure you change _bitmap.PixelFormat to pf24bit in the constructor 🙂 1 Share this post Link to post
Attila Kovacs 629 Posted April 1, 2021 (edited) I can't see ReleaseDC. Also, instead of DC you could pass Canvas.Handle. Also, you could tune it with only repainting clipping area's. Btw, for me it works with pf32bit. Edited April 1, 2021 by Attila Kovacs Share this post Link to post
aehimself 396 Posted April 1, 2021 1 minute ago, Attila Kovacs said: I can't see ReleaseDC. Also, instead of DC you could pass Canvas.Handle. Also, you could tune if with only repainting clipping area's. Btw, for me it works with pf32bit. I never really worked with imaging, I had no idea that there is such a thing as ReleaseDC 🙂 I am relying on errors by ReportMemoryLeaksOnShutdown which missed this. btw, if you meant to change it like this: BitBlt(_bitmap.Canvas.Handle, 0, 0, _bitmap.Width, _bitmap.Height, THackWinControl(Self.Parent).Canvas.Handle, 0, 0, SRCCOPY); It does not work, throws an AV. Canvas seems to be nil? Share this post Link to post
Attila Kovacs 629 Posted April 1, 2021 8 minutes ago, aehimself said: I am relying on errors by ReportMemoryLeaksOnShutdown which missed this. Ok, it's not a memory leak. It's GDI leak 😉 9 minutes ago, aehimself said: It does not work, throws an AV. Canvas seems to be nil? You are right, there is not necessarily an initialized canvas. I assumed it's a TPanel already. By the way, for some reason which I can't recall, I'm using a TImage and its TBitmap, not only TBitmap. In order: Form -> TPanel client aligned -> TImage client aligned and blurred. (To "lock" an application) Maybe to give the painting stuff away.... Share this post Link to post
Renate Schaaf 64 Posted April 1, 2021 1 hour ago, aehimself said: ..unless you have VCL Styles active. Then it flickers like madness 🙂 For very simple forms I use a Fake-Style, which improves drawing immensely. One could call RemoveStyle(self) in OnCreate and FakeStyle(self) in OnCreate or whenever the style changes: uses ... VCL.Controls, VCL.ComCtrls, VCL.Forms, VCL.Themes; //and what I forgot type TCrackControl = class(Vcl.Controls.TControl); procedure RemoveStyle(aControl: TControl); var i: integer; begin if (csDesigning in aControl.ComponentState) then exit; // I like to keep the style of the buttons and TabControls if not((aControl is TButton) or (aControl is TCustomTabControl)) then aControl.StyleElements := []; //keep the scrollbars of scrollboxes but get rid of the form's styled caption bar if (aControl is TScrollingWinControl) and (not(aControl is TForm)) then aControl.StyleElements:=[seBorder]; if aControl is TWinControl then with TWinControl(aControl) do for i := 0 to ControlCount - 1 do RemoveStyle(Controls[i]); end; procedure FakeStyle(aControl: TControl); var BGColor, FontColor { , SelectedColor, SelectedFontColor } : TColor; i: integer; begin if (csDesigning in aControl.ComponentState) then exit; BGColor := StyleServices.GetStyleColor(scWindow); FontColor := StyleServices.GetStyleFontColor(sfWindowTextNormal); TCrackControl(aControl).Font.Color := FontColor; TCrackControl(aControl).Color := BGColor; if aControl is TWinControl then with TWinControl(aControl) do for i := 0 to ControlCount - 1 do FakeStyle(Controls[i]); end; Share this post Link to post