Delphi VCL styles problem with TListview EditCaption and HideSelection - delphi

I'm using Delphi 10.3 in a VCL app with a Dark VCL style (default Windows10 Dark for example).
I have two problems with VCL styling in TListview
When editing an item in TListview, the listview's edit is not styled: it has a white background with black text. It there any way to apply the VCL style to that edit although it not a VCL control?
Also, with HideSelection = False and the listview not having focus, the selected item's background color is the default light gray color instead of a much darker color that would better fit in the VCL style. Is there any way to modify that other than custom drawing?
I checked this with several dark VCL styles, they all have these problems with TListView (and TTreeview too).

I was able to fix the listview's edit background and text color by overriding message handler WM_CTLCOLOREDIT in a TListView descendant (TListViewEx in my case) like below. If you check the VCL source for TCustomListView.WMCtlColorEdit you'll see this fix does not cover the case glassPaint case correct - but I'm not using that. It seems to me this is actually a VCL bug and the below code should have been included in TCustomListView.WMCtlColorEdit.
Note: the same type of fix also works for TTreeview (using a TTreeview descendant obviously)
I have not found a workaround for the unfocused selection color yet.
procedure TListViewEx.WMCtlColorEdit(var Message: TMessage);
var
DC: HDC;
begin
if StyleServices.IsSystemStyle then
inherited
else
begin
DC := Message.WParam;
SetTextColor(DC, StyleServices.GetSystemColor(clWindowText));
SetBkColor(DC, StyleServices.GetSystemColor(clWindow));
Message.Result := 1;
end;
end;

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.

TListView doesn't hide selection when using explorer style

In Delphi XE4 if you set HideSelection to true and use an explorer style TListView (when the selection rectangle has a gradient background like Windows Explorer) clicking on another control will not hide the selection rectangle. It will stay there as if nothing has happened - it will not even turn into a gray rectangle like normally when the Listview doesn't have focus.
Is this a Delphi bug or a "feature" of the MS Listview control? Are there any known workarounds or fixes for this? It's really annoying...
This is a feature of the underlying control. The delphi code does nothing with the property beyond passing on the LVS_SHOWSELALWAYS list view style to the underlying control.
Initially I was surprised by your question. I've never seen the behaviour that you describe. Upon closer inspection I realise that is because all my list views are virtual. That is they set OwnerData to True and supply content in response to OnData events. Doing that is the only workaround that I know of.
This "feature" is explained by David, and here is a workaround.
By utilizing the OnExit event to save the selection and set selection to nil, you would mimic the wanted behavior. When the ListView is focused, restore the selection.
To make it react on the mouse, make the ListView focused in the OnMouseEnter event.
Type
TForm1 = class(TForm)
...
private
FSelected: TListItem;
...
end;
procedure TForm1.ListView1Enter(Sender: TObject);
begin
if (ListView1.SelCount = 0) and Assigned(FSelected) then
ListView1.Selected := FSelected;
end;
procedure TForm1.ListView1Exit(Sender: TObject);
begin
FSelected := ListView1.Selected;
if Assigned(FSelected) then ListView1.Selected := Nil;
end;
procedure TForm1.ListView1MouseEnter(Sender: TObject);
begin
ListView1.SetFocus;
end;
Having mentioned this solution, why not go for the simple one, set HideSelection = false, and the selected item will turn gray when unfocused, just like Sertac mentioned in a comment.

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.

Delphi XE2 VCL styles, How to disable VCL styles on TBitBtn?

I am using the new VCL styles system in Delphi XE2 and its work fine but on one Form I want exception. This Form contains number of TBitBtn control and each TBitBtn control has its own Font colour (clRed, clBlue, clLime etc) different from other.
Due to Style implementation all TBitBtn control’s Caption is display in black colour instead of set colour.
Is there any TStyleHook, which can be register on TBitBtn control, which disabled the Style on TBitBtn Control on that form?
The TBitBtn component doesn't use a vcl style hook, this control use the TButtonGlyph class (which is defined and implemented in the implementation part of the Vcl.Buttons unit) to draw the button using the Windows theme or the current vcl style, this class (TButtonGlyph) is not accessible outside of this unit , so you are out of luck here.
The only option which comes to my mind is create a interposer class and intercept the CN_DRAWITEM message for the TBitBtn control and then execute your own code to draw the button.
TBitBtn = class(Vcl.Buttons.TBitBtn)
private
procedure MyDrawItem(const DrawItemStruct: TDrawItemStruct);
public
procedure CNDrawItem(var Message: TWMDrawItem); message CN_DRAWITEM;
end;
procedure TBitBtn.CNDrawItem(var Message: TWMDrawItem);
begin
MyDrawItem(Message.DrawItemStruct^);
end;
procedure TBitBtn.MyDrawItem(const DrawItemStruct: TDrawItemStruct);
begin
//the new code goes here.
end;

Resources