TStyleManager.SetStyle leaves visual components on other TPageControls not visible - delphi

A MainMenu option in my app calls:
TStyleManager.SetStyle('Ruby Graphite');
It properly changes the style in my XE2 (Update 3) application almost everywhere. Here's the problem:
I have a TPageControl with 3 TabSheets, each containing a TFrame. The above call properly changes the style of the currently visible TabSheet's components to 'Ruby Graphite'.
However, when I click to switch to another tabsheet, all of its components are invisible until I move my mouse over them. When I return to the original TabSheet, its components too are now not visible until I hover over them.
I tried explicitly calling .Refresh and .Repaint on the frames themselves, but that didn't make the components on them visible.
What's weird is that executing:
TStyleManager.SetStyle('Windows');
returns the style to the standard, with all components visible.
The TTabSheet is on a panel which is on a panel, if that matters. And, I'm using some 3rd party components (not in these frames) that are threaded. I believe that I have disabled them though.
Any suggestions on how to force the visual components on a formerly hidden tab to become visible after changing the style from Windows?
EDIT: Link to short screen video that shows the refresh problem: http://tinyurl.com/sostyle.
I have also on occasion found that the software doesn't respond when clicking on the icons (like maximize) in the upper right corner. There's just something weird going on that is over my head. So far, I've been unable boil this down to a small example from the 40,000 lines of code in the application.

Here's a solution and a direction to try that worked for me on Delphi XE4.
I have found that I get the same issues when I minimize a form that is set to use the TStyleManager and sets styles dynamically during the execution of the application.
Another problem besides the redraw problem that I noticed is one that involves the form style not being set to a sizeable when the form is restored from being minimized to the taskbar or a trayicon. The sizeable form border problem and the redraw problem seem to be linked.
I overcame the redraw problem and the border resizing problem by adding an event handler for the OnClick event of the TrayIcon component and calling RecreateWnd for the form that uses the TStyleManager.
Currently it looks like this, with the jtiApp component being a TJvTrayIcon component:
procedure TMainForm.jtiAppClick(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
if not Visible then
begin
WindowState := wsNormal;
RecreateWnd;
Show;
end;
end;
That seems to take care of the redraw problem and the border settings for the recreated form. I'm not sure if that's a JVCL issue or with the TStyleManager but that fix worked when it was an issue dealing with a restore from the tray via a TrayIcon component.
I hope that helps with your issue and others who view this solution.

Related

Is there any way to add a hint or tooltip with FireMonkey?

I've earched for it but couldn't find anything. Is there any way to add a hint or tooltip with FireMonkey? Are there any components available that enable this?
Ideally I am looking for something like this (a callout type tooltip):
To the moderators who have placed this question on hold: I am looking for lines of source code on how to achieve this, not a software to buy/use. There are currently (AFAIK) no source code components that enable doing this, so there is no risk of "opinionated anwers or spam".
This is how I finally did it: to create a hint for a Button that looks like this:
Add a button to a form. Then add a TPopup. Drop a CalloutPanel inside it and optionally set the align to AlClient. The drop a TLabel on that CalloutPanel and write your hint text.
Your structure should look like this:
Then go to the TPopup and set PlacementTarget to Button1 (your button). Next, go to Placement and select BottomCenter:
Next add a handler for the MouseEnter and MouseLeave events on the button:
procedure TForm1.Button1MouseEnter(Sender: TObject);
begin
Popup1.IsOpen := True;
end;
procedure TForm1.Button1MouseLeave(Sender: TObject);
begin
Popup1.IsOpen := False;
end;
That should do it.
You can use FloatAnimation and Opacity property to make a hint.
Add a button to a form. Then add CalloutPanel (or any shape) inside.
Next drop a TLabel on the CalloutPanel to write your hint text. I set CalloutPanel.Visible property to False at the Form Creating moment. Then attach a TFloatAnimation to a CalloutPanel.Opacity property.
Next set some TFloatAnimation properties:
Because of Duration Hint appears smoothly.
Then create Button events OnMouseEnter and OnMouseLeave .
procedure TForm1.Button1MouseEnter(Sender: TObject);
begin
CalloutPanel1.Visible := true;
end;
procedure TForm1.Button1MouseLeave(Sender: TObject);
begin
CalloutPanel1.Visible := false;
end;
That's it
I am new to Delphi and FireMonkey. And I also wanted tool tips. And here is what I figured out: FireMonkey has no provision for tool tips, and this is deliberate and for good reason.
I think the big idea with FireMonkey is that you develop one and only one program. Then, without changing even one line of code, you compile a version to run on Windows, another version to run on Android, another version to run on Mac OS, etc. So without changing even one line of code, you have a version for Desktop and a version for Smartphones that work exactly the same way with the same user interface.
Therefore, FireMonkey is only going to support features that are common to both smartphones and desktops. On smartphones, there is no such thing as hovering a mouse, a finger, or anything else. Therefore, Firemonkey does not support hovering on desktops. Because there is no hovering, there can be no tooltips ('hints' in Delphi nomenclature).
So you have to decide: Do you want an app that works exactly the same in Windows and on smartphones, without changing the code and without having separate code? Or do you want all the desktop features? If you want all the desktop features, including tooltips (hints) and all the rest, then you should be using Embarcadero's VCL (Visual Component Library). Using VCL will allow you to get tooltips (hints) by just setting the 'hint' property of text boxes, buttons, and all the other controls--and without writing code.
But if you want an app that works on smartphones, you will have to use FireMonkey. VCL won't work for that.
As I mentioned, I'm still new to Delphi. So, of course, I appreciate corrections
from experienced Delphi developers.

Delphi hiding a form: Is there a difference between Form.Hide and Form.Visible:=False?

I'm looking through two copies of code and in one I have myForm.Hide and another I have myForm.Visible := False. I can't remember why I changed this, if one was a bug fix or whether there is any difference at all.
There is no difference for Hide. The VCL code is:
procedure TCustomForm.Hide;
begin
Visible := False;
end;
But Show is a bit different:
procedure TCustomForm.Show;
begin
Visible := True;
BringToFront;
end;
Depends on how old your Delphi code is and how far back it goes. Form.Hide at one time (Win95/2000) would hide the form AND its taskbar icon - the other would not. Of course, there was some patches, etc to fix issues with Delphi and certain video cards/color palettes would require you to consider how you wanted to hide forms. (man I am showing my age). I've seen code that would set the form Left to a big negative number just to hide the form off the screen cause of issues with hardware (Delphi 1-3 was really hardware sensitive)
Also, around Delphi 3/4 there was a memory leak using minimize instead of hide in MDI applications (so we used PAgecontrol with form docking over MDI Forms). So, if you are looking at very old code, then those things matter. If you are compiling on Delphi 6 or better, then there is really no difference.

Delphi buttons show white border on Aero glass

I have been trying to find a good-looking design using Aero in Delphi 2010. One of the obvious uses one sees, is where the glass frame is extended to include the OK/Cancel buttons at the bottom of the screen. I notice though that this doesn't look quite right in Delphi 2010 - there is a white border all around each button.
This image shows the problem: the top 3 buttons are from my app, the bottom two were taken from Paint.NET's Layer Properties dialog.
I tried various combinations of DoubleBuffered and a few combinations of placing the controls on other controls first, but the problem remains. Any ideas?
If no one has a clean solution, as a workaround use TBitBtn with DoubleBuffered = false.
It appears that the only workaround is owner-draw, or a third-party button control Check out the Glass Button by Roy Klever or, as stated in the QC entry linked below, TBitBtn with DoubleBuffered=false, which was the accepted answer above to this question.
This is a bug in Windows Aero DWM or else a bug in the windows common controls, or a bug in the way the VCL class hierachy handles common control window messages and painting when painting on glass. In short, windows common controls do not paint themselves properly on glass, or rather DWM composition (Aero) is broken. Surprise surprise.
The standard VCL button component uses the Window Class BUTTON from Windows Common Controls.
Note that TSpeedButton does not use the windows common control, and does not have this problem. however, it also does not accept focus.
It appears Embarcadero knows about this issue, it is QC # 75246, which is closed because it is really a bug in the common controls library, as Won't Fix, with the suggestion to use TBitBtn. Buttons are not alone, this is part of a group of QC reports including panels, and other common controls.
However I have a commercial TcxButton (part of developer express components) which accepts keyboard focus, and does not draw this glitch. Any code that uses the Win32 common control button control appears to have this problem. It may be possible that a low level Win32 API hacker might find a workaround for this. I am looking into it. This answer will be updated if I figure it out.
One interesting detail: TcxButton has three drawing styles, cxButton.LookAndFeel.Kind = {lfOffice11,lfFlat,lfStandard}. Selecting lfOffice11 adds this glitch back in. It looks like a strange interaction between the glass feature in aero in Vista/Win7 and the common control/xptheme button drawing code.
It may be that the only workaround is to use a completely app-drawn button control and to not use Windows common controls buttons, or any button control that relies upon the XP theme engine to draw buttons, on an aero glass pane.
Edit: July 28, someone at Embarcadero has closed the above QC Entry, which was a mistake. I am urging them to reopen it, if only to clarify if this is indeed a Windows bug in the common controls dll.
If you wish to play around, make a copy of the VCL source code for the TButton and TCustomButton classes from StdCtrls, as I have done here, modify CNCtlColorBtn, so that you force one of three things to happen - PerformEraseBackground, DrawParentBackground or inherited, and see the results. Interesting stuff.
procedure TCustomGlassButton.CNCtlColorBtn(var Message: TWMCtlColorBtn);
begin
PerformEraseBackground(Self, Message.ChildDC);
Message.Result := GetStockObject(NULL_BRUSH);
(*
with ThemeServices do
if ThemesEnabled then
begin
if (Parent <> nil) and Parent.DoubleBuffered then
PerformEraseBackground(Self, Message.ChildDC)
else
DrawParentBackground(Handle, Message.ChildDC, nil, False);
{ Return an empty brush to prevent Windows from overpainting we just have created. }
Message.Result := GetStockObject(NULL_BRUSH);
end
else
inherited;
*)
end;
Some interesting reading on Vista era glass/DWM/aero APIs (C++ developers blog)
Here I'm providing some code that makes TButton look right on Glass. Unfortunately it makes the form "click-throw", so I don't think it's a good idea. But maybe you can find a way to fix the form's "click-throw".
if you are able to use win32 api, try exploiting NM_CUSTOMDRAW notification (not ownerdraw), as i do (yes, buttons DO send it, including radio and checkboxes. For these it is best to use WM_CTLCOLORSTATIC though.). This is how it is done in C++, but the idea is the same. While my idea is good, it so happens that my buttons do DISAPPEAR once per program execution from the window, when they are customdrawn and i need to hover mouse over them so they are visible again. That's why i'm still looking for comments for this. Note that it is really difficult to reproduce disappearing buttons in one-form applications. I hovewer am experiencing this behaviour in every project.
case WM_NOTIFY:
switch(((LPNMHDR)lParam)->code){
case NM_CUSTOMDRAW:
{
NMHDR *nmh=(NMHDR*)lParam;
//these 6000 through 6004 are button identifiers assigned by me
if(nmh->idFrom >= 6000 && nmh->idFrom <= 6004){
switch(((LPNMCUSTOMDRAW)nmh)->dwDrawStage){
case CDDS_PREERASE:
//BackgroundBrush is a HBRUSH used also as window background
FillRect(((LPNMCUSTOMDRAW)nmh)->hdc, &((LPNMCUSTOMDRAW)nmh)->rc, BackgroundBrush);
break;
}
}
break;
}
break;

Why is the caret suddenly centered in an edit field?

I have a "username" TEdit on a Delphi 2006 login form. When the application starts up the user is asked to enter the username. The TEdit gets focus and the caret is placed in its horizontal center for some reason. As soon as anything is typed the caret is left aligned again and everything looks normal.
It is also strange that it wasn't always like this. This behaviour suddenly started a few years ago (I believe we still used Delphi 6 at that time). Any idea what might be causing this?
Additional info (has been asked for):
The problem is widespread: D2006 and D6 (I believe), 5 or 6 Delphi instances on as much computers, all applications using that login form. The effect is limited to the form however, it does not occur on other TEdits.
The TEdit is not filled with spaces (that would be strange to do in the first place).
More info (Nov 13):
The caret is not centered exactly, it is almost centered.
Currently it seems to occur in a DLL only. The same login dialog is used in regular executables and does not show the problem there (although I believe it did at some time).
The edit field is a password edit, the OnChange handler sets an integer field of that form only, there are no other event handlers on that edit field.
I added another plain TEdit, which is also the ActiveControl so that it has focus when the form shows (as it was with the password edit). I also removed the default text "Edit1". Now the issue is present in that TEdit in the same way.
The "centered" caret goes back to normal if either a character is entered or if I tab through the controls - when I come back to the TEdit it looks normal. This was the same with the password edit.
I had also the same problem in Delphi 2007,
with a TEdit placed in a modal form called by double-clicking in a Grid.
I made some tests launching the same Form from a TSpeedButton.
I noticed that the problem with the TEdit appears only when the grid is focused.
after more tests the problem appears to be a bug in the VCL.
in TCustomGrid.paint there is a Call of SetCaretPos, even if the grid is not on an active Form.
../..
Focused := IsActiveControl;
if Focused and (CurRow = Row) and (CurCol = Col) then
begin
SetCaretPos(Where.Left, Where.Top);
Include(DrawState, gdFocused);
end;
../..
the code above is from TCustomGrid.paint in Grids.pas
in this code, Focused is set to true if the grid is the "activeControl" of the parent form, the code don't take into account if the form is active or not.
then, if the grid need to be repaint, setCaretPos is called with grid coordinates, causing the bug mentioned in the question.
The bug is very difficult to notice because, most of the times, the caret simply disappear from the active form instead of blinking near the middle of a TEdit.
steps to reproduce the bug :
start new VCL form app.
add TStringGrid into it.
add a second form to the app with just a TEdit in it.
return in main form (unit1) and call form2.showmodal from the grid DblClick event.
that's all : you can launch the application and double click on a grid cell.
if you drag the modal form away of the main form, the grid will need to be repaint, then causing the caret to disappear from the modal form (or to appear in the middle of the TEdit if you are very lucky)
So, I think a fix is needed in Grids.pas.
in the excerpt of grid.pas above, I suggest replacing the call of the function IsActiveControl by a call of a new function called IsFocusedControl :
// new function introduced to fix a bug
// this function is a duplicate of the function IsActiveControl
// with a minor modification (see comment)
function TCustomGrid.IsFocusedControl: Boolean;
var
H: Hwnd;
ParentForm: TCustomForm;
begin
Result := False;
ParentForm := GetParentForm(Self);
if Assigned(ParentForm) then
begin
if (ParentForm.ActiveControl = Self) then
//Result := True; // removed by DamienD
Result := ParentForm.Active; // added by DamienD
end
else
begin
H := GetFocus;
while IsWindow(H) and (Result = False) do
begin
if H = WindowHandle then
Result := True
else
H := GetParent(H);
end;
end;
end;
this fix (made in Delphi2007) worked well for me, but is not garanteed.
(also, do not modify directly units of the VCL).
Just a few additional questions:
Is this problem on one pc or on more pc's?
Does it occur on one application or on all applications?
Does it happen only on your Delphi applications or on all applications?
If it is only on one pc, I think it is a strange registry setting. If its on more pc's but you only have one delphi development pc, it could still be a registry setting. But there are other possibilities.
You could try some tests:
Create an simple app on the dev pc and run it on another. Does this show the effect.
Use an app that is created by Delphi but build on another pc that does not show the effect, and run it on the dev pc, does this show the effect?
I really think this is a registry setting. According to the information you gave me, it happened since Delphi 6 and is still happening.
It also can be a locale setting but then it has to happen in more programs.
Edit:
Thanks for the extra info.
So it looks like the problem can be isolated to a single form. But it occurs on all pc's.
What you can do, is delete the edit, and re-add a new one. This saves searching for weird property values.
Are there events hooked on the TEdit that can possible explain the effects?
What property values are set? (But I prefer a look at the dfm and the code, because then I'm possible able to reproduce the effect.)
Are you sure it is a simple TEdit? It might be initialized with a few spaces instead of an empty string. The onChange handler then might just strip spaces as soon as you start typing.
A TEdit extension might have text alignment set on centered instead of left, and set text alignment only on onChange.
[edit]
Please show the event handlers of the TEdit.
I've also noticed this behaviour in richedits.
One place in our app is a double click on a grid which displays another screen containing a RichEdit. The caret always seems to appear in the Richedit in the same place as the double click on the grid occured, ie if the dblclick was on the 3rd line of the grid, the caret will appear ~3 lines down on the edit. As soon as a key is pressed, the caret resets to the correct position of top left.
Its not limited to a certain form or pc as it happens on all developers machines and also on clients machines.
The app was originally developed in Delphi 5, but the problem didn't occur (or wasn't noticed) until we moved to D2006.
Its not a particularily big problem, just... irritating.
Another workaround:
Before showing the second form, prevent the grid on the first form doing Paint action. Code snippet as below.
Gird.BeginUpdate;
try
//Show the second form here
finally
Grid.EndUpdate;
end;

Multiple form Delphi applications and dialogs

I have a Delphi 7 application that has two views of a document (e.g. a WYSIWYG HTML edit might have a WYSIWYG view and a source view - not my real application). They can be opened in separate windows, or docked into tabs in the main window.
If I open a modal dialog from one of the separate forms, the main form is brought to the front, and is shown as the selected window in the windows taskbar. Say the main form is the WYSIWYG view, and the source view is poped out. You go to a particular point in the source view and insert an image tag. A dialog appears to allow you to select and enter the properties you want for the image. If the WYSIWYG view and the source view overlap, the WYSIWYG view will be brought to the front and the source view is hidden. Once the dialog is dismissed, the source view comes back into sight.
I've tried setting the owner and the ParentWindow properties to the form it is related to:
dialog := TDialogForm.Create( parentForm );
dialog.ParentWindow := parentForm.Handle;
How can I fix this problem? What else should I be trying?
Given that people seem to be stumbling on my example, perhaps I can try with a better example: a text editor that allows you to have more than one file open at the same time. The files you have open are either in tabs (like in the Delphi IDE) or in its own window. Suppose the user brings up the spell check dialog or the find dialog. What happens, is that if the file is being editing in its own window, that window is sent to below the main form in the z-order when the modal dialog is shown; once the dialog is closed, it is returned to its original z-order.
Note: If you are using Delphi 7 and looking for a solution to this problem, see my answer lower down on the page to see what I ended up doing.
I'd use this code... (Basically what Lars said)
dialog := TDialogForm.Create( parentForm );
dialog.PopupParent := parentForm;
dialog.PopupMode := pmExplicit;
dialog.ShowModal();
I ultimately ended up finding the answer using Google Groups. In a nutshell, all the modal dialogs need to have the following added to them:
procedure TDialogForm.CreateParams(var Params: TCreateParams);
begin
inherited;
Params.Style := Params.Style or WS_POPUP;
Params.WndParent := (Owner as TWinControl).Handle;
end;
I'm guessing this does the equivalent of Lars' and Marius' answers in Delphi 7.
Is the dialog shown using ShowModal or just Show? You should probably set the PopupMode property correct of the your dialog. pmAuto would probably your best choice. Also see if you need to set the PopupParent property.
First of all, I am not completely sure I follow, you might need to provide some additional details to help us understand what is happening and what the problem is. I guess I am not sure I understand exactly what you're trying to accomplish and what the problem is.
Second, you shouldn't need to set the dialog's parent since that is essentially what is happening with the call to Create (passing the parent). The dialogs you're describing sound like they could use some "re-thinking" a bit to be honest. Is this dialog to enter the properties of the image a child of the source window, or the WYSIWYG window?
I'm not sure I quite understand what you are getting at, but here's a few things I can suggest you can try...
This behaviour changes between different versions of Delphi. I'd suggest that this is due to the hoops they jumped through to support Windows Vista in Delphi 2007.
If you are using Delphi 2007, try removing the line from the project source file that sets the Application.MainFormOnTaskBar boolean variable.
With this removed, you should be able to use the various Form's BringToFront / SendToBack methods to achieve the Z-ordering that you are after.
I suspect that what you've discovered has been discussed on this link
Of course, I may have just missed your point entirely, so apologies in advance!

Resources