How enable application-wide color schemes? - delphi

I would like to apply color schemes to my application. This is done by making all components have their ParentColor set to true as well as ParentBackground and ParentFont. When I change the form color everything changes. There is an exception: toolbars and toolbuttons don't change. Is it possible to change them with the color of the form or must I implement this in a separate way?
The same applies to font colors, but that is a trifle more strange. When I change the font color of the form the font color of a groupbox caption does not change but the caption of label captions (also inside the groupbox) change allright.
When implementing some way to allow the user choose his own colors is this the way to go (change the form color, make all components have ParentColor set) or are there better ways to achieve this goal?

One way to acheive this is to use interfaces.
It is a bit of (manual) work, but if you'd like to do it in a simple manner you could simply define an interface and ensure all your forms implement this interface.
for example:
type ITheme = interface
procedure SetTheme(const AColor : TColor);
end;
then in each of you forms you could implement this interface.
So to change all of your forms' colors you simply need to call 1 function:
procedure SetGlobalTheme(const AColor : TColor);
var Intf : ITheme;
begin
for i:=0 to screen.Formcount-1 do
begin
if Supports(Screen.Forms[i],ITheme,intf) then
intf.SetTheme(AColor);
end;
end;
Using this method you have full control of each components color, albeit with some more coding to be done. The alternative is to use David suggestion of VCL styles (if your IDE supports it)

Related

Set font color for disabled control with VCLStyles?

When using a darker style in a Delphi application, it is very difficult to see disabled text, which is set to dark gray, and I can no way of setting this to a more usable color. I can set it in the Style Editor, but it is not used when the application is run. Has anyone else come across this behaviour, and been able to work around it?
UPDATE: When I am using a style hook, then the colour is still set to grey, even when I actually set it to a different color.
procedure TEditStyleHookColor.UpdateColors;
var
LStyle: TCustomStyleServices;
begin
LStyle := StyleServices;
if Control.Enabled then
begin
Brush.Color := LStyle.GetStyleColor(scEdit);
FontColor := LStyle.GetStyleFontColor(sfEditBoxTextNormal);
end
else
begin
Brush.Color := LStyle.GetStyleColor(scEditDisabled);
FontColor := clWhite; //TWinControlClass(Control).Font.Color;
end;
end;
UPDATE2
The Style editor also displays the 'wrong' font, as shown in this example.
I suspect that this has nothing to do with VCL styles but only with Windows/Delphi default painting of disabled controls.
Said that you have two choice as stated for example on SwissDelphiCenter:
1) place the control on a panel and disable the panel instead of the
control. This way the color stays to whatever you set it.
2) make a descendent and take over the painting when it is disabled.
I've quickly tried the first and it works great.
You can also make Edit readonly, it's not the same as make it disabled, but it's usually enough and have the advantage of let you select and copy the edit content.

Delphi - Disable [x] Close Button in VCL Styles

I'd like disable [X] close Button with VCL Style in DX Berlin.
Wy this code do not work wiht VCL Style?
EnableMenuItem(GetSystemMenu(Form3.Handle, LongBool(False)),SC_CLOSE, MF_BYCOMMAND or MF_GRAYED);
If you set the action in the FormClose event to caNone, when you try to close the form (clicking on the red cross) nothing is going to happen. In this way you can disable the button.
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
//You cannot type only caNone, otherwise you'll get a compiler error
Action := TCloseAction.caNone;
end;
You can find caNone in System.UITypes; read the documentation for more information.
When using VCL Styles by default style affect the appearance of fonts being used, client area of your form and also your forms border (this also include minimize, maximize and close buttons).
So from what I see you have two options:
You could change StyleElements property of your form to [seFont, seClient] which means that style will only be applied to used fonts and client area of your form but the border area of your form would be unstyled and rendered by OS.
You modify the style at runtime in order to achieve desired effect. Unfortunately I don't have enough experience with Styles to present you with an example of how to achieve this.

Draw an image instead of window caption

I need to do something which seems to be easy, but I'm searching for days with no success.
I have a window of fixed size (say 500*250) and need to replace the whole caption bar with a fixed size JPEG (or better PNG) image (say 500*25).
There are lots of samples talking about Glass, Aero, DWM, blah blah blah. But I just need to draw a fixed image!
I've already tried this, but it doesn't work:
procedure TForm1.Button1Click(Sender: TObject);
var
bmp:TBitmap;
DC:HDC;
begin
DC:=GetWindowDC(form1.Handle);
bmp:=tbitmap.Create;
bmp.SetSize(500, 25);
bmp.Canvas.TextOut(5,5,'Helloooooooooooooooooo');
BitBlt(dc,0,0,500,25,bmp.Canvas.Handle,0,0,SRCCOPY);
bmp.Free;
ReleaseDC(form1.Handle,DC);
end;
It should work both on XP and Vista/7. Please help.
P.S: I have Delphi XE.
You can do so by using VCL Styles.
You can change the appearance of the Windows caption bar like that by using the Delphi integrated Bitmap style designer to change a custom style and then use that Style in your application.
If you don't want to enforce the style to the whole application you can set the StyleElements property of the form to only include seBorder, this means that only the border aka caption of your application will be rendered using your custom style.
If you're working in Delphi XE2 then you won't be able to use the StyleElements property but that is just a minor obstacle, it just means that you will have to resort to using StyleHooks to implement the same behaviour and there is enough documentation on how to do that here.
Sadly, if your Delphi version is older then XE2 then you won't be able to use VCL Styles.
Another but rather unpleasant way would be to create a borderless form by changing the BorderStyle property to bsNone and then implementing your image in a way that it would act as a title bar, processing all actions made on the image and sending appropriate Messages to the application.
You can either:
Intercept the WM_NCPAINT message and custom-draw the caption bar manually.
Remove the caption bar altogether, by using SetWindowRgn() or overriding the CreateParams() method to remove the WS_CAPTION style, and then use the form's OnPaint event, or even a TImage, to display the graphic at the top of the form's remaining client area.
The simplest solution would be to use CreateParams() and TImage.

Font consistency throughout Project?

Is there a quick and effective way of applying a global Font to be used in a project?
By this I mean I would like to set a specific Font name that all controls in my project will use such as TButton, TEdit, TLabel etc.
Typically setting the Font for the Form rather than a specific control will change all the controls on that Form to the Font specified.
There is a slight issue with this however, if you have manually changed a Font on a specific control, then setting the Font by the Form will no longer update those controls that have previously been changed manually.
Idea 1
I was thinking of using a For loop and iterating through each component on my Forms and setting the Font this way, such as:
procedure TForm1.FormCreate(Sender: TObject);
var
i: Integer;
begin
with TForm(Self) do
begin
for i := 0 to ComponentCount - 1 do
begin
if Components[i] is TButton then
begin
TButton(Components[i]).Font.Name := 'MS Sans Serif';
TButton(Components[i]).Font.Size := 8;
TButton(Components[i]).Font.Style := [fsBold];
end;
if Components[i] is TLabel then
begin
TLabel(Components[i]).Font.Name := 'MS Sans Serif';
TLabel(Components[i]).Font.Size := 8;
TLabel(Components[i]).Font.Style := [fsBold];
end;
end;
end;
end;
But doing this seems very messy, it will also be a considerable amount of code for a simple task.
Idea 2
I know I could manually change the fonts at design time one by one for each control, but with several forms to go through this could take time and even then I might of missed a control.
Idea 3
Similar to Idea 2, another way could be to view the Form as Text (DFM) and Find and Replace the font that way.
Basically I am going for consistency within my Application, and having one Font used throughout is what I was looking to achieve.
Am I missing something completely obvious here, is what I am trying to do overkill for such a task?
As discussed in the comments, the key to this is the ParentFont property. This property is defined at various points in the VCL hierarchy. If you set ParentFont to be True for all components in the chain, then you can change the fonts for the entire application simply by modifying
Application.DefaultFont
By default most components set ParentFont to True and so you have nothing to do. The odd one out though is TForm. A brand new default form has ParentFont set to False. This is somewhat disappointing but I suspect reflects the fact that the original designers of the VCL did not anticipate this and that ParentFont was grafted on relatively late in the development of the VCL.
No matter, in an ideal world, all forms in your application should be derived from a common base class that you control. If that is so then you can simply make the change there, set ParentFont to be True, make sure no explicit font settings are applied to any components on you forms, and you are golden. Control the entire application's fonts through a single property. If you don't have a common base class for your forms, here's an ideal time to add it. If you don't want to do that then you need to set ParentFont for each form.
Other related properties are Screen.MessageFont and Screen.MenuFont. These provide global control over the fonts used in message boxes and menus. However, recent versions of Delphi have handed back to Windows control over the painting of message boxes and menus and so these properties have no effect.
The real key, as was mentioned, is to ensure that all your forms descend from your own application base form.
Then, you can view each form and button etc, and review the properties, where any modified font property should be displayed in bold, and is easily identified. Most properties have a "Revert to inherited" menu choice. This should essentially undo any previous selection, without having to go to the text version for editting. (Although it probably does exactly that, deleting any text entry resulting from previous font-setting).
I would definitely want to fix each form once rather than leaving it defined incorrectly and adding more code to fix it at runtime. That change will leave you with a worse problem if you later decide to do something different.
If you want to do this runtime, like you describe it in Idea 1, you should consider making it a recursive function, like this:
procedure SetFontProperties(Control: TControl; Name: TFontName; Size: Integer; Styles: TFontStyles);
// Set font properties
var
Index: Integer;
Font: TFont;
AnObject: TObject;
ChildControl: TControl;
begin
// Set font properties
AnObject := GetObjectProp(Control, 'Font', nil);
if AnObject is TFont then
begin
// Set properties
Font := TFont(AnObject);
Font.Name := Name;
Font.Size := Size;
Font.Style := Styles;
end;
// Set child font properties
if Control is TWinControl then
begin
// Set
for Index := 0 to TWinControl(Control).ControlCount - 1 do
begin
// Child control
ChildControl := TWinControl(Control).Controls[Index];
// Set font properties
SetFontProperties(ChildControl, Name, Size, Styles);
end;
end;
end;
You can then switch fonts for all controls within a form by using it like this:
SetFontProperties(Self, 'Courier', 14, []);
The function will then set the font properties of the form, and the font properties of all child controls on the form, including controls nested within TPanels or other container controls.
However I do agree with you that it's sort of a half messy way of doing it.

Making a TPageControl flat in Delphi 7

I don't know whether this question can be answered here, but I hope it will.
I wrote a simple text editor in Delphi 7 that serves as my primary IDE for writing C code under Windows. I run Windows in a VM and I needed something light.
In any case, it uses a TpageControl that gets a new tab whenever you open or create a new file. Pretty standard.
Now, the TPageControl under Delphi has no flat property.
NO I don't mean setting the tab style to tsButtons or tsFlatButtons
the borders cannot be set to "none" and it looks pretty bad when you add a text editor into the tab control.
Is there any way to make a TpageControl flat?
EDIT:
On an open source PageControl that supports flat here's what I found:
procedure TCustomTabExtControl.WndProc(var Message: TMessage);
begin
if(Message.Msg=TCM_ADJUSTRECT) and (FFlat) then
begin
Inherited WndProc(Message);
Case TAbPosition of
tpTop : begin
PRect(Message.LParam)^.Left:=0;
PRect(Message.LParam)^.Right:=ClientWidth;
PRect(Message.LParam)^.Top:=PRect(Message.LParam)^.Top-4;
PRect(Message.LParam)^.Bottom:=ClientHeight;
end;
tpLeft : begin
PRect(Message.LParam)^.Top:=0;
PRect(Message.LParam)^.Right:=ClientWidth;
PRect(Message.LParam)^.Left:=PRect(Message.LParam)^.Left-4;
PRect(Message.LParam)^.Bottom:=ClientHeight;
end;
tpBottom : begin
PRect(Message.LParam)^.Left:=0;
PRect(Message.LParam)^.Right:=ClientWidth;
PRect(Message.LParam)^.Bottom:=PRect(Message.LParam)^.Bottom-4;
PRect(Message.LParam)^.Top:=0;
end;
tpRight : begin
PRect(Message.LParam)^.Top:=0;
PRect(Message.LParam)^.Left:=0;
PRect(Message.LParam)^.Right:=PRect(Message.LParam)^.Right-4;
PRect(Message.LParam)^.Bottom:=ClientHeight;
end;
end;
end else Inherited WndProc(Message);
end;
The thing is when I tried something similar on the main application it won't work. It won't even compile.
When the tabs are drawn as buttons, no border is drawn around the display area, so set the Style property to tsButtons or tsFlatButtons. (For non-VCL programmers, this is equivalent to including the tcs_Buttons window style on the tab control.)
An alternative is to use a TNotebook. It holds pages, but it doesn't do any painting at all. You'd have to provide the tabs yourself, such as by setting the tab control's height equal to the height of the tabs, or by using a TTabSet. (TTabSet is available in Delphi 2005; I'm not sure about Delphi 7.)
Regarding the code you found, it would be helpful if you indicated why it doesn't compile, or if you gave a link to where you found it, since I suppose the compilation error was because it refers to fields or properties of the custom class rather than the stock one. Here's what you can try to put it in your own code, without having to write a custom control.
Make two new declarations in your form like this:
FOldTabProc: TWndMethod;
procedure TabWndProc(var Msg: TMessage);
In the form's OnCreate event handler, assign that method to the page control's WindowProc property:
FOldTabProc := PageControl1.WindowProc;
PageControl1.WindowProc := TabWndProc;
Now implement that method and handle the tcm_AdjustRect messsage:
procedure TForm1.TabWndProc(var Msg: TMessage);
begin
FOldTabProc(Msg);
if Msg.Msg = tcm_AdjustRect then begin
case PageControl1.TabPosition of
tpTop: begin
PRect(Msg.LParam)^.Left := 0;
PRect(Msg.LParam)^.Right := PageControl1.ClientWidth;
Dec(PRect(Msg.LParam)^.Top, 4);
PRect(Msg.LParam)^.Bottom := PageControl1.ClientHeight;
end;
end;
end;
end;
You can fill in the other three cases if you need them. Tcm_AdjustRect is a message identifier declared in the CommCtrl unit. If you don't have that message in that unit, declare it yourself; its value is 4904.
I suspect this doesn't stop the control from drawing its borders. Rather, it causes the contained TTabSheet to grow a little bigger and cover up the borders.
I'm using Delphi XE8 and the following seems to do the trick:
ATabControl.Tabs.Clear;
ATabControl.Style := TTabStyle.tsFlatButtons;
ATabControl.Brush.Color := clWhite;
You could always use a commercial solution. I would strongly recommend Raize components, which support flat TPageControls with tabs. The component set is very easy to work with, and supports numerous visual enhancements which in my opinion give a better feel to any application.
(source: raize.com)
Drop two TPageControls, one with tabs as Tabs, with a global height equal to the tabs, and one with flatbuttons and Tabvisible properties set to false, which would be aligned under the first one. Then make sure the tab change on the first TPagecontrol makes the tabs also change in the second one.

Resources