Save and Load Checklistbox checked items to .ini file - delphi

I want to save to .ini and after fill checklistbox items from the saved ini file. I have 2 listboxes like...
First listbox contains tables:
Cars
Customers
Users
Suppliers
The second listbox links to the first, if I click on Cars table and checked it the following datas will be visible on the second checklistbox...
Second listbox contains table fields:
CARS
- Car_ID
- Car_Name
- Car_LicNum
- Car_Color etc..
USERS
User_ID
User_Name
User_Password etc...
Anyone can help me how can I save all the checked items (from checklistbox 1 and checklistbox2) to .ini file? And after how can I load and fill the checklistboxes with them?
I did for the first checklistbox but...
procedure TForm1.btn_SaveClick(Sender: TObject);
begin
ini := TIniFile.Create('C:\checklistbox.ini');
try
for i := 0 to Checklistbox1.Items.Count - 1 do
ini.WriteBool('items', Checklistbox1.Items[i], Checklistbox1.Checked[i]);
finally
ini.Free;
end;
end;
Loading items to checklistbox1
procedure TForm1.btn_LoadClick(Sender: TObject);
begin
ini := TIniFile.Create('c:\checklistbox.ini');
try
ini.ReadSection('items', Checklistbox1.Items);
for i := 0 to Checklistbox1.Items.Count - 1 do
CheckListbox1.Checked[i] := ini.ReadBool('items', Checklistbox1.Items[i], False);
finally
ini.Free;
end;
end;
I dont know how can I save items from checklistbox2 which items links to checklistbox1 items. I want to load all the checked items after. I am using Delphi XE7 at the moment. Thanks for the answers!

I guess your problem is getting your head around the fact that the contents of the second list box change, and so the risk of error is quite high. I agree with that and so the answer is to ignore the list boxes themselves and focus on what they represent, and so store the data that the user wants to see - and in this case I would use field names for that - so
ini.WriteString('File To View', 'Name', 'Cars');
and for the fields
ini.WriteInteger('Cars', 'Count', 2);
ini.WriteString('Cars', 'Field 1', 'Cars_ID');
ini.WriteString('Cars', 'Field 2', 'Car_LICNUM');
I guess that you only allow one box in the first checkbox to be checked. If that were not true, or later became not true, you would add count and 'Name x' parameters like this
ini.WriteInteger('File To View', 'Count', 2);
ini.WriteString('File To View', 'Name 1', 'Cars');
ini.WriteString('File To View', 'Name 2', 'Users');
So changing your GUI later becomes easy, as does making your new program backwards compatible. This is the point LU RD makes about basing your INI file on your business model and not your GUI.
Note also the fact that you may store multiple sections - one for each file in fact, but that doesn't really matter and has the hidden benefit that the INI file 'remembers' the users last choice of fields for each file.

Related

How to get value of TDBLookupComboBox?

I have tables on my database Tb_barang and Tb_jenis. Tb_jenis the following columns kd_jenis (primary key) and jenis. I use TDBLookupComboBox to show the items from the Tb_jenis table. There are 6 items (Mie, Susu, etc). I want to save item selected as kd_jenis not jenis.
How to I save jenis in table to kd_jenis?
Sample data: Tb_jenis
jenis kd_jenis
Mie J1
Susu J2
Here it the code I've tried.
if (kd.Text='') or (jenis.Text='Pilih') then
ShowMessage('Data Tidak Lengkap, Silakkan Dilengkapi !')
else
begin
with dm.Qbarang do
begin
sql.Clear;
SQL.Add('select * from Tb_barang where kd_barang='+quotedstr(kd.text)+'');
open;
end;
if DM.Qbarang.Recordset.RecordCount > 0 then
ShowMessage('Data Sudah Ada, Silakkan Isi Yang Lain!')
else
begin
try
DM.koneksi.BeginTrans;
with DM.QU do
begin
close;
SQL.Clear;
SQL.Add('insert into Tb_barang values('+QuotedStr(kd.Text)+','
+QuotedStr(jenis.Text)+')');
ExecSQL;
end;
DM.koneksi.CommitTrans;
ShowMessage('Data Berhasil Disimpan');
except
DM.Qbarang.Close;
DM.Qbarang.Open;
FormShow(sender);
end;
end;
end;
As i understand you want to get Key Value of table from DBLookupComboBox.
so in it is 2 important property in DBLookupComboBox ListField and KeyField
if you set them correctly For Example KeyField set as kd_jenis and List Field Set as jenis then you will see Jenis on List and you can access to jenis in DBLookupCombobox.text and also you can access to KeyField (in this case kd_jenis) by DBLookupCombobox.KeyValue
you should fallow this steps.
Put a TADOConnection to your form for connecting to a database.
build connection string for connecting to Database.
add a ADO table to your form.
Set Connection property to AdoConnection1(Default Name Of ADOCOnnection).
On ADOTable open the Table name Property and select One of your database table and then set active property as true.
Add a DataSource to your form and set Dataset Property as ADOTable1(Default Name of ADOTable)
Add DBLookupCombobox to your form and Set ListSource as DataSource1(the default name of datasource)
Open ListField Property and Select witch field do you want to show in Combobox.
Open the keyField Property and Select witch field is Key field.
All of above Steps should done at Design time. for testing your application add to edit box on your form as edit1 and edit2 then write a small code for OnCloseUp Evwnt of DBLookupCombobox like this
Edit1.Text:=DBLookUpCombobox1.KeyValue;
Edit2.Text:=DBLookupCombobox1.Text;
If you have problems with DBLookupComboBox you may use normal combobox and fill it like the example here.
After someone selects something in your DBLookupComboBox...The cursor to the table is moved.
You can access the value directly:
jenis.ListSource.DataSet.FieldByName('kd_jenis').AsString
This assumes that jenis is a TDBLookupComboBox.
That ListSource.DataSet points to Tb_jenis.
As Craig stated...you have a serious bug in your code...you need a RollbackTrans on exception...other wise you will never release the lock...
DM.koneksi.CommitTrans;
ShowMessage('Data Berhasil Disimpan');
except
DM.koneksi.RollbackTrans;
DM.Qbarang.Close;
DM.Qbarang.Open;
FormShow(sender);
end;
I do something like this...if I need to guarantee saving info and rolling back if fails.
procedure TForm8.SaveData;
begin
Assert(not ADOQuery1.Connection.InTransaction, 'Code problem-We should not be in a Transaction');
ADOQuery1.Connection.BeginTrans;
try
ADOQuery1.ExecSQL;
ADOQuery1.Connection.CommitTrans;
finally
if ADOQuery1.Connection.InTransaction then
begin
{If you are here...your in an ErrorState...and didn't Commit your Transaction}
ADOQuery1.Connection.RollbackTrans;
HandleError(ADOQuery1);
end;
end;
end;

How can I use a DBLookupComboBox to select a foreign key value without immediately changing the target table? (Delphi 5 Pro)

Basic Scenario:
User clicks 'edit item'
Edit dialog opens
A combobox 'item type' should be populated with items form a table 'item_type'. The combobox should display 'item_type.name', but also know about 'item_type.id'
User edits other item stuff and selects an item type, then clicks ok
My program does some input validation
if everything is ok, get the 'item_type.id' from the selected combo item and save it to the foreign key column of the item table ('item.fk_item_type').
If I understand this component correctly, I should set DataSource to point to the destination table 'item' and DataField to 'item.fk_item_type'. But that would immediately edit my item table before I get a chance the validate all the other input.
It feels like I am missing something here. Somewhere I read that I need to use a classic ComboBox and fill it manually. But then I don't understand how to get to the id of the selected item.
Thank you for any pointers.
Edit:
I am starting to suspect that maybe I am missing a fundamental thing. All these DB* components, do they load values from that database automatically, but I have to call Post() myself? Meaning they do not automatically change values in the database?
If I understand you correctly, you want to use a DBLookupComboBox. You have to supply values for the following properties
datasource - linked to the table which you are editing, presumably 'items'
datafield - the name of the field in the table which you are editing, presumably 'item_type'
listsource - linked to the table which populated the combobox, presumably 'item_types'
list field - the name of the field from 'item_types' which you want to display, presumably 'name'
key field - the name of the field from 'item_types' which will be inserted into the items record, presumably 'item_type'
The table which populated the combobox is never edited.
You can validate the values before posting the new/edited 'items' record.
I can show you how to use a non-data aware combobox if necessary, but it's easier to use the data aware version.
Regarding validation, I use the following code template in edit dialogs.
Function TEditQuestion.Execute (n: longint): boolean;
var
gen: longint;
begin
sdsEditQuestion.params[0].asinteger:= n; // TSQLDataSet
with qEditQuestion do // TClientDataSet
begin
open;
if n = -1 then // new record
begin
caption:= 'New record';
insert;
fieldbyname ('alive').asinteger:= 1;
// initialise necessary fields in the dataset
end
else caption:= 'Editing record ' + inttostr (n);
edit;
if showmodal = mrOK then
begin
// validation code comes here. Set 'result' to true if everything is ok
if result then
begin
if n = -1 then
begin
with qGenID do
begin
open;
gen:= fields[0].asinteger; // get new value from generator
close
end;
FieldByName ('id').asinteger:= gen;
end;
post;
applyupdates (0)
end
else cancel // showmodal = OK, result = false
else // showmodal cancelled
begin
cancel;
result:= false
end;
end // with qEditQuestion
end;

Tlistview - There is any component like Tlistview but with DB access?

I've been trying to make a creative thing to avoid the dbgrids, and i've found the Tlistview (using the one from alphaskins, tslistview), and seems to be a nice way!
The problem is, I don't want to code the event onclick on every tlistview to position a record/dataset according to the item I selected on the tlistview .. and I'm doing it with the tlistview item's caption.. and there could be records with the same names
Here is one of the codes I want to avoid:
with q_find_process do
begin
close;
sql.Clear;
sql.Add('Select * from t_process where process_name like '+quotedstr(streeview1.Selected.Text)+');
open;
end;
And no, I don't want to put the ID of the Record on the item caption..!
Any ideas?
Does anyone know other way of showing a lot of records without being only text text and more text? I don't know all components on the tool palette, maybe someone could suggest me other one..
I have sometimes used listviews which have been loaded from database tables - only for small amounts of data. I don't understand what you mean by I don't want to code the event onclick on every tlistview to position a record/dataset according to the item I selected on the tlistview, so I'm going to show you how I solved this problem.
Basically, I create a sub-item which holds the primary key of each record. All the user interface code uses two list views, and at the end, the database is updated. There is no interaction with the database between loading and storing (which might be where I avoid your 'onclick' problem). The widths of each fields are set in the Object Inspector; the final subitem's width is 0 (ie not displayed).
Loading the list view:
srclist.items.clear;
with qSrcList do
begin
close;
params[0].asdate:= dt; // use date of deposit
open;
while not eof do
begin
ListItem:= srclist.Items.Add;
ListItem.Caption:= fieldbyname ('kabnum').asstring;
ListItem.SubItems.Add (fieldbyname ('price').asstring);
ListItem.SubItems.Add (fieldbyname ('duedate').asstring);
ListItem.SubItems.Add (fieldbyname ('docket').asstring);
ListItem.SubItems.Add (fieldbyname ('id').asstring);
next
end;
close
end;
Saving data:
with dstlist do
for index:= 1 to items.count do
with qInsert do
begin
dstlist.itemindex:= index - 1;
lvitem:= dstlist.selected;
parambyname ('p1').asinteger:= deposit;
parambyname ('p2').asinteger:= strtoint (lvitem.SubItems[3]);
parambyname ('p3').asfloat:= strtofloat (lvitem.SubItems[0]);
execsql;
end;
I hope that this helps you. The context of this code (not that it matters too much) is in a financial application where the user wishes to populate a bank deposit form with cheques. SrcList holds the cheques which have yet to be deposited (there will only be a few per given date) and DstList holds the cheques which have already been connected to a given deposit form.

Looping all components on a TabSheet

I have the following code which shoul loop all components on a given tab on my tabsheet.
I have tried many variants of the same code found on the net, but I simply can't get it to work.
First I check if it is the right tab - that works.
Then I check to see how many components - that doesn't work. It says 0 component even though I now that there are 2 panels with 9 checkboxes total.
procedure TfrmHsUsers.pagUsersClick(Sender: TObject);
var
i: integer;
Fieldname: string;
begin
if pagUsers.Properties.ActivePage.Name = 'tabProgram' then
begin
ShowMessage(IntToStr(pagUsers.Properties.ActivePage.ComponentCount));
for i := 0 to pagUsers.Properties.ActivePage.ComponentCount - 1 do
if (pagUsers.Properties.ActivePage.Components[i]) is TcxDbCheckBox then
begin
Fieldname := TcxDbCheckBox(pagUsers.Properties.ActivePage.Components[i]).DataBinding.DataField;
TcxDbCheckBox(pagUsers.Properties.ActivePage.Components[i]).Enabled := Settings.License.IsEnabled(Fieldname);
end;
end;
end;
Any hints to what might be wrong in my code?
What's wrong is that you are looping over the Components property. That lists the components that are owned by the tab sheet. For components created in the form designer, the form is the owner. So it is expected that pagUsers.Properties.ActivePage.ComponentCount is zero since the only thing on your form that owns anything is the form itself.
What you need to to is use ControlCount and Controls[] to iterate over the children of the tab sheet. Simply replace all use of ComponentCount with ControlCount, and likewise replace Components[] with Controls[].
Note that the ControlCount and Controls[] properties only give the immediate children. Since you have panels you most likely have the panels as children of the tab sheet, and the check boxes as children of the panels. So you need to iterate over the children of the panels.
My answer here shows one way to do that. If you use the code I presented there then your iteration over checkboxes can be written very simply indeed:
TabSheet := pagUsers.Properties.ActivePage;
for CheckBox in TControls.Enumerator<TcxDbCheckBox>(TabSheet) do
....

Profile Manager with Combobox and TIniFile

I'm currently trying to create a "Profile Manager" using a TIniFile to store the data, and displaying the data in various components on a form (editboxes and such).
On the form, i've got a Combobox. This serves as a way of displaying the "Profile Name" as set by the user.
The data is being stored in the format of 1 profile per inifile section. Each section contains the configuration data for 1 profile including the Profile Name. The profile name key is the same across each section. This is the sort of layout i've currently got in the inifile (as an example);
[0]
PROFILE_NAME=Profile 1A
PROFILE_DATA=Profile Data 1A
PROFILE_PASS=Profile Password 1
PROFILE_USER=Profile Username 1
[1]
PROFILE_NAME=Profile 1B
PROFILE_DATA=Profile Data 1B
PROFILE_PASS=Profile Password 1B
PROFILE_USER=Profile Username 1B
What i want to do is load a list of all values with the key "PROFILE_NAME" into a combobox regardless of what section they're located in. The section names themselves are references to the itemindex in the combobox when that data was added.
From there, i can handle loading the other data into it's relevant fields, but i'm having a problem figuring out how to load the "PROFILE_NAME" values into the combobox. Any ideas?
For those familiar with the Voice Communication program "Ventrilo", it features something similar to what i'm trying to achieve with it's "Server and User Manager". It's inifile layout is very similar, and the only difference i can find is that it has a "USER_COUNT" value referencing how many users have been added. Each user has servers assigned to them, rather than the servers being accessible by every user.
Is it possible for me to achieve this?
You have to use TIniFile.ReadSections to get a list of all the section names, and then you can loop through them and read the individual PROFILE_NAME from each of those sections. (I prefer TMemIniFile, as TIniFile is based on the WinAPI functions directly and has issues sometimes on network drives when trying to update with new values. TMemIniFile also works cross-platform when you get to XE2.)
I'm creating the TMemIniFile and TStringList and freeing them, but if you're using them repeatedly you'll probably want to create them in your form's OnCreate and free them in FormClose instead; that way you'll have a list of the section names to match back to the items in the ComboBox when you want to access the rest of the items in the OnClick event to populate the rest of the form.
var
Sections: TStringList;
Ini: TMemIniFile;
s: string;
begin
Sections := TStringList.Create;
try
Ini := TMemIniFile.Create('YourIniFile.ini');
try
Ini.ReadSections(Sections);
for s in Sections do
ComboBox1.Items.Add(Ini.ReadString(s, `PROFILE_NAME`, `Empty`);
finally
Ini.Free;
end;
finally
Sections.Free;
end;
end;
To make it easier to tie back to the items in the ComboBox, declare a new integer variable (i in my snippet below), and change the for loop to this (make sure you don't sort the Sections - let the ComboBox handle the sorting!):
for i := 0 to Sections.Count - 1 do
begin
s := Ini.ReadString(Sections[i], 'PROFILE_NAME', 'Empty');
ComboBox1.Items.AddObject(s, TObject(i));
end;
To get the section name again when the user clicks a combobox item:
procedure TForm1.ComboBox1Click(Sender: TObject);
var
i: Integer;
SectionName: string;
begin
// Get the Sections item index we stored above
i := Integer(ComboBox1.Items.Objects[ComboBox1.ItemIndex]));
// Get the associated Sections section name
SectionName := Sections[i];
// Use the retrieved section name to get the rest of the values
ProfileNameEdit.Text := Ini.ReadString(SectionName, 'PROFILE_NAME', '');
ProfileDataEdit.Text := Ini.ReadString(SectionName, 'PROFILE_DATA', ''); // etc
end;

Resources