From a form, I create and show a second form. From the second form I want to update a control on the first form. But I get access violations. I can get it to work with the form in the autocreate, but not when I create the form with the create method I get violations.
Below is an example. If I run it as it is with form 11 in autocreate it works (I update a button caption in the first form). But, if in unit 10, if I comment out form11.show;, and I uncomment the create and the show and then take Form11 out of autocreate, I get an access violation.
Question - How can I update the parent form from the showed form when I create the form with a create method.
Unit10
unit Unit10;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TForm10 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form10: TForm10;
implementation
uses Unit11;
{$R *.dfm}
procedure TForm10.Button1Click(Sender: TObject);
var
fForm : TForm11;
Begin
// fForm := Form11.Create(Self); //This and next line give me access violation
// fForm.Show; // with form11 out of autocreate
form11.show; //This works with form11 in the autocreate.
end;
end.
Unit11
unit Unit11;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TForm11 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form11: TForm11;
implementation
uses unit10;
{$R *.dfm}
procedure TForm11.Button1Click(Sender: TObject);
begin
form10.button1.caption := 'Changed';
end;
end.
This is incorrect:
fForm := Form11.Create(Self)
It should be like this:
fForm := TForm11.Create(Self)
That is, TForm11, not Form11. To create an object, you have to call the constructor via the class.
I've always just had my forms auto-creating and can't think of a reason to not do so, but here's the likely cause of your problem:
The Create method needs to be called on a class, not on a variable.
This line would probably work to create a new instance of TForm11:
fForm := TForm11.Create(Self);
Related
I did:
procedure TForm1.SpeedButton1Click(Sender: TObject);
begin
DataTable.qOrders.Next;
end;
It works, but the problem is when I click the button to reach the last record, the button is not disabled, like in a TDBNavigator.
How did I make the TSpeedButton disable and enable automatically like the TDBNavigator does?
Drop a TActionList onto your form and add the standard dataset actions to it. Connect these actions to your dataset and your speedbuttons to the appropriate actions. These standard actions will handle the enable state according to the current dataset state.
Here is a simple solution, that works perfectly for me.
I have a form (frmMain), dataset (dsWork), datasource (srcWork), grid and two speedbuttons (btnNext and btnPrior). The important part is in "OnDataChange" event of TDataSource. Here is the source code:
unit MainForm;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, Grids, DBGrids, DB, DBTables, StdCtrls, ExtCtrls;
type
TfrmMain = class(TForm)
btnNext: TButton;
srcWork: TDataSource;
dsWork: TTable;
btnPrior: TButton;
grdWork: TDBGrid;
procedure btnNextClick(Sender: TObject);
procedure btnPriorClick(Sender: TObject);
procedure srcWorkDataChange(Sender: TObject; Field: TField);
private
{ Private declarations }
public
{ Public declarations }
end;
var
frmMain: TfrmMain;
implementation
{$R *.dfm}
procedure TfrmMain.btnNextClick(Sender: TObject);
begin
if not dsWork.Eof then dsWork.Next;
end;
procedure TfrmMain.btnPriorClick(Sender: TObject);
begin
if not dsWork.Bof then dsWork.Prior;
end;
procedure TfrmMain.srcWorkDataChange(Sender: TObject; Field: TField);
begin
btnNext.Enabled := not dsWork.Eof;
btnPrior.Enabled := not dsWork.Bof;
end;
end.
How to create (when I want to show it) and destroy (when I want to hide it) frames on the main TForm? Frames' align = alClient.
I tried this:
The form:
unit main;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, uFrame1, uFrame2;
type
TFormMain = class(TForm)
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
f1: TFrame1;
f2: TFrame2;
end;
var
FormMain: TFormMain;
implementation
{$R *.dfm}
procedure TFormMain.FormCreate(Sender: TObject);
begin
f1 := TFrame1.Create(Self);
f1.Parent := Self;
end;
end.
First frame:
unit uFrame1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls;
type
TFrame1 = class(TFrame)
btn1: TButton;
procedure btn1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
implementation
{$R *.dfm}
uses main, uFrame2;
procedure TFrame1.btn1Click(Sender: TObject);
begin
Self.Free;
FormMain.f2 := TFrame2.Create(FormMain);
FormMain.f2.Parent := FormMain;
end;
end.
Second frame:
unit uFrame2;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls;
type
TFrame2 = class(TFrame)
lbl1: TLabel;
btn1: TButton;
procedure btn1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
implementation
{$R *.dfm}
uses main, uFrame1;
procedure TFrame2.btn1Click(Sender: TObject);
begin
Self.Free;
FormMain.f1 := TFrame1.Create(FormMain);
FormMain.f1.Parent := FormMain;
end;
end.
but it crashes with access vialataions when I click button on FrameStart or Frame1 (TForm FormCreate works fine i.e. it creates and shows FrameStart).
Delphi 7.
You can't call Self.Free in those event handlers. When the event handler returns, the VCL code that executes next still uses a reference to an object that you just freed. And that's where the access violation comes from. If you had been running with FastMM in full debug mode then you would have been shown a helpful diagnostic message.
These frames will have to kill themselves in a more roundabout manner. Post a CM_RELEASE message to the frame asking it to call Free on the frame. You post the message, rather than sending it, so that all the in flight messages are processed first. You'll need to add a message handler to the frame to respond to the message.
You've got some of it.
The basic idea behind this sort of stuff.
add a private property to your mainform to hold the frame.
in the button click handler assuming you only want one at a time do
if assigned(fMyFrame) then
begin
fMyFrame.Free;
fMyFrame := nil;
end;
fMyFrame := TSomeFrame.Create(self);
fMyFrame.Parent := self;
fMyFrame.blah...
When you just want to get rid of it as opposed to replacing it
if assigned(fMyFrame) then
begin
fMyFrame.Free;
fMyFrame := nil;
end;
If you want your frame to raise another frame, repeat the above in there.
If you want the frame you raise in a frame to be a sibling, e.g. have the same Parent, then don't use Form1 var.
fMyNextFrame.Parent = self.Parent;
There's a huge number of ways you could improve on this once you get it working, it's a classic scenario for interfaces and or inheritance, but figure this bit out first.
mySomething := TMySomething.Create();
you can now do something with something.
After you called free, it's not can't, it's don't and don't let anything else either.
Don't do self.free, it's like playing with matches in barrel of petrol. It will hurt....
Error: Types of actual and formal var parameters must be identical
unit unAutoKeypress;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TForm1 = class(TForm)
Memo1: TMemo;
Button1: TButton;
Memo2: TMemo;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure SimulateKeyDown(Key:byte);
begin
keybd_event(Key,0,0,0);
end;
procedure SimulateKeyUp(Key:byte);
begin
keybd_event(Key,0,KEYEVENTF_KEYUP,0);
end;
procedure doKeyPress(var KeyValue:byte);
begin
SimulateKeyDown(KeyValue);
SimulateKeyUp(KeyValue);
end;
procedure TForm1.Button1Click(Sender: TObject);
const test = 'merry Christmas!';
var m: byte;
begin
Memo2.SetFocus();
m:=$13;
doKeyPress(m); // THIS IS WHERE ERROR
end;
end.
always error in function doKeyPress(m);
a simple question, why?
I know something wrong with types, but all types are similar, everywhere is byte, strange
for me and I cant run a program.
TForm is inherited from TWinControl and there is a method called DoKeyPress declared in TWinControl, which is in the current scope of the compiler inside the ButtonClick event.
The problem is that doKeyPress is a method of TForm1 (inherited from TWinControl) and so when you write doKeyPress inside a TForm1 method the compiler wants to use TForm1.doKeyPress rather than the local function. The class scope is nearer than the local function scope.
Possible solutions include:
Renaming the local function to avoid the clash.
Using a fully qualified name, unAutoKeypress.doKeyPress.
The former is a better solution in my opinion.
When a user selects a value in my TDateTimePicker I want to override the to-be-set value to the start of the week that goes with the selected value.
I tried setting it in the OnChange event, but then the originally selected value will be set right after I finished the event.
How would I go about this?
use the "ONCloseUp" event - this sample works for me (Delphi 7, WinXP)
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, ComCtrls, DateUtils, StdCtrls;
type
TForm1 = class(TForm)
dtp1: TDateTimePicker;
btn1: TButton;
edt1: TEdit;
procedure btn1Click(Sender: TObject);
procedure dtp1CloseUp(Sender: TObject);
private
{ Private declarations }
procedure SetDayToMonday();
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.SetDayToMonday;
begin
dtp1.DateTime := dtp1.DateTime - DayOfTheWeek(dtp1.DateTime) + 1;
end;
procedure TForm1.dtp1CloseUp(Sender: TObject);
begin
SetDayToMonday;
end;
end.
--reinhard :-)
Use the onUserInput event!
I would post a message to the form, define a message (WM_USER+1000+X), post it, and handle it. If you don't "pend" it like this, you could also do a PendingDateTimeTimer:TTimer that does validation slightly later (say 10msec) after the OnChange event sets PendingDateTimeTimer.Enabled := true.
Is there a way to disable the parent windows buttons? I have a "working" form that is called by a lot of forms, that I would like to disable the parent form buttons until it's finished doing it's thing. Then turn them back on.
I'd like to do something that is attached to the OnShow event and onClose event.
Thanks
-Brad
Create the form you want to call, as in:
unit fMyModalForm;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs;
type
TfrmMyModalForm = class(TForm)
procedure FormShow(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
private
fCallingForm: TForm;
{ Private declarations }
public
{ Public declarations }
property CallingForm: TForm read fCallingForm write fCallingForm;
end;
(*
var
frmMyModalForm: TfrmMyModalForm;
*)
implementation
{$R *.dfm}
procedure TfrmMyModalForm.FormShow(Sender: TObject);
begin
fCallingForm.Enabled := False;
end;
procedure TfrmMyModalForm.FormClose(Sender: TObject;
var Action: TCloseAction);
begin
fCallingForm.Enabled := True;
end;
end.
Then after the button where you want to call this modal form:
unit fMain;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls,
fMyModalForm;
type
TfrmMain = class(TForm)
btnCall: TButton;
btn1: TButton;
btn2: TButton;
procedure btnCallClick(Sender: TObject);
private
{ Private declarations }
f : TfrmMyModalForm;
public
{ Public declarations }
end;
var
frmMain: TfrmMain;
implementation
{$R *.dfm}
procedure TfrmMain.btnCallClick(Sender: TObject);
begin
if not Assigned(f)
then begin
f := TfrmMyModalForm.Create(Self);
f.CallingForm := Self;
end;
f.Show();
end;
end.
If you just want to disable all buttons you can iterate through them and in stead of disabling the CallingForm only disable the buttons on the CallingForm. See the Stack Overflow topic (and my answer) at :Cast a form dynamically EDITED: or see answer of _J_ (which basically show the topic).
I would use actions in stead of buttons though.
If the secondary window opens, does something and closes, then it would make sense to open it with ShowModal instead of Show, that way the user can't use the main form until the second form has closed.
If you want to iterate though all the buttons and disable or enable them, the code would look something like this:
var
i: Integer;
begin
for i := 0 to MainForm.ComponentCount - 1 do
if (MainForm.Components[i] is TButton) then
TButton(MainForm.Components[i]).Enabled := False;
end;
For stuf like this you will need only 1 line of code and an TActionList component.
Create an actionlist with an action and link the action to the button. An action has an OnUpdate event which lets you determine if the action (and thus the linked button) should be enabled. The OnUpdate event is triggered everytime the action should know if it must be enabled or not.