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;
Related
I'm trying to disable a TForm's descendant and showing it as a modal form.
procedure TForm1.Button1Click(Sender: TObject);
var
Frm : TMyForm;
begin
Frm := TMyForm.Create(nil);
try
Frm.Enabled := False;
Frm.ShowModal();
finally
Frm.Free;
end;
end;
At runtime, it raises the following error message:
Cannot make a visible window modal.
The OP wants to display a disabled form modally when the form should be displayed for read-only purposes.
Disabling the form is the wrong thing to do.
How do you display the information? If you are using TEdit, TMemo, or TRichEdit controls, you should simply set them to read only. Otherwise, if you have some combinations of various controls like radio buttons, you should disable each and every such control, not the form itself. I mean, surely you still want the Cancel button to be enabled?
In addition, disabling the form instead of the actual controls will make the controls look enabled, which is very confusing! That's an important point.
So what you need to do is to display the form normally (not disabled!) and then set its controls to their appropriate states when the dialog is shown.
Just to emphasise my point about disabling the form vs its controls, consider this dialog box:
If I do
procedure TCustomViewFrm.FormShow(Sender: TObject);
begin
Enabled := False;
end;
then it looks like this when shown:
As you can see, every control looks very enabled indeed, but no control responds to mouse or keyboard input. This is very confusing and a horribly bad UX.
In fact, you cannot even close the dialog box using its title-bar Close button or Alt+F4. You cannot close it using its system menu, either. In fact, you cannot close it at all, because to close a window, it must respond to user input, and a disabled window doesn't do that. (You cannot move the window, either.)
Instead, if we disable all controls (except the Cancel button),
procedure DisableControl(AControl: TWinControl);
begin
for var i := 0 to AControl.ControlCount - 1 do
begin
if
(AControl.Controls[i] is TCustomButton)
and
(TCustomButton(AControl.Controls[i]).ModalResult = mrCancel)
then
Continue;
if AControl.Controls[i] is TWinControl then
DisableControl(TWinControl(AControl.Controls[i]));
AControl.Controls[i].Enabled := False;
end;
end;
procedure TCustomViewFrm.FormShow(Sender: TObject);
begin
DisableControl(Self);
end;
you get this nice UI:
Not only is it very clear that all controls are disabled, the user can also close the dialog box without killing your application using the Task Manager.
I am trying to put a combobox on a toolbar in Delphi 2010. The App is a MDI text editor. If I place a combobox on the toolbar and run the app, when I click the combobox, it opens up a new child window and doesn't drop down for a selection. I have tried putting the toolbar and combobox in both a controlbar and a coolbar, both with the same results. In fairness, I have not recreated the toolbar, just moved it to the other controls.
Has anyone seen this before and how do I get around it?
I just tried it again with the same results. Here is the code for the combobox1.
procedure TMainForm.ComboBox1Change(Sender: TObject);
begin
exec_sql(combobox1.Text);
end;
There is no on click for the toolbar and no button currently opens a new child.
The exec_sql looks like this:
procedure TMainForm.exec_sql(MachName:string);
var
sql_str: string;
parm_str: string;
begin
mach.Free;
parm_str := MachName;
sql_str := 'Select * from machines where MACHINE_NAME = :parm_str';
with adoquery1 do
begin
close;
sql.Text := sql_str;
with Parameters.ParamByName('parm_str') do
begin
DataType := ftString;
Value := parm_str;
end;
open;
mach := TMachineData.get_record_data(ADOQuery1);
end;
ShowMessage('Current Machine Is ' + mach.MACHINE_NAME);
end;
I am unable to reproduce your problem. Here are the steps I took to try and do so:
File->New->Other->Delphi Projects->MDI Application
Created a new folder when prompted for the project
Delphi shows a new MDI parent, with a toolbar, some toolbuttons, menu, etc.
Dropped a new TComboBox on the toolbar
Added 'Item 1', 'Item 2', and 'Item 3' to the combobox via the Object Inspector
Ran the application, and clicked the dropdown button on the combobox.
Picked any item from the combobox; it behaved as expected.
Picked a different item from the combobox. It behaved as expected.
Therefore, the problem is not with placing a TComboBox on a TToolBar, and has to be elsewhere in your code, in a location not included in your question.
You'll need to use the debugger, set some breakpoints in various locations, and take a look at the call stack window to see how you got where you're at in the code. You can then set a new breakpoint in one of those earlier calls, repeat the process, and keep doing so until you trace back to the point that's causing your problem.
I just tested debugging in this fashion. I created a FormCreate event in the default CHILDWIN unit, added Dialogs to the implementation uses clause, and added a call to MessageDlg('New child created', mtInformation, [mbOK], 0); in that FormCreate event. I set a breakpoint there, and ran the app, and then clicked on the New toolbar button. When the breakpoint was triggered, the call stack window looked like this (I've highlighted the place that caused the new child window to be created - the line below it is relevant as well):
The problem was the combobox was firing the Form1.OnActivate event which created a new mdi child. OnActivate was set to ActionFirstChildExecute. I was creating a new blank child when the app opened. This had the described undesired effect. I removed the OnActivate and moved the ActionFirstChildExecute to OnShow. The app and combobox then worked as expected. There was nothing in the ActionFirstChildExecute to cause the behavior as shown in the code below. The problem was clicking the combobox fired the Form1.OnActivate event calling the code below.
procedure TMainForm.ActionFirstChildExecute(Sender: TObject);
var
ChildForm: TMDIChild;
begin
Inc (Counter);
ChildForm := TMDIChild.Create (Self);
ChildForm.Caption := ('NONAME' + IntToStr(MDIChildCount));
ChildForm.Show;
(ActiveMDIChild as TMDIChild).FormCreate(Application);
if ParamStr(1) <>'' then open_mru_item(ParamStr(1));
end;
I am working with a Delphi form that contains a TMainMenu. In a particular situation I want to show this form parented and client-aligned inside another form. This works fine but the main menu of the parented form does not appear. I see a comment in this SO question that states "A child window cannot have a menu". Is there anything that I can do to override this behaviour and make a TMainMenu appear?
An aside: I've only just noticed this because where I have used this principle before, I've been using the Developer Express menu component and this is quite happy to show in a child form.
Later edit:
Using the code from TLama below, this works (but the child menu is not themed, I,e very plain):
This works:
procedure TForm65.FormShow(Sender: TObject);
begin
Winapi.Windows.SetParent(ChildForm.Handle, Handle); // <<<<<<<<
ChildForm.BorderStyle := bsNone;
ChildForm.Align := alClient;
ChildForm.Show;
end;
This code DOES NOT work. Why?
procedure TForm65.FormShow(Sender: TObject);
begin
ChildForm.Parent := Self; // <<<<<<<<<
ChildForm.BorderStyle := bsNone;
ChildForm.Align := alClient;
ChildForm.Show;
end;
MSDN makes this perfectly clear:
A child window has a client area but no other features, unless they are explicitly requested. An application can request a title bar, a window menu, minimize and maximize buttons, a border, and scroll bars for a child window, but a child window cannot have a menu.
This refers to the menu as drawn by Windows itself. If your component custom draws a menu bar, then of course it can have a menu, even if it is a child window.
Your call to SetParent does not make your window into a child window. This is explained in the documentation:
For compatibility reasons, SetParent does not modify the WS_CHILD or WS_POPUP window styles of the window whose parent is being changed. Therefore, if hWndNewParent is NULL, you should also clear the WS_CHILD bit and set the WS_ POPUP style after calling SetParent. Conversely, if hWndNewParent is not NULL and the window was previously a child of the desktop, you should clear the WS_POPUP style and set the WS_CHILD style before calling SetParent.
I just want to change the color of a button after I press it.
Do I have to use "styles" to do this or....?
You can change the button.StyleLookup property to change the style (color).
You need to add a new style to the Stylebook.
Select 'Edit custom style ...' in the Right mouse button menu from a button.
Change the Fill.Color property from the TRectangle items under the background:TRectangle
Apply and Close Stylebook
Clear button.stylelookup
Change the button.stylelookup in your buttonclick to the new create style, when you didn't change the name its Button1Style1
Using Styles
An alternative to creating a different style and switching to that new style would be creating a custom style for the button and changing the color in that style at run time.
Right-click the button and select 'Edit custom style...' from the main menu.
Click Apply and Close in the style editor.
You've just created a custom style for the button. So when you edit it at run time, it will only affect that button.
Now, enter the following in your OnClick event to change the color at run time:
var
r: TRectangle;
begin
// Find the background TRectangle style element for the button
r := (Button1.FindStyleResource('background') as TRectangle);
if Assigned(r) then
begin
r.Fill.Color := claBlue;
end;
end;
Note: Add FMX.Objects to your uses clause if you don't already have it. That's where TRectangle is.
But wait...
You'll notice that the button's color changes back to the default when the mouse leave or enters the button. That's due to the animations. If you set the stylename properties for both of the TColorAnimation style elements in the style editor for the custom style, you can also set the color on those. For my example, I've named the TColorAnimations coloranimation1 and coloranimation2.
Here's the revised code:
var
r: TRectangle;
ca: TColorAnimation;
begin
// Find the background TRectangle style element for the button
r := (Button1.FindStyleResource('background') as TRectangle);
if Assigned(r) then
begin
r.Fill.Color := claBlue;
end;
ca := (Button1.FindStyleResource('coloranimation1') as TColorAnimation);
if Assigned(ca) then
begin
ca.StartValue := claBlue;
end;
ca := (Button1.FindStyleResource('coloranimation2') as TColorAnimation);
if Assigned(ca) then
begin
ca.StopValue := claBlue;
end;
Note: Add FMX.Ani to your uses clause for TColorAnimation.
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.