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;
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'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;
I created a TTabControl with two TTabItems. On each TTabItem there is one (or more) TImageViewers and several TEdits. When I click on the TImageViewer, a modal screen pops up, I set some values, and I want to report those values to the user through the TEdits. So on returning from the Modal screen,
I execute
editn.text := whateveritis;
I then say
editn.repaint;
Nothing happens. I say TTabItem.repaint. Nothing happens. I click the other TTabIem and then come back to the first TabItem and, voila, the Edit control contains the right information. So my editn.text := whateveritis must be working (that's the only write to the TEdit), but I can't get the blinkin' control to show the result without going off-tab. How do I get it to redisplay as soon as I change the content? Do I need to write an OnChange routine that is one line, self.repaint? Seems ugly, and I'd hope there's a more global approach. Suggestions?
In light of initial comments, let me give more details. Setup: In main screen, drop in tabcontrol, and in tabcontrol drop in 2 tabitems. In tabitem1, drop in a timageviewer and 4 tedits (plus other stuff, probably irrelevant). Image gets dropped into the imageviewer (and displays correctly). The onclick event activates the following (ellipsis cuts out irrelevant code):
procedure TSCKMain.ImageViewer1Click(Sender: TObject);
var
lochold, scrollhold : tpoint;
backfromwavform : tmodalresult;
begin
lochold.X := mouseloccallback.x;
lochold.Y := mouseloccallback.y;
scrollhold.X := round(imageviewer1.ViewportPosition.X);
scrollhold.Y := round(imageviewer1.ViewportPosition.Y);
…
repeat backfromwavform := Wavform.Showmodal until backfromwavform<>mrnone;
case backfromwavform of
mrOK : begin {blue end}
Specsingle.BlueEnd.X := lochold.X;
Specsingle.BlueEnd.y := lochold.y;
edit13.Text := inttostr(Specsingle.BlueEnd.X);
Edit14.Text := inttostr(Specsingle.BlueEnd.y);
PublicWindowFlag := 'RePlot';
end;
mrContinue : begin {red end}
Specsingle.RedEnd.X := lochold.X;
Specsingle.RedEnd.y := lochold.y;
edit15.Text := inttostr(Specsingle.RedEnd.X);
Edit16.Text := inttostr(Specsingle.RedEnd.y);
PublicWindowFlag := 'RePlot';
end;
…
end;
if PublicWindowFlag<>'Cancel' then
if PublicWindowFlag='RePlot' then
begin
specsingle.RegenImage;
end
else
showmessage('Single image semaphore error. Debug.');
Imageviewer1.scrollto(scrollhold.X-Imageviewer1.viewportposition.X, scrollhold.y-Imageviewer1.ViewportPosition.Y);
end;
The modal screen sends back either mrContinue or mrOK correctly, and the appropriate case executes. However, edit13, edit14, edit15, and edit16 do not change their content. However, if I click over to Tabitem2 and back to Tabitem1, they DO repaint and DO contain the correct characters, which they could only have gotten from the above code. Conclusion: somehow, the edits aren’t repainting independently, but it’s not clear why.
Got it. The canvas for the imageviewer, the canvas for the bitmap in the imageviewer, and the canvas for the parent form are all in play. One must be sure that the canvas is the right one. As soon as scenes got untangled between imageviewer.bitmap and everything else, the edits worked as one would expect.
I'm replacing my good old TMainMenu with TActionMainMenuBar but have some issues with it.
I'm currently using for my main menu items the 'OnClick' handler, but I can't find a way to reproduce the same behavior with TActionMainMenuBar.
For example, I create a TAction "Test" with category "Sample" and I drag/drop this on the TActionMainMenuBar. I can add code to the execute handler of the sub-menu "Test" because it has a TAction assigned to it, but I can't add event code to the main menu item "Sample" because it is just a Category / TActionClientItem of "Test" with no events.
I tried to assign an Action to this TActionClientItem, but Delphi XE3 is saying "You cannot set property ..." and when I click that it gives me a dialog "Actions not implemented for the current framework 'None'".
Another way would be two TActions, "Sample" and "Test" with "(no category)", but I can't drag/drop "Test" as sub-menu of "Sample" which I dropped on the TActionMenuBar before. It looks like I can only drag/drop main menu items, and not sub-menu items on an empty main menu item.
The purpose for using the main menu item's OnClick handler is usually to determine whether sub-menu items are enabled/disabled or visible. It's also usually where you enable/disable things like toolbar buttons that perform the same function:
procedure TForm1.MyMainMenuItemClick(Sender: TObject);
begin
SomeMenuItem.Enabled := SomeConditionTest;
SomeToolButton.Enabled := SomeMenuItem.Enabled;
AnotherMenuItem.Enabled := AnotherConditionTest;
AnotherToolButton.Enabled := AnotherMenuItem.Enabled;
end;
For TActionMainMenuBar items, you do this in the individual actions instead, in the OnUpdate event. The advantage of this is that when you enable/disable the action, all controls connected to the action are also enabled/disabled at the same time.
procedure TForm1.SomeActionUpdate(Sender: TObject);
begin
SomeAction.Enabled := SomeConditionTest; // Also controls the toolbutton
end;
procedure TForm1.AnotherActionUpdate(Sender: TObject);
begin
AnotherAction.Enabled := AnotherConditionTest; // Toolbutton too.
end;
The OnUpdate event is called just before the the child items are displayed, which is the same time that the old main menu item's OnClick would be called.
I'm using the following method to create a an ActionClient with an Action at run time.
procedure TMainForm.AddToProjectHistory(Path: string);
var
NewOption: TAction;
ActionClient: TActionClientItem;
begin
NewOption := TAction.Create(self);
NewOption.ActionList := ActionManager1;
NewOption.Caption := Path;
NewOption.OnExecute := ProjectHistoryExecute;
ActionClient := TActionClientItem(aToolBarFile.ActionClient.Items[0].Items.Add);
ActionClient.Action := NewOption;
ActionClient.Caption := Path;
end;
This works fine if there is already an item in the list, but doesn't work at all if there isn't
e.g. if I add an Item at design time then I can add more items at runtime
But if I don't add anything at design time, theres no Drop Down to display the list of items, no drop down appears after adding items.
This doesn't have to be done with Actions but the rest of the menu system uses actions and I don't think I can add standard MenuItems to the action drop down.
Delphi 2005
The VCL automatically creates button controls of a type that depends on whether the item has child elements. By default (and depending on the ActionManager's style setting), for an ActionClientItem which has child items, a TXPStyleDropDownBtn button is created, and for a childless ActionClientItem, a TXPStyleButton is created.
So when the first child item is added during run time, the button is of the wrong type. Changing the type of that button would require destruction of the current button and a complete and manual instantiation of the new button. This should be possible, but have not tried, because:
The really most easy solution is to fool the VCL by adding a child item at design time, and to delete that item on form creation:
procedure TForm1.FormCreate(Sender: TObject);
begin
aToolBarFile.ActionClient.Items[0].Items[0].Free;
end;
if you create an actionclient of type "context", it won't have to change button type. The menu will drop down when you right click on the button instead.