I want to know, how to add or remove windowMenu option of actionMenuBar component, i have a mdi aplication i can add the option but later I can't remove it
sorry my english
i have this:
//add windowmenu and works fine
procedure TForm2.Button1Click(Sender: TObject);
begin
Form1.ActionMainMenuBar1.WindowMenu := '&Ventana';
end;
//remove windowmenu but dont work
procedure TForm2.Button2Click(Sender: TObject);
begin
Form1.ActionMainMenuBar1.WindowMenu := '';
end;
type
ActionMainMenuBarAccess = class(TActionMainMenuBar);
procedure TForm2.Button2Click(Sender: TObject);
begin
ActionMainMenuBar1.WindowMenu := '';
ActionMainMenuBarAccess(ActionMainMenuBar1).FWindowMenuItem := nil;
ActionMainMenuBarAccess(ActionMainMenuBar1).RefreshMDIMenu;
end;
See also this QualityCentral report.
Related
I have 2 StyleBooks loaded with custom styles and want them to be applied for all forms at once (testing it on windows, Tokyo 10.2.3).
procedure TForm6.Button1Click(Sender: TObject);
begin
StyleBook := StyleBook2;
end;
procedure TForm6.Button2Click(Sender: TObject);
begin
StyleBook := StyleBook1;
end;
If I set UseStyleManager=true, this code doesn't work. If UseStyleManager=false, it works but only for 1 form.
You can use Application.Components[] to get access to each form and set its StyleBook property. Leave UseStyleManager = False for both stylebooks.
Add to the main form:
type
TForm14 = class(TForm)
...
private
procedure ChangeApplicationStyle(sb: TStyleBook);
and implement:
procedure TForm14.ChangeApplicationStyle(sb: TStyleBook);
var
i: integer;
begin
for i := 0 to Application.ComponentCount - 1 do
if Application.Components[i] is TForm then
TForm(Application.Components[i]).StyleBook := sb;
end;
Finally to change, e.g.:
procedure TForm14.Button1Click(Sender: TObject);
begin
ChangeApplicationStyle(StyleBook1);
end;
procedure TForm14.Button2Click(Sender: TObject);
begin
ChangeApplicationStyle(StyleBook2);
end;
I have an application that restores windows on startup but this results in a potential flicker as each window is created and positioned.
To get around this I have the splash screen (stretched to the full size of the screen) set to "StayOnTop" and close it after the OnShow event using a TTask. The problem is that occasionally the splash screen gets stuck. If you click where buttons should be they redraw and show correctly.
I have tried to "invalidate" all WinControls but this problem still shows up.
I have never seen the problem in the debugger.
Are there any other tricks anyone can suggest to forcing a full repaint of the screen?
Here is my code to close the splash - This is in the OnShow of the main form.
aTask := TTask.Create(procedure()
begin
Sleep(800);
TThread.Synchronize(nil, procedure()
begin
fSplash.Close;
FreeAndNil(fSplash);
DoInvalidate(self);
end);
end);
aTask.Start;
Here is my attempt to invalidate everything...
Procedure DoInvalidate( aWinControl: TWInControl );
var
i: Integer;
ctrl: TControl;
begin
for i:= 0 to aWinControl.Controlcount-1 do
begin
ctrl:= aWinControl.Controls[i];
if ctrl Is TWinControl then
DoInvalidate( TWincontrol( ctrl ));
end;
aWinControl.Invalidate;
end;
Martin
You don't need to recursively invalidate everything, just invalidating the Form itself is sufficient.
If you upgrade to 10.2 Tokyo, you can now use TThread.ForceQueue() instead of TThread.Synchronize() in a TTask:
procedure TMainForm.FormShow(Sender: TObject);
begin
TThread.ForceQueue(nil, procedure
begin
FreeAndNil(fSplash);
Application.MainForm.Invalidate;
end
);
end;
If you stick with TTask, you should at least use TThread.Queue() instead:
procedure TMainForm.FormShow(Sender: TObject);
begin
TTask.Create(procedure
begin
TThread.Queue(nil, procedure
begin
FreeAndNil(fSplash);
Application.MainForm.Invalidate;
end;
end
).Start;
end;
Or, you could just use a short TTimer, like zdzichs suggested:
procedure TMainForm.FormShow(Sender: TObject);
begin
Timer1.Enabled := True;
end;
procedure TMainForm.Timer1Timer(Sender: TObject);
begin
Timer1.Enabled := False;
FreeAndNil(fSplash);
Invalidate;
end;
Or, you could assign an OnClose event handler to the splash form to invalidate the MainForm, and then PostMessage() a WM_CLOSE message to the splash form:
procedure TMainForm.FormCreate(Sender: TObject);
begin
fSplash := TSplashForm.Create(nil);
fSplash.OnClose := SplashClosed;
fSplash.Show;
end;
procedure TMainForm.FormShow(Sender: TObject);
begin
if fSplash <> nil then
PostMessage(fSplash.Handle, WM_CLOSE, 0, 0);
end;
procedure TMainForm.SplashClosed(Sender: TObject; var Action: TCloseAction);
begin
fSplash := nil;
Action := caFree;
Invalidate;
end;
Or, use the OnDestroy event instead:
procedure TMainForm.FormCreate(Sender: TObject);
begin
fSplash := TSplashForm.Create(nil);
fSplash.OnDestroy := SplashDestroyed;
fSplash.Show;
end;
procedure TMainForm.FormShow(Sender: TObject);
begin
if fSplash <> nil then
fSplash.Release; // <-- delayed free
end;
procedure TMainForm.SplashDestroyed(Sender: TObject);
begin
fSplash := nil;
Invalidate;
end;
In my application i save some edit value in a Tstringlist with code below:
procedure TForm1.Button3Click(Sender: TObject);
var
F: TStringList;
begin
SaveDialog1.Filter := 'GPP files (*.GPP)|*.GPP';
if SaveDialog1.Execute then
begin
F := TStringList.Create;
with F do
begin
Add(label7.Caption);
Add(label21.Caption);
SaveToFile(Savedialog1.Filename);
Free;
end;
end;
end;
I want to save Tradiobutton state too in this Tstringlist.
Can you help me?
Regards
Making an answer just to show the code sample in properly formatted form
To read:
try/finally pattern - http://docwiki.embarcadero.com/RADStudio/XE2/en/Writing_a_Finally_Block_(Delphi)
TIniFile class and boolean values - http://docwiki.embarcadero.com/Libraries/XE2/en/System.IniFiles.TCustomIniFile.WriteBool - as a somewhat outdated with limited capabilities but a very simple text structured format.
So your code would become something like
const ini_def_sect = 'Default Section';
procedure TForm1.Button3Click(Sender: TObject);
var
F: TCustomIniFile;
begin
SaveDialog1.Filter := 'GPP files (*.GPP)|*.GPP';
if SaveDialog1.Execute then
begin
F := TIniFile.Create(SaveDialog1.Filename);
try
F.WriteString(ini_def_sect, label7.Name, label7.Caption);
F.WriteString(ini_def_sect, label21.Name, label21.Caption);
F.WriteBool(ini_def_sect, radiobutton1.Name, radiobutton1.Checked);
F.UpdateFile;
finally
F.Destroy;
end;
end;
end;
Or in VCL with-based style (that many dislike as they dislike with statement in Pascal)
const ini_def_sect = 'Default Section';
procedure TForm1.Button3Click(Sender: TObject);
begin
SaveDialog1.Filter := 'GPP files (*.GPP)|*.GPP';
if SaveDialog1.Execute then
begin
with TIniFile.Create(Savedialog1.Filename) do
try
WriteString(ini_def_sect, label7.Name, label7.Caption);
WriteString(ini_def_sect, label21.Name, label21.Caption);
WriteBool(ini_def_sect, radiobutton1.Name, radiobutton1.Checked);
UpdateFile;
finally
Destroy;
end;
end;
end;
And you REALLY REALLY should give your variables (including labels, forms, radio buttons, etc) reasonable names while you still can remember a bit about what do each of those mean. Trust me - a month or two and you would forget.
PS: reading the structured file could be something like
procedure TMainForm.btnOpenClick(Sender: TObject);
begin
OpenDialog1.Filter := 'GPP files (*.GPP)|*.GPP';
if OpenDialog1.Execute then
begin
with TIniFile.Create(OpenDialog1.Filename) do
try
label7.Caption := ReadString(ini_def_sect, label7.Name, '');
label21.Caption := ReadString(ini_def_sect, label21.Name, '');
radiobutton1.Checked := ReadBool(ini_def_sect, radiobutton1.Name, False);
finally
Destroy;
end;
end;
end;
I did some searching and only found more unanswered questions. :)
Using D5pro.
I want to reassign the DataSource to a TDBGrid at run time. I have seven identical structured DataSets and depending on a button click I want the appropriate DataSet displayed in the grid.
I have tried everything and I cannot get it to show the next DataSet. It sticks with the first one assigned at start up. I am getting to overkill approaches and still nothing is working. Here's where I am at the moment.
procedure SetSource(var aSrc : TDataSource);
begin
aSrc.DataSet.Close;
dbgridShowData.DataSource:=aSrc;
aSrc.DataSet.Open;
aSrc.DataSet.First;
aSrc.DataSet.Refresh;
end;
Where am I going wrong?
Thanks
You can change the Dataset shown by a DBGrid quite easily at runtime quite easily. There two approaches:
1: use a single DataSource assigned to DBGrid.DataSource and change the DataSource.DataSet to the desired DataSet. Here is a simple example with all assignments made at runtime.
procedure TForm1.FormCreate(Sender: TObject);
begin
DBGrid1.DataSource := DataSource1;
DataSet1.Active := true;
DataSet2.Active := true;
DataSet3.Active := true;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
DataSource1.DataSet := DataSet1;
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
DataSource1.DataSet := DataSet2;
end;
procedure TForm1.Button3Click(Sender: TObject);
begin
DataSource1.DataSet := DataSet3;
end;
2: use a DataSource for each DataSet and change DBGrid.DataSource to the desired DataSource. Here is a simple example with all assignments made at runtime.
procedure TForm1.FormCreate(Sender: TObject);
begin
DataSource1.DataSet := DataSet1;
DataSource2.DataSet := DataSet2;
DataSource3.DataSet := DataSet3;
DataSet1.Active := true;
DataSet2.Active := true;
DataSet3.Active := true;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
DBGrid1.DataSource := DataSource1;
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
DBGrid1.DataSource := DataSource2;
end;
procedure TForm1.Button3Click(Sender: TObject);
begin
DBGrid1.DataSource := DataSource3;
end;
If you define the columns of the DBGrid, the structure of the DataSets will need to be the the same, or you will have to change the column definitions when you change the Dataset displayed.
I prefer using a DataSource per DataSet because it is more flexible.
You probably need to change the DataSource.DataSet instead:
procedure SetDataFromDataSet(const aDataSource: TDataSource;
const aNewDataSet: TDataSet);
begin
aDataSource.DataSet.Close;
aDataSource.DataSet := aNewDataSet;
if not aNewDataSet.Active then
aNewDataSet.Open;
end;
Sample use:
SetDataFromDataSet(DataSource1, CustomerQuery);
You may not want to close and open datasets globally like this, though. It's probably better to do that from the calling code. Of course, that would depend on what you need for your app.
Tested with Delphi5 pro.
procedure TForm1.setDataSourceDataSet(var newDataSource:TDataSource);
begin
if DBgrid1.DataSource = nil then begin
DBgrid1.DataSource:=newDataSource;
end else begin
if DBgrid1.DataSource.Name = newDataSource.Name then exit;
DBGrid1.DataSource.Enabled:=False;
DBgrid1.DataSource:=newDataSource;
end;
If DBgrid1.DataSource.DataSet.active=False then DBgrid1.DataSource.DataSet.active:=True;
DBGrid1.DataSource.Enabled:=True;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
setDataSourceDataSet(DataSource1);
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
setDataSourceDataSet(DataSource2);
end;
The secret lies in:
DBGrid1.DataSource.Enabled:=False;
...making changes...
DBGrid1.DataSource.Enabled:=True;
Tested with D5Pro
We have a combo box with more than 100 items.
We want to filter out the items as we enter characters in combo box. For example if we entered 'ac' and click on the drop down option then we want it to display items starting with 'ac' only.
How can I do this?
Maybe you'd be happier using the autocompletion features built in to the OS. I gave an outline of how to do that here previously. Create an IAutoComplete object, hook it up to your combo box's list and edit control, and the OS will display a drop-down list of potential matches automatically as the user types. You won't need to adjust the combo box's list yourself.
To expand on Rob's answer about using the OnChange event, here is an example of how to do what he suggests.
procedure TForm1.FormCreate(Sender: TObject);
begin
FComboStrings := TStringList.Create;
FComboStrings.Add('Altair');
FComboStrings.Add('Alhambra');
FComboStrings.Add('Sinclair');
FComboStrings.Add('Sirius');
FComboStrings.Add('Bernard');
FComboStrings.Sorted := True;
ComboBox1.AutoComplete := False;
ComboBox1.Items.Text := FComboStrings.Text;
ComboBox1.Sorted := True;
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
FreeAndNil(FComboStrings);
end;
procedure TForm1.ComboBox1Change(Sender: TObject);
var
Filter: string;
i: Integer;
idx: Integer;
begin
// Dropping down the list puts the text of the first item in the edit, this restores it
Filter := ComboBox1.Text;
ComboBox1.DroppedDown := True;
ComboBox1.Text := Filter;
ComboBox1.SelStart := Length(Filter);
for i := 0 to FComboStrings.Count - 1 do
if SameText(LeftStr(FComboStrings[i], Length(ComboBox1.Text)), ComboBox1.Text) then
begin
if ComboBox1.Items.IndexOf(FComboStrings[i]) < 0 then
ComboBox1.Items.Add(FComboStrings[i]);
end
else
begin
idx := ComboBox1.Items.IndexOf(FComboStrings[i]);
if idx >= 0 then
ComboBox1.Items.Delete(idx);
end;
end;
My brief contribution working with objects in the combobox:
procedure FilterComboBox(Combo: TComboBox; DefaultItems: TStrings);
function Origin: TStrings;
begin
if Combo.Tag = 0 then
begin
Combo.Sorted := True;
Result := TStrings.Create;
Result := Combo.Items;
Combo.Tag := Integer(Result);
end
else
Result := TStrings(Combo.Tag);
end;
var
Filter: TStrings;
I: Integer;
iSelIni: Integer;
begin
if(Combo.Text <> EmptyStr) then
begin
iSelIni:= Length(Combo.Text);
Filter := TStringList.Create;
try
for I := 0 to Origin.Count - 1 do
if AnsiContainsText(Origin[I], Combo.Text) then
Filter.AddObject(Origin[I], TObject(Origin.Objects[I]));
Combo.Items.Assign(Filter);
Combo.DroppedDown:= True;
Combo.SelStart := iSelIni;
Combo.SelLength := Length(Combo.Text);
finally
Filter.Free;
end;
end
else
Combo.Items.Assign(DefaultItems);
end;
You can handle the combo box's OnChange event. Keep a master list of all items separate from the UI control, and whenever the combo box's edit control changes, adjust the combo box's list accordingly. Remove items that don't match the current text, or re-add items from the master list that you removed previously.
As Rob already answered, you could filter on the OnChange event, see the following code example. It works for multiple ComboBoxes.
{uses}
Contnrs, StrUtils;
type
TForm1 = class(TForm)
ComboBox1: TComboBox;
ComboBox2: TComboBox;
procedure FormCreate(Sender: TObject);
procedure ComboBoxChange(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
FComboLists: TList;
procedure FilterComboBox(Combo: TComboBox);
end;
implementation
{$R *.dfm}
procedure TForm1.ComboBoxChange(Sender: TObject);
begin
if Sender is TComboBox then
FilterComboBox(TComboBox(Sender));
end;
procedure TForm1.FilterComboBox(Combo: TComboBox);
function Origin: TStrings;
begin
if Combo.Tag = 0 then
begin
Combo.Sorted := True;
Result := TStringList.Create;
Result.Assign(Combo.Items);
FComboLists.Add(Result);
Combo.Tag := Integer(Result);
end
else
Result := TStrings(Combo.Tag);
end;
var
Filter: TStrings;
I: Integer;
begin
Filter := TStringList.Create;
try
for I := 0 to Origin.Count - 1 do
if AnsiStartsText(Combo.Text, Origin[I]) then
Filter.Add(Origin[I]);
Combo.Items.Assign(Filter);
Combo.SelStart := Length(Combo.Text);
finally
Filter.Free;
end;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
FComboLists := TObjectList.Create(True);
// For Each ComboBox, set AutoComplete at design time to false:
ComboBox1.AutoComplete := False;
ComboBox2.AutoComplete := False;
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
FComboLists.Free;
end;