I am having an Access Violation when my project tries to open up a separate form using the .Show command.
The code is supposed to open the login form. But just outputs an Access Violation error.
This is the code from the main form, and is run on its activation:
procedure TForm4.FormActivate(Sender: TObject);
begin
label1.BringToFront;
DBMatch.Enabled := false;
DBContestants.Enabled := false;
btncreate.Enabled := false;
DbNav.Enabled := false;
login.Show;
end;
The below code is the start of the login form:
unit login_form;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs ,StdCtrls, ExtCtrls, unit4,pngimage;
type
Tlogin = class(TForm)
edit_username: TEdit;
edit_password: TEdit;
btnlogin: TButton;
btnForgotten: TButton;
lUsername: TLabel;
LPassword: TLabel;
Imageside: TImage;
procedure btnloginClick(Sender: TObject);
procedure FormActivate(Sender: TObject);
private
{ Private declarations }
public
end;
var
login: Tlogin;
password,result:string;
implementation
{$R *.dfm}
uses dmChess_u;
This is one of the errors on the main form
Your Form4 (MainForm) object is being created before the login object is created. Chances are, the TForm.OnActivate event is also being fired before the login object is created, too. Thus, the login pointer would still be nil when you try to call Show on it. You can easily verify that by using the debugger.
A Form's OnActivate event is not a good place to display a login form. That event is fired whenever a Form gains input focus, which can happen many times during the Form's lifetime.
Also, it is generally best to not auto-create your Form objects at program startup, except for the MainForm. Create your Form objects dynamically via their Create constructor only when actually needed, and free them when no longer needed, eg:
login := Tlogin.Create(nil);
try
login.ShowModal;
finally
login.Free;
end;
Alternatively:
login := Tlogin.Create(Self);
login.Show;
...
// OnClose event handler
procedure TloginClose(Sender: TObject; var Action: TCloseAction);
begin
Action := caFree;
end;
However, in this particular case, if you want the user to login before interacting with the MainForm, I would suggest displaying the login form before the MainForm is even created, eg:
Application.Initialize;
login := Tlogin.Create(nil);
try
if login.ShowModal <> mrOk then
Exit;
finally
login.Free;
end;
Application.CreateForm(TForm4, Form4);
Application.Run;
The other answer is really good. I will just add to one of the points. Don't create the form manually. Just create it in the project and then call it when needed. After you've created the form in your project you can do whatever you want on the form interface and code what you need. Then when you need the form, you can have a button on the main form. When the button is clicked make it show the form.
frmLogin.ShowModal;
Make that button invisible. Add this code to your mainForm on create code block, or just manually do it on the button properties by unchecking the visible property.
btnLogin.Visible := false;
Then go to your main form events and go to On Show. Double click and in the code block that appears just write
btnLogin.Click;
This means that before the form shows it will click the existing button. Since the show event is executed after the create event and you already have both forms created in the project, you won't have that problem.
Related
I would like to hide an application from the Windows 7 taskbar.
I want to make something like a toolbar on the edge of the screen which does certain things when the user clicks on it, but I don't want it to show in the taskbar, since its a thing that i want to stay in the background.
I tried the instructions in the following post, but it did not work on my application:
How to hide a taskbar entry but keep the window form
Then i tried it on a new empty VCL Forms Application and it still did not work. I searched for other solutions, but they all do very much the same like in the linked post.
Has something changed, that makes that impossible in windows 7? Or is there anything you
could think of, that could prevent it from working?
You can override the main form's CreateParam to remove the flag that forces the taskbar button (WS_EX_APPWINDOW) and additionally make the form owned by the application window. That's doing the opposite of the requirement for the shell to place a taskbar button for a window. From "Managing Taskbar Buttons":
[..] To ensure that the window button is placed on the taskbar, create an
unowned window with the WS_EX_APPWINDOW extended style. [..]
Sample:
type
TForm1 = class(TForm)
protected
procedure CreateParams(var Params: TCreateParams); override;
end;
procedure TForm1.CreateParams(var Params: TCreateParams);
begin
inherited;
Params.ExStyle := Params.ExStyle and not WS_EX_APPWINDOW;
Params.WndParent := Application.Handle;
end;
Don't change the state of MainFormOnTaskbar property of the 'Application' from its default 'True' if you use this method.
You can also remove the second line (..WndParent := ..) and instead set PopupMode of the form to pmExplicit in the object inspector to same effect.
BTW, here's the documentation quote from the same topic for the solution TLama posted:
To prevent the window button from being placed on the taskbar, [...]
As an alternative, you can create a hidden window and make this hidden
window the owner of your visible window.
When you set MainFormOnTaskbar to false, the main form is owned by the application window by VCL design. And if you hide the application window, the requirement is fulfilled.
Try to use a tricky way described in this article:
Set the MainFormOnTaskBar to False in your project file. Then try to hide the application window from the main form's OnShow and OnActivate event handlers. So your project might look like follows:
Project1.dpr:
program Project1;
uses
Forms,
Unit1 in 'Unit1.pas' {Form1};
{$R *.res}
begin
Application.Initialize;
Application.MainFormOnTaskbar := False;
Application.CreateForm(TForm1, Form1);
Application.Run;
end.
Unit1.pas:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs;
type
TForm1 = class(TForm)
procedure FormShow(Sender: TObject);
procedure FormActivate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.FormShow(Sender: TObject);
begin
ShowWindow(Application.Handle, SW_HIDE);
end;
procedure TForm1.FormActivate(Sender: TObject);
begin
ShowWindow(Application.Handle, SW_HIDE);
end;
end.
your application main form is normally created in the dpr so open the dpr and look for the line that creates the main form.
// add this line first
// blank app title will prevent app from showing in the applications list in task manager
Application.Title := '';
// this line is already in the dpr and creates the main form, the class will differ
Application.CreateForm(TMainForm, Result);
// make the main form invisible to windows taskbar/task switcher
i := GetWindowLong(Application.Handle, GWL_EXSTYLE);
SetWindowLong(Application.Handle, GWL_EXSTYLE, i OR WS_EX_TOOLWINDOW AND NOT WS_EX_APPWINDOW);
i know this works on XP and 7. i'm guessing it's good for 8 as well. this adds the tool window flag and removes the appwindow flag so i guess if you're not interested in the toolwindow flag you can leave out the following part
i OR WS_EX_TOOLWINDOW
I would like to know where is the formshow in delphi 2010 as when I can only see a formcreate in my project.
The reason I am asking is because I need to add Randomize in the FormShow event handler, as shown below:
procedure TfrmWinnaSpree.FormShow(Sender: TObject);
begin
Randomize;
end;
You create the event handler the same way you create almost every event handler in Delphi, by double-clicking the method in the Events tab of the Object Inspector.
Click on the form itself (not any control on the form), then switch to the Object Inspector. Click on the Events tab, and then scroll down to the OnShow event. Double-click in the right half next to the event name, and the IDE will create a new, empty event handler and put the cursor in the right place to start writing code.
procedure TForm3.FormShow(Sender: TObject);
begin
|
end;
However, FormShow is the wrong place to call Randomize, because FormShow executes every time your form is shown, and that can happen more than once. Here's an example (it assumes two forms, Form1 and Form2, autocreated as usual in the .dpr file with the default variable names, which of course is a bad idea - this is to demonstrate a problem with your question's purpose):
procedure TForm2.FormShow(Sender: TObject);
begin
ShowMessage('In FormShow');
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
Form2.Visible := not Form2.Visible;
end;
Run the program and click TForm1.Button1 multiple times; you'll see the In FormShow message every other time you do so.
The proper places for a call to Randomize are:
in your main form's FormCreate
in an initialization section of your main form's unit
unit uMainForm;
interface
...
implementation
...
initialization
Randomize;
end.
in your project source (.dpr) file
program MyGreatApp;
uses
Math,
Vcl.Forms,
uMainForm in 'uMainForm.pas' {Form1};
{$R *.RES}
begin
Randomize;
Application.Initialize;
Application.MainFormOnTaskbar := True;
Application.Title := 'My Super App';
Application.CreateForm(TForm1, Form1);
Application.Run;
end.
Alternatively you can also override the protected method TForm.DoShow:
type
TForm = class(Forms.TForm)
protected
procedure DoShow; override;
end;
implementation
procedure TForm.DoShow;
begin.
// custom show code
inherited;
// custom show code
end;
The advantage over the event-based approach is that you can put your custom code before or after the inherited call.
Say I have a form, with a menu bar on it. I have an item on the menu bar, a TMenuItem, for which I can assign a shortcut key combo, say, for example Ctrl+I. But when I assign the ShortCut property for the TMenuItem, it seems to just change the visual appearance of the menu item to show the shortcut code rather than automatically listening for the short-cut key to be pressed and triggering my ActionManager code.
My google-fu seems to be failing today, I'm only finding articles about how to assign global-hot-keys for windows, not how to assign application-specific hot-keys that only work on the active form.
Can anyone outline for me the steps necessary to add a hot-key beyond just adding the shortcut property in the menu. I'm thinking somewhere I probably need to set the form to be listening for keyboard input and trap the keypress and respond to it? But I'm not exactly sure where or what the Delphi way to do that would be.
You seem to be using Actions (ActionManager), so assign your shortcut to the relevant Action instead. (Assigning the Action to the MenuItem will then assign the shortcut to the menu item, too.)
Consider the example of window handles are not provided for VCL message, for which we use WM_HOTKEY. This message is sent by registered window in Windows hotkey that allows the program to respond to it, even without the input focus:
type
TForm1 = class(TForm)
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
{ Private declarations }
// Declare a event handler
procedure WMHotKey(var Msg: TWMHotKey); message WM_HOTKEY;
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
{ TForm1 }
procedure TForm1.FormCreate(Sender: TObject);
begin
// Registering a hotkey Ctrl+Alt+F5
RegisterHotKey(Handle, 0, MOD_CONTROL or MOD_ALT, VK_F5);
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
// Unregisters a hotkey
UnRegisterHotKey(Handle, 0);
end;
procedure TForm1.WMHotKey(var Msg: TWMHotKey);
begin
// This procedure is called when a window message WM_HOTKEY
inherited; // We give the form to process the message,
// if she already has its handler
Beep; // We perform additional actions
end;
Great to know about that way of WM_HOTKEY, but it is too extreme:
It acts at system level, so if another app is active and on such app you press the hotkey, it is also captured by our app.
It does noy only work when our app is with keyboard focus
The good thing is that it can use hotkeys like "Control" & "+" (VK_ADD = 107) and "Control" & "-" (VK_SUBTRACT = 109).
Buy what i would want is the HotKey only if our Application is active and not affecting any other APP hoytkeys.
Thanks, at least i have a starting point.
I'm stuck on a problem in Delphi 7 about event propagation (due to my ignorance).
I am asked to dynamically attach an OnMouseUp event handler on some controls on a form (and I'm fine with that thing), but if the OnMouseUp is present, the OnClick event on that control must not be processed.
Background
If you are asking the reason behind this, well, I'm in charge to modify an old production monitoring application (sigh) that from now on must accommodate a conditional behaviour for some controls, in direct response to a former click on a special function button.
Some of those controls have an OnClick event handler already; the first solution the team came up with was to punctually intervene on each OnClick handler and manage out the contextual actions in relation to the special function button status.
I suggested to take advantage of the Object-Oriented design already in place for the application forms: they all inherit from the same custom ancestor object, so I planned to insert an initialization method there to dynamically attach OnMouseUp events to the controls that are declared to support it in subclasses.
The need
I'm not hereby asking a validation or questioning on the (possible lack of) design goodness about all this (by the way, after a lot of thinking and reasoning it seemed to be the path we can walk with less pain); my problem is that for such design to take place I'd have to let dynamically-attached OnMouseUp event handlers stop event propagation to the pre-existent OnClick events on those controls.
Is it possible with Delphi 7?
Please note, the following does not explicitly answer the question here. It's more a proposal to the concept re-design (redirect OnClick events instead of adding extra OnMouseUp). It's about how to redirect OnClick event handler (if assigned some) of all components (might be filtered, if needed) to another (common) OnClick event handler. It includes also a method for restoring them to the original state.
In the following example I'll try to show you how to replace and then optionally restore the OnClick event handlers (if the component has written some) by the specific one. This is done to all components having the OnClick event published, so you don't need to know in advance if the component class has OnClick event available or not (but it can very simply be modified to use only a specific class).
The code consists from the following:
OnSpecialClick - it is the event handler to what all OnClick events will be binded when you call the ReplaceOnClickEvents procedure, notice that it must be published to be visible for RTTI !!!
Button1Click - represents here the old event handler which should be replaced, it is binded to the Button1.OnClick event at design time
ReplaceOnClickEvents - method, which iterates through all components on the form and checks if the currently iterated one has the OnClick event handler assigned; if so, it stores it into a backup collection and replace this event handler by the OnSpecialClick
RestoreOnClickEvents - method, which restores the original OnClick event handlers; it iterates through the backup collection and assign the event methods to its stored component instances
CheckBox1Click - this check box click event is meant to be the switch between the common and a special mode (CheckBox1 checked state means to be the special mode), only this OnClick event is not replaced by the ReplaceOnClickEvents call (because you wouldn't be able to restore the mode back to normal)
And here it is:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, TypInfo, StdCtrls, Contnrs;
type
TEventBackup = class
Component: TComponent;
OnClickMethod: TMethod;
end;
type
TForm1 = class(TForm)
Button1: TButton;
CheckBox1: TCheckBox;
procedure Button1Click(Sender: TObject);
procedure CheckBox1Click(Sender: TObject);
private
procedure ReplaceOnClickEvents;
procedure RestoreOnClickEvents;
published
procedure OnSpecialClick(Sender: TObject);
end;
var
Form1: TForm1;
EventBackupList: TObjectList;
implementation
{$R *.dfm}
procedure TForm1.OnSpecialClick(Sender: TObject);
begin
ShowMessage('Hi, I''m an OnSpecialClick event message!');
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
ShowMessage('Hi, I''m just that boring original OnClick event message!');
end;
procedure TForm1.ReplaceOnClickEvents;
var
I: Integer;
Component: TComponent;
EventMethod: TMethod;
EventBackup: TEventBackup;
begin
for I := 0 to ComponentCount - 1 do
begin
Component := Components[I];
if Component = CheckBox1 then
Continue;
if IsPublishedProp(Component, 'OnClick') then
begin
EventMethod := GetMethodProp(Component, 'OnClick');
if Assigned(EventMethod.Code) and Assigned(EventMethod.Data) then
begin
EventBackup := TEventBackup.Create;
EventBackup.Component := Component;
EventBackup.OnClickMethod := EventMethod;
EventBackupList.Add(EventBackup);
EventMethod.Code := MethodAddress('OnSpecialClick');
EventMethod.Data := Pointer(Self);
SetMethodProp(Component, 'OnClick', EventMethod);
end;
end;
end;
end;
procedure TForm1.RestoreOnClickEvents;
var
I: Integer;
EventBackup: TEventBackup;
begin
for I := 0 to EventBackupList.Count - 1 do
begin
EventBackup := TEventBackup(EventBackupList[I]);
SetMethodProp(EventBackup.Component, 'OnClick', EventBackup.OnClickMethod);
end;
EventBackupList.Clear;
end;
procedure TForm1.CheckBox1Click(Sender: TObject);
begin
if CheckBox1.Checked then
ReplaceOnClickEvents
else
RestoreOnClickEvents;
end;
initialization
EventBackupList := TObjectList.Create;
EventBackupList.OwnsObjects := True;
finalization
EventBackupList.Free;
end.
As both TLama and TOndrej have said, there are a few ways to accomplish what you're attempting:
To do if Assigned(Control.OnMouseUp) then Exit; on your OnClick event handler
To "unassign" the OnClick event when assigning OnMouseUp (and vice-versa)
Both approaches will accomplish what you've detailed, though "unassigning" the OnClick event will be best for performance (to an infintismally small extent) since you won't be performing the if statement repeatedly.
As I typed this question I realize that it probably should.
Docking a form to a TPageControl calls FormShow when form.Create() is called and when form.ManualDock(pagecontrol,pagecontrol.alClient) is called.
Un-docking the form also calls show and I assume this is because the form is actually 'reset' when you dock/undock?
If this is as designed I'll just refactor the code I dont want to fire there to onCreate (unless that is bad design).
If should or not is more philosophical than technical question. The TForm.OnShow event is fired by performing control message CM_DOCKCLIENT which is used also by the ManualDock function. Internally this message calls the CM_SHOWINGCHANGED what fires the event itself.
In the following example I will use two forms, Form1 (with a page control and a button) and Form2 (empty and dockable). I presume that both are auto created.
The following code is a proof that the OnShow event is fired by the CM_DOCKCLIENT control message. Clicking on the button, the CM_DOCKCLIENT message is performed and Form2's OnShow event is fired.
Code for Form1
procedure TForm1.FormShow(Sender: TObject);
begin
Form2.Show;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
DockObject: TDragDockObject;
begin
DockObject := TDragDockObject.Create(Form2);
try
// sending the CM_DOCKCLIENT message internally performs also the
// CM_SHOWINGCHANGED message which triggers the TForm.OnShow event
PageControl1.Perform(CM_DOCKCLIENT, WPARAM(DockObject), LPARAM(SmallPoint(0, 0)));
finally
DockObject.Free;
end;
end;
And Form2 has only the OnShow event handler
procedure TForm2.FormShow(Sender: TObject);
begin
ShowMessage('FormShow');
end;
An easy workaround is not to dock the Form2 manually by its own (in the OnShow event) but dock it by the creator or let's say by the form which displays it. In my previous example I've displayed the Form2 in the Form1.OnShow event, so I can easily dock it manually there.
procedure TForm1.FormShow(Sender: TObject);
begin
Form2.Show;
Form2.ManualDock(PageControl1);
end;
procedure TForm2.FormShow(Sender: TObject);
begin
// no manual docking of this form by itself
end;