I am trying to hide the dropdown button in a third party component that derives from TComboBox.
I tried setting the style to csSimple, but this solution does not work for me... There is code in the third party component that checks for csSimple in various places. I would prefer not to change this code.
From what I can see from other posts, others have suggested using a different component, eg, a textbox, or covering the dropdown arrow with something to hide it. I want to avoid these solutions too.
Is there any other way? I was thinking maybe there is a way to do this with PostMessage/SendMessage, but I don't know what to pass as params.
Thank you
Serge Goncharov from AlphaControls came up with a very dark (but operational) solution to this. It involves access to the top two private variables of TDBLookupControl, FDataList and FButtonWidth.
His solution works as follows:
unit Unit2;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, DBCtrls, sDBLookupComboBox, StdCtrls, sCheckBox, XPMan,
sSkinProvider, sSkinManager;
type
TForm1 = class(TForm)
DBLookupComboBox1: TDBLookupComboBox;
CheckBox1: TCheckBox;
procedure CheckBox1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
StoredWidth: integer;
implementation
{$R *.dfm}
type
TAccessLookUpCombo = class(TDBLookupControl)
public
FDataList: TPopupDataList;
FButtonWidth: Integer;
end;
procedure TForm1.CheckBox1Click(Sender: TObject);
begin
if CheckBox1.Checked then
TAccessLookUpCombo(DBLookupComboBox1).FButtonWidth := StoredWidth
else begin
StoredWidth := TAccessLookUpCombo(DBLookupComboBox1).FButtonWidth;
TAccessLookUpCombo(DBLookupComboBox1).FButtonWidth := 0;
end;
DBLookupComboBox1.Invalidate;
end;
end.
With the TAccessLookUpCombo(DBLookupComboBox1) construct, you access the parent's TDBLookupControl top values. Very ugly indeed, but it happens to work.
Related
I have created a listbox and used it to add and remove/load and save items to a file which is all well and good. However when I try to use the listbox on another unit, I get an error: undeclared identifier.
The first Unit below MainUnit is where I am trying to use the listbox and getting the error undeclared identifier. Below the MainUnit is the ManageUsersUnit this is the unit in which the listbox is used and created. All the code works with the listbox on the ManageusersUnit but not on any other unit.
unit MainUnit;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, Menus, ExtCtrls;
implementation
uses AddTenantUnit, HomeUnit, MainMenuUnit;
{$R *.dfm}
{ the error im getting is in this procedure}
procedure TMainForm.FormCreate(Sender: TObject);.
begin
if fileExists('Newuser.dat')
then
begin
UserListBox.Items.LoadFromFile('Newuser.dat');
end
{endif};
end;
unit ManageUsersUnit;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, MainUnit, StdCtrls, Menus, ComCtrls;
type
TManageUsersForm = class(TForm)
AddUserButton: TButton;
RemoveUserButton: TButton;
ChangeUsernameButton: TButton;
ChangePasswordButton: TButton;
HomeButton: TButton;
UserListBox: TListBox;
UsernameEdit: TEdit;
SaveButton: TButton;
PasswordEdit: TEdit;
SubText1Label: TLabel;
SubText2Label: TLabel;
PassListBox: TListBox;
Button1: TButton;
private
{ Private declarations }
public
{ Public declarations }
end;
var
ManageUsersForm: TManageUsersForm;
implementation
uses PassWord, AddUserUnit, HomeUnit;
{$R *.dfm}
procedure TManageUsersForm.HomeButtonClick(Sender: TObject);
begin
HomeForm.show;
ManageUsersForm.Close;
end;
//Add UserName
procedure TManageUsersForm.AddUserButtonClick(Sender: TObject);
var
i : integer;
Found : Boolean;
AddUser : string;
begin
{ManageUsersForm.close;
AddUserForm.ShowModal; }
Found := false;
AddUser := UsernameEdit.Text;
i := 0;
while i < userlistbox.Items.Count do
begin
if (UpperCase(AddUser) = Uppercase(userlistbox.Items[i]))
then
Found := True;
inc(i);
end;
{endhwile};
if Found = False then
begin
userlistbox.Items.Add(AddUser);
UsernameEdit.Text := '';
showMessage(AddUser + ' added');
end
else
showMessage(AddUser + ' is already present.');
{endif};
end;
procedure TManageUsersForm.RemoveUserButtonClick(Sender: TObject);
var
deleted : string;
begin
with UserListBox do
begin
if ItemIndex = -1
then
showMessage('You must select a User first.')
else
begin
Deleted := Items.Strings[ItemIndex];
Items.Delete(ItemIndex);
showMessage(Deleted + ' deleted');
end;
{endif};
end;
end;
procedure TManageUsersForm.Button1Click(Sender: TObject);
var
i : integer;
Found : Boolean;
check : string;
begin
check := PasswordEdit.Text;
Found := false;
i := 0;
while i < userlistbox.Items.Count do
begin
if (UpperCase(check) = Uppercase(userlistbox.Items[i]))
then
Found := True;
inc(i);
end;
{endhwile};
if Found = False
then
begin
showMessage(check + ' Incorrect Username');
end
else
showMessage(' well done in file :).');
{endif};
end;
procedure TManageUsersForm.SaveButtonClick(Sender: TObject);
begin
assignfile (userFile,'Newuser.dat');
UserListbox.Items.SaveToFile('Newuser.dat');
showMessage('Saved to file');
end;
procedure TManageUsersForm.FormCreate(Sender: TObject);
begin
if fileExists('Newuser.dat')
then
begin
UserListBox.Items.LoadFromFile('Newuser.dat');
end
{endif};
end;
procedure TManageUsersForm.TestBtnClick(Sender: TObject);
begin
AddUserForm.ShowModal;
end;
end.
Your MainUnit needs to use the ManageUsersUnit unit. If the only references to the form in that other unit are in the implementation section then add it to the uses clause there. As a general rule you should only add to the interface uses clause if absolutely necessary.
unit MainUnit;
/// ...
implementation
uses AddTenantUnit, HomeUnit, MainMenuUnit, ManageUsersUnit;
Your code then references the UserListBox directly, but this reference is a member variable of the TManageUsersForm class, so you must first identify an instance of that class before you access the members of that instance.
In this case you appear to have a public instance already available which you presumably intend to use: ManageUsersForm
So your code to load the file data into the list should be:
ManageUsersForm.UserListBox.Items.LoadFromFile('Newuser.dat');
This will fix your immediate issue. However, there is a lot wrong with this approach.
MainUnit assumes that ManageUsersForm is a valid, existing form instance. If the form is set to AutoCreate in the project then this may be valid but is dangerous since it makes your code
vulnerable to a change in the way that your forms are created.
MainUnit is inappropriately responsible for some of the behaviour of the ManageUsers form. If the ManageUsers form truly manages users then this should include persistence of users to/from files itself.
MainUnit is highly dependent upon the internal details of the ManageUsers form. We say it is "tightly coupled". If the ManageUsers form is modified to use, for example, a grid to a listview to present users, the MainUnit code will break since it relies on the intimate knowledge that the users list is specifically a listbox.
As a result of the way that the VCL works, the user interface controls on a form in Delphi are publicly accessible, but you should regard these as private (or, at most, protected). Your ManageUsers form should provide a public interface to the functionality that it provides to the "outside world" which deals in terms of user data.
There should be no references to the user interface elements of a form - including any event handlers - outside of the form itself (or descendant form classes if you are using an OO hierarchy of form classes).
I have an object that is created on Form1 and I would like to be able to access one of its fields on Form2. I have tried to google it and nobody can give an answer that I can understand. Please excuse me but I am a novice.
Form1
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
Ttest=class
public
sName:string;
end;
type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
end;
var
Form1: TForm1;
implementation
uses Unit2;
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
var
myObj:Ttest;
begin
myObj.Create;
myObj.sName := 'Name';
Form2.Show;
end;
end.
Form2
unit Unit2;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TForm2 = class(TForm)
Button2: TButton;
procedure Button2Click(Sender: TObject);
end;
var
Form2: TForm2;
implementation
uses Unit1;
{$R *.dfm}
procedure TForm2.Button2Click(Sender: TObject);
begin
ShowMessage(myObj.sName);//This is not working
end;
end.
You have two forms that both use an object. You should define the object in a separate unit and list it in the Uses clause in the Interface section of both forms. Try using something already defined in a main library, like TStringlist, so you don't get confused with this part.
From what you're showing here, you're attempting to create an instance of that object in one form and do something with it in another form. That's a common thing to do: you may have one unit that asks for a filename and loads a file into a TStringList, then hands that over to another form or unit to deal with.
The way you're doing it, however, can be improved to reduce coupling between the two forms.
What you want to do is define a property like this in TForm2:
TForm2 = class( TForm )
. . .
private
Ftestobj : TTest; // or TStringlist
public
property testobj : TTest read Ftestobj write Ftestobj;
Then in TForm1.OnButtonClick do something like this:
form2.testobj := myobj;
form2.Show;
And then this becomes:
procedure TForm2.Button2Click(Sender: TObject);
begin
ShowMessage(Ftestobj.sName);
end;
I did a whole session in CodeRage 9 on this topic recently, in fact. It's entitled, "Have you embraced your inner plumber yet?" and it's all about moving data in and out of forms like this. (I call it plumbing code.)
Search for "coderage 9" and watch the video. At the end is a link where you can download my example code. That should keep you busy for a while. :)
what is most standard and simple way to tell a prevent Delphi program to show ANY message windows when user run the exe?
for example this is my program with a web browser object, when site have errors that Geko component showing errors to user... i want to stop it.
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, OleCtrls, MOZILLACONTROLLib_TLB;
type
TForm1 = class(TForm)
MozillaBrowser1: TMozillaBrowser;
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
uses Unit1;
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
begin
MozillaBrowser1.Navigate('http://www.xeex.ir');
end;
end.
There's no way to do what you ask in general. You cannot apply a setting that will stop all error dialogs being shown.
For native Delphi exceptions, you can choose to ignore them if you wish. Not that that would be a good idea. For message boxes shown by third party code you need that code to offer a way to suppress those errors. If that mechanism exists, you can of course use it. But if no mechanism exists then you are out of luck. And every different library will use separate mechanisms.
I want to access a main form variable from a class that is called from the main from.
Something like this:
Unit1:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs,Unit2, StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
public
end;
var
Form1: TForm1;
Chiled:TChiled;
const
Variable = 'dsadas';
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
begin
Chiled.ShowMainFormVariable;
end;
end.
Unit2:
unit Unit2;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs;
type
TChiled = class
private
public
procedure ShowMainFormVariable;
end;
var
Form1: TForm1;
implementation
procedure TChiled.ShowMainFormVariable;
begin
ShowMessage(Form1.Variable);
end;
end.
if in Unit2 i add to uses Unit1 an circular errors pops up.
How to make the Unit1 to be GLOBAL?
As other answers tell, you should use one of the units in implementation section.
Suppose you chose in 'unit2' you'd use 'unit1' in implementation. then you need to devise a mechanism to tell the 'TChiled' how to access 'Form1'. That's because since you haven't used 'unit1' in interface section of 'unit2', you cannot declare the 'Form1:TForm1' variable in interface section. Below is just one possible solution:
unit2
type
TChiled = class
private
FForm1: TForm;
public
procedure ShowMainFormVariable;
property Form1: TForm write FForm1;
end;
implementation
uses
unit1;
procedure TChild.ShowMainFormVariable;
begin
ShowMessage((FForm1 as TForm1).Variable);
end;
then in unit1 you can set the Form1 property of TChiled before calling TChiled's method:
procedure TForm1.Button1Click(Sender: TObject);
begin
Chiled.Form1 := Self;
Chiled.ShowMainFormVariable;
end;
the simplest solution is to add Unit1 to a uses clause inside Unit2's implementation section as this gets around the circular reference.
However I'd suggest that this design is flawed. It is hard to see what you are trying to achieve with the sample code so it is difficult to offer any real advice.
Well, the simple naive answer is that you should add Unit1 to the uses clause of the implementation section of Unit2:
unit Unit2;
......
implementation
uses
Unit1;
.....
You can't add it to the uses clause in the interface section of Unit2 since that would create a circular reference at the interface section. In order words, the interface of Unit1 would uses Unit2, and the interface of Unit2 would use Unit1. The language does not allow that. The common solution is to use one of the units at the implementation level.
Having said that, your code is rather confused and fails in many other ways. Your problems run deeper than the circular reference. For example, what do you mean by Form1.Variable? The constant Variable is not a member of TForm1. You declare two global variables named Form1 of type TForm1. Why do you do that?
Also, you have spelled child incorrectly.
I generally create a Data Module (or any type of non-visual container) to share global variables. This way both units can use the variable without a circular reference.
Let's say I have two forms in a delphi project, I want to be able to access form1's variables from form2. Is there anyone to declare, say a 'public' variable in form1 which can be read from all forms?
I have tried putting a variable in the public declaration
{ private declarations }
public
{ public declarations }
test: integer;
end;
and in form 2 i have
unit Unit2;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, unit1;
type
{ TForm2 }
TForm2 = class(TForm)
procedure FormClose(Sender: TObject; var CloseAction: TCloseAction);
procedure FormCreate(Sender: TObject);
private
{ private declarations }
public
{ public declarations }
end;
var
Form2: TForm2;
implementation
{$R *.lfm}
{ TForm2 }
procedure TForm2.FormCreate(Sender: TObject);
begin
form1 //<---------- DOES NOT GET RECOGNIZED
end;
end.
I then put 'Unit1' into the uses section on Form2, but it seems I can't do that due to a circular reference. I'd like to refrain from using pointers if possible.
First, it's better to pretend that globals don't exist at all. If you start out programming with the crutch of global variables, you'll just avoid learning the very simple techniques that allow you to do without them. You're worrying about using pointers (which actually wouldn't help your problem at all), but not concerned about using globals which are actually more dangerous.
Globals are dangerous because:
They provide a link between the units that share them (a concept called tight coupling)
This link is hidden in the sense that it's not obvious the exact nature of the link without examining the detail of the code.
Your code will become littered with side effects, which is to say that when you do something in a method, other unexpected things happen as well. This increases the possibility and complexity of subtle bugs.
The cumulative effect of the above points is that even if you break a project down into 20 units, your brain still has to think about all 20 units at the same time - as if they were only1 unit. This defeats the purpose of breaking your project down into smaller manageable units in the first place.
You'll quickly find that even medium sized projects start becoming very difficult to maintain.
Circular References
Please take a look at my answer to a previous question for thoughts on dealing with circular references more generally.
In your case, you simply need to move Unit1 from the interface uses to the implementation uses.
var
Form2: TForm2;
implementation
uses
Unit1;
{$R *.lfm}
{ TForm2 }
procedure TForm2.FormCreate(Sender: TObject);
begin
Form1.test; //Is now accessible, but remember: it will cause a runtime error if Form1 hasn't been created or has already been destroyed.
end;
Sharing data without globals
You'll notice that technically you're still using globals; albeit ones created by Delphi and not your own. Here's a technique you can use to share data in a more controlled fashion.
unit Unit3;
interface
type
TSharedData = class(TObject)
public
Test1: Integer;
Test2: Integer;
end;
Then in Form1 you add the following:
implementation
uses
...
Unit3;
type
TForm1 = class(TForm)
...
public
SharedData: TSharedData;
end;
//Inside either the constructor or OnCreate event add the following line:
SharedData := TSharedData.Create;
//Inside either the destructor or OnDestroyevent add the following line:
SharedData.Free;
Then in Form2 you do something slightly different because you want to use Form1's shared data not its own "shared data".
implementation
uses
...
Unit3;
type
TForm2 = class(TForm)
...
public
Form1SharedData: TSharedData;
end;
//Nothing added to constructor/destructor or OnCreate/OnDestroy events
Finally, after creating both forms, you give Form2 a refernce to Form1's shared data:
procedure RunApplicationWithoutFormGlobals;
var
LForm1: TForm1;
LForm2: TForm2;
begin
Application.Initialize;
Application.MainFormOnTaskbar := True;
Application.CreateForm(TForm1, LForm1);
Application.CreateForm(TForm2, LForm2);
LForm2.Form1SharedData := LForm1.SharedData;
Application.Run;
end;
The above illustrates how easily you can do away with even Delphi's global variables.
Disclaimer: Some of the code goes agaisnt generally accepted encapsulation principles, but is for illustrative purposes only.
First, if you must use globals (it's probably better not to use globals, as Craig has wisely pointed out) then you should put the globals you want to share in SharedGlobals.pas:
unit SharedGlobals;
interface
var
{variables here}
Something:Integer;
implementation
{ nothing here?}
Now use that unit, from the two units you want to share access to that variable in. Alternatively, have both reference another object, which is named something sensible, and have that object be designed, as the holder of state (variable values) that those two instances (forms or classes, or whatever) need to share.
Second idea, since your two units that you have already have dependencies on each other, you could also get around your circular dependency by using the unit that would create a circular dependency, from the implementation section instead of the interface:
unit Unit2;
interface
/// stuff
implementation
uses Unit1;
...
unit Unit1;
interface
/// stuff
implementation
uses Unit2;
the example shows Form1(main) and Form2(other) which has better use for organization;
FORM1 interface declaration only;
can be read from all forms;
interface
procedure oncreate(Sender: TObject);
implementation
uses Form2;
procedure TForm1.oncreate(Sender: TObject);
begin
Form2.test1;
//{Form2.test2} are not visible to Form1;
end;
FORM2 interface and implementation declarations;
implementation declaration are local to the unit; cannot be read from all forms;
interface
procedure test1;
implementation
procedure test2; //declared under implementation;
procedure TForm2.test1;
begin
ShowMessage('form2 test1 message');
end;
procedure TForm2.test2;
begin
ShowMessage('form2 test2 message');
end;
any procedure, function, type, variable can be local to the unit under implementation;
it also makes intellisense(Ctrl+Space) clean from declarations used only to the unit;