change treeview node color - delphi

Is there any way to change the color of the nodes in a TTreeView. I want to color my treeview with a dark color and then I can't see the nodes.
alt text http://rigo.ro/temp/ChangeTreeViewNodeColor.png

It is not easily evident that you only wanted to change the line color.
Anyway, there's a message for that in the API;
uses
commctrl;
procedure TForm1.FormCreate(Sender: TObject);
begin
SendMessage(TreeView1.Handle, TVM_SETLINECOLOR, 0, ColorToRGB(clYellow));
end;

maybe you can use OnCustomDrawItem event of TTreeView:
procedure TForm1.TreeView1CustomDrawItem(Sender: TCustomTreeView;
Node: TTreeNode; State: TCustomDrawState; var DefaultDraw: Boolean);
begin
with Sender as TCustomTreeView do
begin
Canvas.Brush.Color := clBlack;
Canvas.Font.Color := clBlack
end;
end;

Related

Delphi - change the menu bar color

Edit: Scroll to question bottom to see answered working code.
I am trying to change the colour of the menu bar on a Form.
I found this site with some advice:
https://www.experts-exchange.com/questions/20150240/Color-on-the-MainMenu.html
I will paste the code itself below.
Unfortunately, it doesn't quite work as I would like. The shortcomings are:
The colour only applies to the menu items, the remaining space to the right of the last menu item is grey. I have set the Form colour to be the same as the menu, but it doesn't change this.
Some of the entries in each menu drop-down should be disabled, and if I don't apply the colouring code they are correctly shown disabled. Applying the colour changes removes this visual effect, and their colour is the same as all the other entries in the menu drop-down.
My questions are:
Is there a pre-rolled menu object out there that will allow me to easily colour the menu bar, including the empty space to the right, and that preserves properties like showing disabled?
If not, could someone point me in the right direction as to what additional changes I need to make to the code that could fix the problems above?
I am a total newbie to Delphi (and coding, really) but if I can get the names of things to look up then I can Google and take it from there.
I'm using Delphi 10.3.
Code copied from the link above:
type
TForm1 = class(TForm)
.....
procedure FormCreate(Sender: TObject);
public
procedure DrawMenuItem(Sender: TObject; ACanvas: TCanvas;
ARect: TRect; Selected: Boolean);
end;
...
procedure TForm1.DrawMenuItem(Sender: TObject; ACanvas: TCanvas;
ARect: TRect; Selected: Boolean);
var
S: String;
begin
with ACanvas do
begin
S := TMenuItem(Sender).Caption;
if Selected then
Brush.Color := clHighLight
else
Brush.Color := clLime;
FillRect(ARect);
DrawText(ACanvas.Handle, PChar(S), Length(S), ARect, DT_SINGLELINE or DT_VCENTER);
end;
end;
procedure AllOnDrawTo(M: TMenuItem; P: TMenuDrawItemEvent);
var
I: Integer;
begin
M.OnDrawItem := P;
for I := 0 to M.Count-1 do
AllOnDrawTo(M.Items[I], P);
end;
procedure TForm1.FormCreate(Sender: TObject);
var
I: Integer;
begin
for I := 0 to MM.Items.Count -1 do
AllOnDrawTo(MM.Items[I], DrawMenuItem);
end;
UPDATE:
#tom-brunberg gave me the required additions in a comment. Below is the updated code to implement both items I requested. I have kept the original code because I think it is interesting to see the contrast between the two options.
type
TForm1 = class(TForm)
.....
procedure FormCreate(Sender: TObject);
public
procedure AdvancedDrawMenuItem(Sender: TObject; ACanvas: TCanvas;
ARect: TRect; State: TOwnerDrawState);
end;
...
procedure TForm1.AdvancedDrawMenuItem(Sender: TObject; ACanvas: TCanvas;
ARect: TRect; State: TOwnerDrawState);
var
S: String;
begin
with ACanvas do
begin
S := TMenuItem(Sender).Caption;
// Set the highlight colour when the menu item is selected. Grey highlight if disabled.
if odSelected in State then
if odDisabled in State then
Brush.Color := clBtnFace
else
Brush.Color := clGradientActiveCaption
else
Brush.Color := clGradientInactiveCaption;
// Set the colour of the menu item textm, grey if disabled
if odDisabled in State then
Font.Color := clGray
else
Font.Color := clBlack;
// this line fill rest of the top of the form the same colour as the menu. If its the LAST menu item fill rect all way to the right. My example has 8 menu items
if (Parent = nil) and (TMenuItem(Sender).MenuIndex = 8) and not (odSelected in State) then
ARect.Right := Width;
FillRect(ARect);
DrawText(ACanvas.Handle, PChar(S), Length(S), ARect, DT_SINGLELINE or DT_VCENTER);
end;
end;
procedure AdvancedAllOnDrawTo(M: TMenuItem; P: TAdvancedMenuDrawItemEvent);
var
I: Integer;
begin
M.OnAdvancedDrawItem := P;
for I := 0 to M.Count-1 do
AdvancedAllOnDrawTo(M.Items[I], P);
end;
procedure TForm1.FormCreate(Sender: TObject);
var
I: Integer;
begin
for I := 0 to MM.Items.Count -1 do
AdvancedAllOnDrawTo(MM.Items[I], AdvancedDrawMenuItem);
end;
I don't have a full answer for you, but you did say that you can 'google from there'.
Your code applies a custom drawing routine to the menu items only. If you also want to draw the menu bar itself you need to have a custom drawing routine for that. The standard TMenu OwenerDraw allows you to receive events for the menu items. The Menu does have a Window Handle, which means you can paint to it, ideally you want it to stop itself from overpainting any changes you make. Have a look at the source code for the TMenu painting (I haven't had time to do that) and see if you can spot what you need to override to paint it.
TMenu wil be wrapping the generic Windows handling for a menu, so you may be able to find out how Windows allows you to draw the menu and then implement that. (That's a fair amount of googling!)
As for the enabled/disabled feedback You can draw anything you like in the on draw event. If you want to visually display something different when the TMenuItem is disabled, check if it's disabled and then draw what you want.

How to change Font color for disabled TComboBox?

I have a TComboBox with Style:= csOwnerDrawVariable; and I want to show the disabled Font color in black and not in 'gray'.
This is what I get with this source:
procedure TCustomComboBox.WndProc(var Message: TMessage);
begin
case Message.Msg of
CN_CTLCOLORMSGBOX .. CN_CTLCOLORSTATIC, //48434..48440
WM_CTLCOLORMSGBOX .. WM_CTLCOLORSTATIC:
begin
Color:= GetBackgroundColor; // get's the current background state
Brush.Color:= Color;
end;
end;
inherited;
end;
But I want the Font color of the inner Edit control in black.
If I change Font.Color:= clBlack at the WndProc or something else nothing happens.
A Google search give me some tips about changing a TEdit as read only, but this doesn't help me yet.
Update
Here is now my short solution after getting the tip from #Abelisto.
TCustomComboBox = class (TComboBox)
protected
procedure DrawItem(Index: Integer; Rect: TRect; State: TOwnerDrawState); override;
end;
procedure TCustomComboBox.DrawItem(Index: Integer; Rect: TRect; State: TOwnerDrawState);
begin
if odComboBoxEdit in State then begin // If we are drawing item in the edit part of the Combo
if not Enabled then
Canvas.Font.Color:= clBlack; // Disabled font colors
Canvas.Brush.Color:= GetBackgroundColor; // Get the right background color: normal, mandatory or disabled
end;
inherited DrawItem(Index, Rect, State);
end;
Use OnDrawItem event.
There is no special settings for the components at design time - all performed in code. Just put on the form ComboBox1 and Button1 and assign the events to them.
procedure TForm3.Button1Click(Sender: TObject);
begin
ComboBox1.Enabled := not ComboBox1.Enabled; // Change Enabled state
end;
procedure TForm3.ComboBox1DrawItem(Control: TWinControl; Index: Integer;
Rect: TRect; State: TOwnerDrawState);
var
txt: string;
begin
if Index > -1 then
txt := ComboBox1.Items[Index]
else
txt := '';
if odComboBoxEdit in State then // If we are drawing item in the edit part of the Combo
if ComboBox1.Enabled then
begin // Enabled colors
ComboBox1.Canvas.Font.Color := clRed; // Foreground
ComboBox1.Canvas.Brush.Color := clWindow; // Background
end
else
begin // Disabled colors
ComboBox1.Canvas.Font.Color := clYellow;
ComboBox1.Canvas.Brush.Color := clGray;
end;
ComboBox1.Canvas.TextRect(Rect, Rect.Left, Rect.Top, txt); // Draw item. It may be more complex
end;
procedure TForm3.FormCreate(Sender: TObject);
begin
with ComboBox1 do // Setup combo props
begin
Items.Add('111');
Items.Add('222');
Items.Add('333');
ItemIndex := 1;
Style := csOwnerDrawVariable;
end;
end;

Changing background for TDBGrid row?

I want to simulate Explorer themes for TDBGrid selected row (dgRowSelect), instead of that Blueish color. How can I do that?
Here is a sample of the expected result:
When you say "simulate", I'm not clear how you're intending to choose the selected row background color, but the following should draw it in a standard TDBGrid.
procedure TForm1.FormCreate(Sender: TObject);
begin
DBGrid1.DefaultDrawing := False;
DBGrid1.Options := DBGrid1.Options + [dgRowSelect];
end;
procedure TForm1.DBGrid1DrawDataCell(Sender: TObject; const Rect: TRect;
Field: TField; State: TGridDrawState);
var
Grid : TDBGrid;
BackColor : TColor;
begin
Grid := Sender as TDBGrid;
if gdSelected in State then begin
BackColor := clYellow; // or whatever
Grid.Canvas.Brush.Color := BackColor;
Grid.Canvas.Font.Color := Grid.Font.Color;
end;
Grid.Canvas.FillRect(Rect);
Grid.Canvas.TextOut(Rect.Left, Rect.Top, Field.DisplayText);
end;
You can use the OnDrawColumnCell event
Here's a simple example:
procedure TForm4.DBGrid1DrawColumnCell(Sender: TObject; const Rect: TRect;
DataCol: Integer; Column: TColumn; State: TGridDrawState);
begin
if mydataSet.FieldByName('Age').AsInteger > 18 then
DBGrid1.Canvas.Brush.Color:= clRed;
DBGrid1.DefaultDrawColumnCell(Rect, DataCol, Column, State);
end;
Hope this helps
Change the "SelectedBackColor" of the TDBGrid to the color you want.

How do I draw the selected list-box item in a different color?

Is is possible to change the item selection focus color and text color in a TListBox?
When themes are not enabled in the project, or the list box style is set to owner-draw, the selection around the item is painted blue, which I believe is globally defined by the system's appearance settings.
I would like to change the color of selected items to a custom color.
So an example, the result would be something like this:
Note the last listbox has been modified in Paint to illustrate the example.
try this:
procedure TForm1.ListBox1DrawItem(Control: TWinControl; Index: Integer;
Rect: TRect; State: TOwnerDrawState);
begin
with (Control as TListBox).Canvas do
begin
if odSelected in State then
Brush.Color := $00FFD2A6;
FillRect(Rect);
TextOut(Rect.Left, Rect.Top, (Control as TListBox).Items[Index]);
if odFocused In State then begin
Brush.Color := ListBox1.Color;
DrawFocusRect(Rect);
end;
end;
end;
I saw, Style property has to be lbOwnerDrawFixed
This helped me do something I needed to do also, namely, eliminate any visible selection. I modified the code above very slightly to accomplish this:
procedure TForm1.OnDrawItem(Control: TWinControl; Index: Integer;
Rect: TRect; State: TOwnerDrawState);
begin
with (Control as TListBox).Canvas do
begin
if odSelected in State then
begin
Brush.Color := clWhite;
Font.Color := clBlack;
end;
FillRect(Rect);
TextOut(Rect.Left, Rect.Top, (Control as TListBox).Items[Index]);
if odFocused In State then begin
Brush.Color := ListBox1.Color;
DrawFocusRect(Rect);
end;
end;
end;
Made the selected item's background color white, and it's font color black, which did what I needed. Thanks so much!

add 2-line caption in a TListView?

in a label i can add a new line like this
Label.Caption:='First line'+#13#10+'SecondLine';
can this be done in a TListView?
listItem:=listView.Items.Add;
listItem.Caption:='First line'+#13#10+'SecondLine';
thanks
It is possible to have multiline strings in a standard TListView in vsReport style, but AFAIK it doesn't support varying row heights. However, if you have all rows with the same number of lines > 1 you can achieve that quite easily.
You need to set the list view to OwnerDraw mode, first so you can actually draw the multiline captions, and second to get a chance to increase the row height to the necessary value. This is done by handling the WM_MEASUREITEM message, which is sent only for owner-drawn list views.
A small example to demonstrate this:
type
TForm1 = class(TForm)
ListView1: TListView;
procedure FormCreate(Sender: TObject);
procedure ListView1DrawItem(Sender: TCustomListView; Item: TListItem;
Rect: TRect; State: TOwnerDrawState);
private
procedure WMMeasureItem(var AMsg: TWMMeasureItem); message WM_MEASUREITEM;
end;
{ TForm1 }
procedure TForm1.FormCreate(Sender: TObject);
begin
ListView1.ViewStyle := vsReport;
ListView1.OwnerDraw := True;
ListView1.OwnerData := True;
ListView1.Items.Count := 1000;
with ListView1.Columns.Add do begin
Caption := 'Multiline string test';
Width := 400;
end;
ListView1.OnDrawItem := ListView1DrawItem;
end;
procedure TForm1.ListView1DrawItem(Sender: TCustomListView;
Item: TListItem; Rect: TRect; State: TOwnerDrawState);
begin
if odSelected in State then begin
Sender.Canvas.Brush.Color := clHighlight;
Sender.Canvas.Font.Color := clHighlightText;
end;
Sender.Canvas.FillRect(Rect);
InflateRect(Rect, -2, -2);
DrawText(Sender.Canvas.Handle,
PChar(Format('Multiline string for'#13#10'Item %d', [Item.Index])),
-1, Rect, DT_LEFT);
end;
procedure TForm1.WMMeasureItem(var AMsg: TWMMeasureItem);
begin
inherited;
if AMsg.IDCtl = ListView1.Handle then
AMsg.MeasureItemStruct^.itemHeight := 4 + 2 * ListView1.Canvas.TextHeight('Wg');
end;
I know this is an old thread, and I can't take credit for figuring this out, but to adjust the row height in a TListView you can add an image list for the StateImages and then specify the image height by expanding the StateImages item in the properties window. You don't need to load any actual images.
Sorry I can't credit the actual person who figured it out - it was on a forum I visited a while back.
I don't seem to be able to achieve this using the TListView. But using the TMS TAdvListView, you can use HTML in the item text so this will put the caption onto 2 lines:
with AdvListView1.Items.Add do
begin
Caption := '<FONT color="clBlue">Line 1<BR>Line 2</font>';
end;

Resources