How Do I Add A TLabel To The Menu Bar in Delphi? - delphi

I use Beyond Compare (a great program), and was very impressed when it displayed a "New Version Available" label on its Menu Bar. I clicked on it, up popped an install new version box, it installed, the program restarted itself and there was the new version and no more label.
I thought that was a great feature. The label is there prominently on the menu bar where the user can't miss it. I've already got the update procedure, so all I had to do was add the label. That should be easy.
Here's the label where I want it:
(source: beholdgenealogy.com)
... Wrong. I couldn't figure out how to add a label there. The menu bar and the control area above it appear to be hands-off area for visual components. I couldn't place one there.
But I know it can be done, because Beyond Compare is a Delphi program.
Can anyone tell me what I have to do to put a TLabel in my Menu Bar or at least make it appear to be over the Menu Bar in the correct position?
For reference, I use Delphi 2009.
Conclusion: Christopher seems to have correctly figured out what the Beyond Compare people did. I've decided to implement the menu item, but without the customization of his "owner draw" solution. So I don't get the blue bold underline hyperlink look, but I also don't lose all the automatic things (like the Vista styling) that owner draw skips.
To space the menu item over to the right, I've added an item after the "Help" that has the caption " " and is disabled.
Thanks, Christopher. I was stuck thinking it must be a Label, but you saw around that.

Are you sure it's a label?
I haven't used the program, but it could just be a menu item, set to 'owner draw' and painted to look like a link?
http://sirmonkeys.com/images/updatelink.png
(done in Delphi 7)
procedure TForm1.MYITem1DrawItem(Sender: TObject; ACanvas: TCanvas;
ARect: TRect; Selected: Boolean);
begin
acanvas.Font.Style := [fsUnderline,fsbold];
acanvas.Font.color := clblue;
acanvas.Brush.Style := bsClear;
acanvas.TextOut(arect.left+1,arect.top+1,'Link to Update...');
end;
procedure TForm1.MYITem1MeasureItem(Sender: TObject; ACanvas: TCanvas;
var Width, Height: Integer);
begin
width := 100;
end;
and then either a have an ImageList assigned to MainMenu1.Images
or set MainMenu1.OwnerDraw to true.

Beyond Compare's implementation is actually a TLabel. We use Toolbar 2000 for our menus and toolbars, so embedding a control on the menu directly is supported (with a correct background), and it has the advantage that it supports right-justified menu items.

Related

Drag/Drop to arrange vertically within ScrollBox

I was asked to make some changes to a project another developer did 10+ years ago in Delphi 7. This is a proprietary bit of code, so I'll be extremely specific.
The "Container" is a TScrollBox and the panels inside are TSpkRollPanel's -- a collapsible or expandable TPanel derivative.
I hope the image below explains everything. It's really simple. I'm supposed to make the TSpkRollPanel elements drag/drop vertically ONLY so they can be arranged in the desired order. I've spent a few hours getting up to speed on Delphi (Which I haven't seen in 10+ years)
If anyone could point me in the right direction, I'd appreciate it. I'm Delphi literate, just rusty.
With standard TPanel panels the following works fine, and most probably with your panels as well. The steps are the following:
Select all panels
Set Align property of all panels to AlTop
Set DragMode property of all panels to dmAutomatic
Switch to event view in Object Inspector
Double click in entry field of OnDragDrop to create event handler
Double click in entry field of OnDragOver to create event handler
If the names of the two created event handlers include identifier for a specific panel, you may want to rename the event handlers to reflect that they are common for all panels.
6 Finally, add code to event handlers
procedure TForm1.PanelDragDrop(Sender, Source: TObject; X, Y: Integer);
begin
TPanel(Source).Top := TPanel(Sender).Top - 5;
end;
procedure TForm1.PanelDragOver(Sender, Source: TObject; X, Y: Integer;
State: TDragState; var Accept: Boolean);
begin
Accept := True;
end;
The solution works so that when a panel (A) is dropped on another (B) it (A) will take that panels (B) place in the alignment order, pushing (B) and the other panels down.

Vcl Style Utils - Get rid of default form icon

I've already posted this as an issue on RRUZ's Vcl Style Utils library on GitHub. However, I thought I could get some help here too.
I'm using VCL Styles to create a Windows 10 user interface, specifically using the "Windows 10 Dark" style. I'm also using the VCL Style Utils to be able to add buttons to the non-client area in the title bar. I'm attempting to completely disregard the form icon and its default functionality in favor of a back button, just like most new Windows 10 apps do.
I'm trying to place a button in the far upper-left corner of the form, using the TNCControls component in Vcl.Styles.NC. However, when I place a button over the form's icon, the button cannot be clicked in the area of the icon. Although I'm able to overlap the icon, clicking in that particular area of the title bar always opens the form's system menu, instead of clicking the button I've placed there.
I do not wish for this menu to pop up when clicking there:
How I'm currently creating this button:
procedure TfrmTestMain.SetupTitleBar;
var
B: TNCButton;
begin
FNCControls:= TNCControls.Create(Self);
B:= FNCControls.ButtonsList.Add;
B.Style := TNCButton.TNCButtonStyle.nsTranparent;
B.BoundsRect := Rect(0, 0, 45, 32);
B.UseFontAwesome:= True;
B.Caption := '';
B.ImageAlignment:= TImageAlignment.iaCenter;
B.ImageStyle:= TNCButton.TNCImageStyle.isNormal;
B.ImageIndex:= fa_chevron_left;
end;
What I've tried so far:
Replaced the form's Icon with a completely empty .ico file.
Changing form style to bsSizeToolWin, but the title bar becomes too small, and I lose the minimize / maximize buttons.
Changing form style to bsDialog, but I get the same effect as #2 above, as well as not being able to resize the form.
Made sure button style is nsPushButton, and although it covers up the form icon, clicking the area still clicks the icon, which in turn shows the default system menu.
Following everything in this thread, but the conclusion is that Windows forces you to have this icon.
Removed biSystemMenu from the form's BorderIcons property, but this also removes the default buttons in the top-right of the form, forcing me to place my own system buttons there.
How do do I completely eliminate the form icon and its default functionality in favor of my Windows 10 style back button?
The TNCControls component includes the ShowSystemMenu property. If you set the value to false, then the system menu will be not shown.
Try this
uses
Vcl.Styles.Utils.Graphics;
procedure TfrmTestMain.FormCreate(Sender: TObject);
begin
SetupTitleBar;
end;
procedure TfrmTestMain.NCClick(Sender: TObject);
begin
ShowMessage('Hello');
end;
procedure TfrmTestMain.SetupTitleBar;
var
B: TNCButton;
begin
FNCControls:= TNCControls.Create(Self);
FNCControls.ShowSystemMenu := False; //Disable the system menu.
B := FNCControls.ButtonsList.Add;
B.Style := TNCButton.TNCButtonStyle.nsTranparent;
B.BoundsRect := Rect(0, 0, 45, 32);
B.UseFontAwesome:= True;
B.Caption := '';
B.ImageAlignment:= TImageAlignment.iaCenter;
B.ImageStyle:= TNCButton.TNCImageStyle.isNormal;
B.ImageIndex:= fa_chevron_left;
B.OnClick := NCClick;
end;

Enable scroll bars in disabled TMemo control

Is there a way how to enable scroll bars in disabled TMemo component ? I want let the users scroll the content even if the control's Enabled property is set to False.
I know about the possible workaround with ReadOnly and color changes like in disabled state, but this would help me a lot.
Thanks in advance :)
A control can be disabled or enabled, but not half dis- and half enabled. (And, for the nit-pickers amongst us, I think no hack should make it so :-), for the reason given below).
Using ReadOnly is the easiest solution. Be mindful though with the color changes to not make the control look disabled. That would also be very confusing for the user with regard to recognizing enabled/disabled controls. It would be better to make it look like a scrollable multi-line label. That usually is done by setting the (background) color equal to the color of its parent.
Haven't used the solution suggested and linked by #HalloDu, but that looks like a good alternative.
Well, it is not exactly, what you want, but the effect is the same. Look at this article where an ViewOnly Property for WinControls is implemented, which I found quite useful over the years. LINK
That's not perfect way but it works :
Use ScrollBar comp. adjacent to Memo.
procedure TForm9.FormCreate(Sender: TObject);
begin
Memo1.ScrollBars := ssNone;
ScrollBar1.Min := 0;
ScrollBar1.Max := Memo1.Lines.Count div (Memo1.Height div 13);//13 is height of a line in memo
end;
procedure TForm9.ScrollBar1Scroll(Sender: TObject; ScrollCode: TScrollCode;
var ScrollPos: Integer);
begin
if ScrollCode in [scPageDown, scLineDown] then
SendMessage(Memo1.Handle, WM_VSCROLL, SB_PAGEDOWN,0)
else if ScrollCode in [scPageUp, scLineUp] then
SendMessage(Memo1.Handle, WM_VSCROLL, SB_PAGEUP,0);
end;
There is a way.
Place entire TMemo inside a TScrollBox.
When you fill the memo with text, adjust the height and width to accommodate the size of the text (that's another question but I'm sure it can be done)

Live update of StringGrid when horizontal scroll bar is moving?

In Delphi 2010, I need to display a grid that has a horizontal scroll bar with about 15 columns x 5 rows.
I chose to use a StringGrid.
However, while the mouse button is down dragging the horizontal scroll bar, I want the grid to scroll live.
The StringGrid component, it appears, does not scroll live. It waits until the mouse button is released before updating the column and scrolling if necessary.
Also, the horizontal scroll bar button (is that what it's called) is not proportional to the number of columns. And for a down-arrow when on the bottom row to move to the top of the next column to the right...
These seem like common needs, so I was surprised not to find them in TStringGrid.
Any suggestions on a way around these two problems? I can use a DbGrid or other standard component, but my preference is to not use a commercial grid if I can avoid it. And I'm not going to use shareware or freeware...
TIA
For the first question, you can set goThumbTracking in the StringGrid's Options at design-time, or at run-time: StringGrid1.Options := StringGrid1.Options + [goThumbTracking];
For the third question, you can provide the functionality you need by using keyboard event handlers of the control. An example;
procedure TForm1.StringGrid1KeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
var
StringGrid: TStringGrid;
begin
StringGrid := Sender as TStringGrid;
case Key of
VK_DOWN:
if StringGrid.Row = StringGrid.RowCount - 1 then begin
Key := 0;
StringGrid.Row := StringGrid.FixedRows;
if StringGrid.Col = StringGrid.ColCount - 1 then
StringGrid.Col := StringGrid.FixedCols
else
StringGrid.Col := StringGrid.Col + 1;
end;
VK_UP: //...;
VK_RIGHT: //;
VK_LEFT: //;
end;
end;
For the second question, the scrolling code seems to be buried in private methods of TCustomGrid. I have no clue how to achieve that..
If noticed you are not interested in third party components - Freeware, I am not fond of these either, but we all must make sacrifices sometimes if we want to get the problems solved. This is one of these sacrifices! This component is to good to be ignored. You will not create something like it yourself if you don't have a couple of years of free time.
Either write a new component based on TStringGrid (I would not - it is not the best tool in the box to begin with)
But take some time and learn TVirtualStringTree. The component is years ahead of TStrignGrid. The source is available and there are many who uses it.
And there are events already implemented to react on scrollbar changes
OnScroll, OnShowScrollbar
http://www.delphi-gems.com/index.php?option=com_content&task=view&id=12&Itemid=38
Search on stackoverflow and you can read much more about tvirtualstringtree
Second the suggestion to use TVirtualStringTree. Working with the TStringGrid component is like stabbing yourself in the belly with a rusty scissor.

When I add a TPanel to a TToolBar, do I get a TPanel or a TToolButton?

When Delphi (2006) goes quantum: I've got "something" that appears to be both a TToolBar and a TPanel, depending on how you observe it. I'd like to understand what's going on.
Here is how to create it and what happens:
in the DFM
add a TToolBar named bar;
in that TToolBar, put a TPanel.
in the code and at runtime:
the panel appears in the list of buttons bar.Buttons[], let's say at index i
bar.Buttons[i], from the compiler point of view, is a TToolButton
bar.Buttons[i].ClassName = 'TPanel'
(bar.Buttons[i] is TToolButton) = true, but that's the compiler optimising the call to 'is' out;
indeed IsBarButton(bar.Buttons[i]) is false for function IsBarButton (defined below);
bar.Buttons[i].Name is the name I gave the TPanel in the DFM
inspecting the value bar.Buttons[i] in the debugging:
it has a property 'Caption' the real TToolButton's don't have
strangely, it has all properties TToolButton's have, like TToolButton.Indeterminate (=true).
IsToolButton:
function IsToolButton(X : TObject) : boolean;
begin
Result := X is TToolButton;
end;
So bar.Buttons[i] both is and is not a TToolButton... what's up ?
(Bottom story is I'd like to distinguish my TPanel from the genuine TToolButton's. This I can do in more or less hackish ways. My goal by asking this question here, is to get a fuller understanding of what's really happening here.)
Question: what is happening ?
Sub-question: is it legitimate to add a TPanel to a TToolBar ?
The only thing the OS allows to be added to a tool bar is a tool button. To add anything else, you technically need to create a button and then put your other things on top of it. The button that gets added is literally a placeholder. It's there to take up space so the next thing you add gets positioned properly.
You can see this sometimes if the non-tool-button control you add is transparent. Then you can see the tool bar's separator underneath, so it looks like there's a vertical line running through the middle of your control.
When you add a non-tool-button control to the tool bar, the Buttons property indeed lies about the type of the control. You'll notice throughout ComCtrls.pas that TToolBar itself always casts the buttons to TControl and then checks whether they really descend from TToolButton. It's completely legitimate to add non-buttons to a tool bar; that's why the Form Designer allows it in the first place.
I suggest you use the Form Designer to create your tool bar. That way, the IDE will maintain an identifier for you in your form, so you'll always have a direct reference to your panel. You won't have to go hunting for it in the tool bar. Even if you're creating the tool bar manually, it's a good idea to make an extra field to refer to the panel. Even if you move the panel around within the tool bar, it will still be the same object the whole time, so you needn't worry about dangling references.
When you put a couple of buttons and a panel on a toolbar, and a Memo somewhere, then run this code in the form's onCreate:
procedure TForm1.FormCreate(Sender: TObject);
function _IsToolButton(const aObject: TObject): Boolean;
begin
Result := aObject is TToolButton;
end;
function _IsPanel(const aObject: TObject): Boolean;
begin
Result := aObject is TPanel;
end;
var
i: Integer;
begin
for i := 0 to bar.ButtonCount - 1 do begin
Memo.Lines.Add(Format('bar.Buttons[%d].Name: %s', [i, bar.Buttons[i].Name]));
Memo.Lines.Add(Format('bar.Buttons[%d].ClassName: %s', [i, bar.Buttons[i].ClassName]));
Memo.Lines.Add(Format('bar.Buttons[%d] is TToolButton: %s', [i, BoolToStr(_IsToolButton(bar.Buttons[i]), True)]));
Memo.Lines.Add(Format('bar.Buttons[%d] is TPanel: %s', [i, BoolToStr(_IsPanel(bar.Buttons[i]), True)]));
// Memo.Lines.Add(Format('bar.Buttons[%d] has Caption property: %s', [i, 'dunno yet']));
Memo.Lines.Add('');
end;
end;
you'll see that the panel is not a TooButton and most definitely a TPanel.
The debugger showing properties of a ToolButton for the panel, is simply the debugger casting each and every bar.Buttons[i] to a TToolButton. When you right-click on the "Data" tab of the Debug inspector, you can Type Cast it to a TPanel and you will get the correct information.
'is it legitimate?' - well, you are definitely using the toolbar in a way that the creator of the toolbar did not ment it to be used. Will it blow up in your face? Who knows. I guess you could walk through the sourcecode for the toolbar and check if it is safe or not, but what about possible third party tools or components, expecting to find buttons in a toolbar?
I would see if I could find another way of solving my problem. Clever hacks have a tendency to turn out not so clever after all, and it will surely higten the wtf-rate of your code.
Do you have to use a toolbar? What about a flowpanel with buttons and panels instead? Or a panel with a toolbar and a panel?

Resources