In a normal Delphi XE8 VCL Form application, after having put a TTaskbar component on the form, at program start always the following error message box is displayed TWICE:
There are NO OTHER components on the form, so I suppose it is an internal incompatibility.
I do nothing with the TTaskbar component in this test project, it has just been put on the form.
The strange thing is: It worked without error message at program start for about one day. Now always this error message is displayed at program start, even in a new VCL Form project with no other components.
What could be the cause for this error? Could it be a bug in the component?
EDIT: Now this is very strange: When I create the Taskbar component at run-time in a button-click event, then NO error message is displayed and the TTaskbar properties work well:
var
Taskbar1: TTaskbar;
procedure TForm1.btn1Click(Sender: TObject);
begin
Taskbar1 := TTaskbar.Create(Self);
with Taskbar1 do
begin
Name := 'Taskbar1';
//TaskBarButtons := <>; // ??
TabProperties := [];
ProgressState := TTaskBarProgressState.Normal;
ProgressMaxValue := 5;
ProgressValue := 2; // works well
end;
end;
EDIT2: Even more strange: When instead of creating the TTaskbar object in the button-click event I create it in the FormCreate event then 3 error messages are displayed:
var
Taskbar1: TTaskbar;
procedure TForm1.FormCreate(Sender: TObject);
begin
Taskbar1 := TTaskbar.Create(Self);
with Taskbar1 do
begin
Name := 'Taskbar1';
//TaskBarButtons := <>; // ??
TabProperties := [];
end;
end;
This is the first error message displayed while the form is still not visible:
The other two error messages are the same as the first screenshot above and are displayed when the form has become visible.
Related
I've encountered a very strange DBComboBox problem in a master/detail app using Access via ADO. If you have a DBComboBox (.Style=csDropDown) containing a list of items and you enter some text that doesn't exist in the list, the value in the table's DBComboBox field won't appear when navigating back to that record. I've used the DBNavigator.OnClick code below to attempt to resolve this problem but it only works if the first record in the table contains a value not in the list. When you change the value of the DBComboBox in the first record to one that is in the list, no nonconforming items will appear in the DBComboBox text. Has anyone found a solution to this?
procedure TForm1.DBNavigator1Click(Sender: TObject; Button: TNavigateBtn);
var
SavePlace : TBookmark;
begin
if (DBComboBox1.Text='') then begin
SavePlace := TADODataSet(DBNavigator1.DataSource.DataSet).GetBookmark;
TADODataSet(DBNavigator1.DataSource.DataSet).Requery;
TADODataSet(DBNavigator1.DataSource.DataSet).GotoBookMark(SavePlace);
TADODataSet(DBNavigator1.DataSource.DataSet).FreeBookMark(SavePlace);
end;
end;
Unfortunately I don't have XE installed, but I have made a sample project which
reproduces your problem in D7 and Seattle. The code is shown below and I think
you will find that if you follow the exact steps below, it shows that there is something
rather strange going on. Update See the bottom of the answer for a possible work-around, which I think is preferable to the code you quote in your q.
As you'll see, except for Form1 itself, all the components are created at runtime
entirely in code. This is to remove any doubt whether the behaviour is caused
by some obscure property setting (it isn't) and in case you wish to submit it
to EMBA as a bug report. For a similar reason I've used a TClientDataSet so that
the app does not depend on any external data.
Steps (please follow steps 4-7 exactly the first time you try them)
Restart the IDE and create a new project and edit the .Pas file for the main form as shown below. The reason for restarting the IDE is that I discovered that if it has been running for a long time (two days in my case) the details of the misbehaviour
of the app change slightly).
Compile and run.
The app will start with the first from in the DBGrid selected.
Type anything (an 'X' will do) into the DBComboBox, then click the Save toolbutton
on the DBNavigator.
Click the Next (>) toolbutton on the DBNavigator once only. The DBComboBox now displays
'Two'.
Click the Prior (<) toolbutton on the DBNavigator once only. The DBComboBox is now empty.
Click the Prior (<) toolbutton on the DBNavigator once only. The DBComboBox now displays
what you typed in step 4.
Close the app. Most likely the IDE debugger will catch a fault and open the CPU window.
This fault occurs on the line
DestroyWindow(FHandle);
in TApplication.Destroy. I am no Windows internals expert but I think it's likely that this is because of some corruption being caused by whatever causes the blank result in step 6. The fact that
step 7 causes the DBComboBox to correctly display what you typed makes me suspect that cause is actually
in the way the DBComboBox interacts with its FieldDataLink which connects it to the dataset.
Btw, the fact that the fault does not occur if you call DBComboBox1.Free in TForm1's FormDestroy
seems to me to confirm that the fault is related to whatever is causing your problem.
All this, and the fact that it has apparently passed unnoticed in the 25 years of Delphi, seems very strange
to me. This demo app can show up another quirk that's been lurking in the DBGrid for a similar length ot time. To see
it:
Comment out all the references to the DBComboBox and reinstate dgMultiSelect amongst the grid options in the
line that sets them. Compile and run the app.
Click in the cell in the Name column for the first row, type something and save it.
Click the Next toolbutton once. The first row does not de-select itself as it should.
AFAICT (by displaying the DBGrid's count of Bookmarks on the form's caption) this is not
because it has saved a bookmark on the first row.
While I've been writing this, a possible work-around has occurred to me, which I'll updated
this to include if I can get it to work.
Code
type
TForm1 = class(TForm)
procedure FormCreate(Sender : TObject);
private
procedure SetUpDataSet;
procedure SetUpGUI;
protected
public
ClientDataSet1 : TClientDataSet;
DBGrid1: TDBGrid;
DataSource1: TDataSource;
DBNavigator1: TDBNavigator;
DBComboBox1: TDBComboBox;
end;
[...]
procedure TForm1.SetUpGUI;
begin
ClientDataset1 := TClientDataSet.Create(Self);
DataSource1 := TDataSource.Create(Self);
DataSource1.DataSet := ClientDataSet1;
DBGrid1 := TDBGrid.Create(Self);
DBGrid1.Top := 8;
DBGrid1.Left := 8;
DBGrid1.Width := 425;
DBGrid1.Options := [dgEditing, dgTitles, dgColumnResize, dgColLines, dgRowLines, dgTabs, dgConfirmDelete, dgCancelOnExit{, dgMultiSelect}];
DBGrid1.DataSource := DataSource1;
DBGrid1.Parent := Self;
DBNavigator1 := TDBNavigator.Create(Self);
DBNavigator1.DataSource := DataSource1;
DBNavigator1.Top := 144;
DBNavigator1.Left := 16;
DBNavigator1.Parent := Self;
DBComboBox1 := TDBComboBox.Create(Self);
DBComboBox1.DataField := 'Name';
DBComboBox1.DataSource := DataSource1;
DBComboBox1.Top := 240;
DBComboBox1.Left := 16;
DBComboBox1.Parent := Self;
end;
procedure TForm1.SetUpDataSet;
var
Field : TField;
begin
// Create 2 fields in the CDS
Field := TIntegerField.Create(Self);
Field.FieldName := 'ID';
Field.FieldKind := fkData;
Field.DataSet := ClientDataSet1;
Field := TStringField.Create(Self);
Field.FieldName := 'Name';
Field.Size := 40;
Field.FieldKind := fkData;
Field.DataSet := ClientDataSet1;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
SetUpGUI;
SetUpDataSet;
// Set up DBComboBox
DBComboBox1.Style := csDropDown;
DBComboBox1.Items.Add('One');
DBComboBox1.Items.Add('Two');
DBComboBox1.Items.Add('Three');
// Next, set up the CDS
ClientDataSet1.CreateDataSet;
ClientDataSet1.InsertRecord([1, '']);
ClientDataSet1.InsertRecord([2, 'Two']);
ClientDataSet1.InsertRecord([3, '']);
ClientDataSet1.First;
end;
Possible work-around Add the following method to Form1:
procedure TForm1.ClientDataSet1AfterScroll(DataSet: TDataSet);
var
S : String;
begin
S := DataSet.FieldByName('Name').AsString;
if S <> DbComboBox1.Text then
DbComboBox1.Text := S;
Caption := IntToStr(DBGrid1.SelectedRows.Count);
end;
Then, in the SetUpGUI method, add the following immediately after the line where ClientDataSet1 is created:
ClientDataset1.AfterScroll := ClientDataSet1AfterScroll;
I have not tested this thoroughly, but it seems to work in the test conditions of the steps I've described above.
I've created a pop-up loadscreen Form that I want to show above any other form in a Firmonkey Multi device project. Now i've run into the problem that the loadscreen doesn't get updated with the things I do in the background Form. How can I solve this?
In the code below is an example of what i've tried:
procedure TForm1.Button1Click(Sender: TObject);
var
loadScreen:TfrmLoadScreen;
begin
loadScreen := TfrmLoadScreen.Create(nil);
loadScreen.ShowModal(
procedure(ModalResult: TModalResult)
var
i:Integer;
begin
for i := 0 to 200 do
begin
loadScreen.CurrentItem := i;
loadScreen.TextMessage := 'Item:' + loadScreen.CurrentItem.ToString;
Sleep(100);
end;
ModalResult := mrCancel;
end);
end;
I guess I have to do some multi-threading, but I don't have any experience doing this! How should I do this for my loadscreen?
I've also tried the following, but the form doesn't get shown:
procedure TForm1.Button1Click(Sender: TObject);
var
loadScreen:TfrmLoadScreen;
begin
loadScreen := TfrmLoadScreen.Create(nil);
loadScreen.OnShow := FormShowLoadScreen;
loadScreen.Show;
end;
procedure TForm1.FormShowLoadScreen(Sender: TObject);
var
i:Integer;
loadScreen:TfrmLoadScreen;
begin
loadScreen := TfrmLoadScreen(Sender);
for i := 0 to 200 do
begin
loadScreen.CurrentItem := i;
Sleep(100);
end;
loadScreen.Close;
end;
In your first code block, the annonymous method is only called after loadscreen.modalresult is set to something other than 0. This never happens (that we can see)
In your second block, you have 2 different loadscreen instances. They are not the same one. The FormShowLoadScreen handler is called after the firstly loadscreen.show, but it creates a 2nd loadscreen, with it's own displays. In fact, this might happen so fast, you wouldn't see it happen.
You really need to learn more about Delphi multi-threading. To display a "progress" form, you will have to put it's processing (display updates) inside the synchronise event of a separate thread that is started just after the loadscreen form is shown.
Actually... It's actually much easier in FMX to show an animation indicator before starting an annonymous thread, and then hide it again in the thread terminate block.
See Marco Cantu's blog post here Background Operations on Delphi Android, with Threads and Timers
I have run into an issue a couple of weeks ago that appear to have no logical explanation. I'm building an application with Delphi 2007 using AlphaControls and a WebBrowser component placed on a form. The TWebBrowser fetches a banner from the web and displays it into the UI. bad thing is that as soon as the form with the banner is displayed, I get the "Could not obtain OLE Control window handle", while the browser is being displayed outside of the form, in the top left corner of the desktop.
I've been trying basically anything to figure it out, but the debugger does not provide too much information about what's going on (that's all I get: First chance exception at $770C4B32. Exception class EOleError with message 'Could not obtain OLE control window handle'. Process project1.exe (3700)). Funny thing is that the same TWebBrowser on Form1 of a new project works without any issues.
Any thoughts on that would be highly appreciated.
It is caused by the html form being closed. The vendor's forums show some code that will fix the problem.
http://www.bsalsa.com/forum/showthread.php?t=255
Set Cancel to True in the OnWindowClosing event and navigate to an
empty page if it is the main webbrowser. In case your webbrowser is a
popup window, you may want to close the form the EWB is on.
procedure TForm2.EmbeddedWB1WindowClosing(ASender: TObject; IsChildWindow: WordBool; var Cancel: WordBool);
begin
Cancel := True;
(ASender as TEmbeddedWB).GoAboutBlank;
end;
TWebBrowser is still being focused as ActiveControl and TOleControl.HookControlWndProc is being called on a ActiveControl which is no longer in the memory. As a result EOleError exception is raised because the window handle cannot be obtained. You can avoid this by setting ActiveControl to nil (changing the active control focus) prior to shutting down the application.
ActiveControl := nil;
This is the function which causes the exception (OleCtrls.pas):
procedure TOleControl.HookControlWndProc;
var
WndHandle: HWnd;
begin
if (FOleInPlaceObject <> nil) and (WindowHandle = 0) then
begin
WndHandle := 0;
FOleInPlaceObject.GetWindow(WndHandle);
// Exception is raised here because WndHandle could not be obtained
if WndHandle = 0 then raise EOleError.CreateRes(#SNoWindowHandle);
WindowHandle := WndHandle;
DefWndProc := Pointer(GetWindowLong(WindowHandle, GWL_WNDPROC));
CreationControl := Self;
SetWindowLong(WindowHandle, GWL_WNDPROC, Longint(#InitWndProc));
SendMessage(WindowHandle, WM_NULL, 0, 0);
end;
end;
Another way is to trap WM_PARENTNOTIFY message with the parameter WM_DESTROY when the destroy message is being sent to TWebBrowser handle because the parent form (where TWebBrowser is nested in) gets a WM_PARENTNOTIFY message:
procedure ParentNotify(var Msg: TMessage); message WM_PARENTNOTIFY;
implementation of message handler:
procedure TMyForm.ParentNotify(Var Msg: TMessage);
begin
if (Msg.WParamLo = WM_DESTROY) and (Msg.LParam = mywebbrowser.Handle) then close;
end;
I have run into an issue a couple of weeks ago that appear to have no logical explanation. I'm building an application with Delphi 2007 using AlphaControls and a WebBrowser component placed on a form. The TWebBrowser fetches a banner from the web and displays it into the UI. bad thing is that as soon as the form with the banner is displayed, I get the "Could not obtain OLE Control window handle", while the browser is being displayed outside of the form, in the top left corner of the desktop.
I've been trying basically anything to figure it out, but the debugger does not provide too much information about what's going on (that's all I get: First chance exception at $770C4B32. Exception class EOleError with message 'Could not obtain OLE control window handle'. Process project1.exe (3700)). Funny thing is that the same TWebBrowser on Form1 of a new project works without any issues.
Any thoughts on that would be highly appreciated.
It is caused by the html form being closed. The vendor's forums show some code that will fix the problem.
http://www.bsalsa.com/forum/showthread.php?t=255
Set Cancel to True in the OnWindowClosing event and navigate to an
empty page if it is the main webbrowser. In case your webbrowser is a
popup window, you may want to close the form the EWB is on.
procedure TForm2.EmbeddedWB1WindowClosing(ASender: TObject; IsChildWindow: WordBool; var Cancel: WordBool);
begin
Cancel := True;
(ASender as TEmbeddedWB).GoAboutBlank;
end;
TWebBrowser is still being focused as ActiveControl and TOleControl.HookControlWndProc is being called on a ActiveControl which is no longer in the memory. As a result EOleError exception is raised because the window handle cannot be obtained. You can avoid this by setting ActiveControl to nil (changing the active control focus) prior to shutting down the application.
ActiveControl := nil;
This is the function which causes the exception (OleCtrls.pas):
procedure TOleControl.HookControlWndProc;
var
WndHandle: HWnd;
begin
if (FOleInPlaceObject <> nil) and (WindowHandle = 0) then
begin
WndHandle := 0;
FOleInPlaceObject.GetWindow(WndHandle);
// Exception is raised here because WndHandle could not be obtained
if WndHandle = 0 then raise EOleError.CreateRes(#SNoWindowHandle);
WindowHandle := WndHandle;
DefWndProc := Pointer(GetWindowLong(WindowHandle, GWL_WNDPROC));
CreationControl := Self;
SetWindowLong(WindowHandle, GWL_WNDPROC, Longint(#InitWndProc));
SendMessage(WindowHandle, WM_NULL, 0, 0);
end;
end;
Another way is to trap WM_PARENTNOTIFY message with the parameter WM_DESTROY when the destroy message is being sent to TWebBrowser handle because the parent form (where TWebBrowser is nested in) gets a WM_PARENTNOTIFY message:
procedure ParentNotify(var Msg: TMessage); message WM_PARENTNOTIFY;
implementation of message handler:
procedure TMyForm.ParentNotify(Var Msg: TMessage);
begin
if (Msg.WParamLo = WM_DESTROY) and (Msg.LParam = mywebbrowser.Handle) then close;
end;
I have a form one which I want to show a file open dialog box before the full form opens.
I already found that I can't do UI related stuff in FormShow, but it seems that I can in FormActivate (which I protect from being called a second time...)
However, if the user cancels out of the file open dialog, I want to close the form without proceeding.
But, a form close in the activate event handler generates an error that I can't change the visibility of the form.
So how does one do some UI related operation during form start up and then perhaps abort the form (or am I trying to stuff a function into the form that should be in another form?)
TIA
It would be best (i think) to show the file open dialog BEFORE you create and show the form. If you want to keep all code together you might add a public class procedure OpenForm() or something:
class procedure TForm1.OpenForm( ... );
var
O: TOpenDialog;
F: TForm1;
begin
O := TOpenDialog.Create();
try
// set O properties.
if not O.Execute then Exit
F := TForm1.Create( nil );
try
F.Filename := O.FIlename;
F.ShowModal();
finally
F.Free();
end;
finally
O.Free();
end;
end;
Set a variable as a condition of the opendialog and close the form on the formshow event if the flag is not set correctly.
procedure TForm1.FormCreate(Sender: TObject);
begin
ToClose := not OpenDialog1.Execute;
end;
procedure TForm1.FormShow(Sender: TObject);
begin
if ToClose then Close();
end;
or even more simply
procedure TForm1.FormShow(Sender: TObject);
begin
if not OpenDialog1.Execute then Close();
end;
If you want to keep the logic conditioning the opening self-contained in the Form, you can put a TOpenDialog in your Form and use a code like this in your OnShow event:
procedure TForm2.FormShow(Sender: TObject);
begin
if OpenDialog1.Execute(Handle) then
Color := clBlue
else
PostMessage(Handle, WM_CLOSE, 0, 0); // NB: to avoid any visual glitch use AlpaBlend
end;
If you don't need this encapsulation, a better alternative can be to check the condition before trying to show the form, for instance by embedding the Form2.Show call in a function that tests all the required conditions first.
Two Ways....
1. using oncreate and onactivate
create a global flag or even 2
var
aInitialized:boolean;
Set the flag to false in the oncreate handler.
aInitialized := false; //we have not performed our special code yet.
Inside onActivate have something like this
if not aInitialized then
begin
//our one time init code. special stuff or whatever
If successful
then set aInitialized := true
else aInitialized := false
end;
And how to close it without showing anything just add your terminate to the formshow. of course you need to test for some reason to close.. :)
Procedure Tmaindlg.FormShow(Sender: TObject);
Begin
If (shareware1.Sharestatus = ssExpired) or (shareware1.Sharestatus = ssTampered) Then
application.Terminate;
End;
In your DPR you will need to add a splash screen type effect. In my case I am showing progress as the application starts. You could also just show the form and get some data.
Code from the splash.pas
Procedure tsplashform.bumpit(str: string);
Begin
label2.Caption := str;
gauge1.progress := gauge1.progress + trunc(100 / items);
update;
If gauge1.progress >= items * (trunc(100 / items)) Then Close;
End;
Program Billing;
uses
Forms,
main in 'main.pas' {maindlg},
Splash in 'splash.pas' {splashform};
{$R *.RES}
Begin
Application.Initialize;
Application.Title := 'Billing Manager';
SplashForm := TSplashForm.Create(Application);
SplashForm.Show;
SplashForm.Update;
splash.items := 5;
SplashForm.bumpit('Loading Main...');
Application.CreateForm(Tmaindlg, maindlg);
SplashForm.bumpit('Loading Datamodule...');
Application.CreateForm(TfrmSingleWorkorder, frmSingleWorkorder);
SplashForm.bumpit('Loading SQL Builder...');
Application.CreateForm(TDm, Dm);
SplashForm.bumpit('Loading Security...');
Application.CreateForm(TSQLForm, SQLForm);
SplashForm.bumpit('Loading Reports...');
Application.CreateForm(Tpickrptdlg, pickrptdlg);
Application.Run;
End.