Work With checked listBox - delphi

hello
i want implement a algorithm that uses checked listbox
what i need is uses checked items data and index from begin
how can I do it?

I am not sure whether you want this or not.
You must use CheckListBox1.Checked[i] function
This code will return the checked items in the TCheckListBox along with its index.
for I := 0 to CheckListBox1.Items.Count - 1 do
begin
if CheckListBox1.Checked[i] then
begin
ShowMessage('Item at index ' + IntToStr(i) + ' is selected.' +
'Its value is '+ CheckListBox1.Items.Strings[i]);
end;
end;

Related

How to get ALL selected items from ListBox into an EditBox?

I am experimenting (for the first time) with using a ListBox instead of a ComboBox to select strings, which will then populate an Edit box that will be used to build an SQL string.
I have the ListBox set to MultiSelect and ExtendedSelect both set to True. However, when I run the code below, I am only getting a single selected item from the ListBox to the Edit box.
I have search and searched for the last 48 hours for help, but nothing works. What am I missing or doing wrong?
procedure TfrmQuery.lbSelectExit(Sender: TObject);
var
i : integer;
begin
for i := 0 to lbSelect.SelCount -1 do
begin
if lbselect.selected[i] then
begin
edQryScript.Text := 'Select ' + lbSelect.items.Strings[i] + ' from ';
end;
end;
end;
I understand that you select the fields that must appear in the SELECT clause from the ListBox and then compose the SELECT statement.
You are going through all the selected items, but you are only putting one each time inside of the SELECT and then, with the next iteration, you lose the previous information.
As #amigoJack mentions, I guess it is lbSelected.Count and not lbSelected.SelCount that would stop iterating when it has gone through the number of selected items and not the total number of items, among which it still has to decide whether it is selected or not (if lbselect.selected[i] then ...).
Try something like this:
procedure TfrmQuery.lbSelectExit(Sender: TObject);
var
i : integer;
begin
for i := 0 to lbSelect.Count -1 do
begin
if lbselect.selected[i] then
begin
if edQryScript.Text <> '' then
edQryScript.Text := edQryScript.Text + ', ';
edQryScript.Text := edQryScript.Text + lbSelect.items.Strings[i];
end;
edQryScript.Text := 'Select ' + edQryScript.Text + ' from ';
end;
end;
So that every iteration adds the field to what was previously in edQryScript.Text. It is easier to bother about the Select ... from when we already have all the fields to the select statement.

Delphi Outlook Contact Automation - Iterate thru ALL properties of a ContactItem

I am currently writing a small program to extract ALL data elements of my Outlook contacts to a new CRM database. I have imported the necessary type library and came up with the following code (It's almost there!!!)
`
Contacts := NameSpace.GetDefaultFolder(olFolderContacts);
for i := 1 to Contacts.Items.Count do
begin
Contact := Contacts.Items.Item(i);
{now you can read any property of contact. For example, full name and
email address}
// ShowMessage(Contact.FullName + ' <' + Contact.Email1Address + '>');
Memo1.Lines.Add(Contact.FullName + ' <' + Contact.Email1Address + '>');
Memo1.Lines.Add(IntToStr(Contact.ItemProperties.Count) + '>');
for j := 0 to Contact.ItemProperties.Count -1 do
if Contact.ItemProperties.Item(j) <> null then
Memo1.Lines.Add(Contact.ItemProperties.Item(j));
end;
Iterating thru the contacts works OK (I see them in the Memo). My problem is the 2nd "for" loop in trying to check ALL 168 fields of the contact (the 168 shows in the memo too)
Can anyone help shed some light on iterating thru each Property of each contact item? I have found some answers but I need/want to drill down further.
Thanks in advance
Bill
Your comparison to null in that way might be the problem. Try:
for j := 0 to Contact.ItemProperties.Count -1 do
Memo1.Lines.Add(Contact.ItemProperties.Item(j).Name);
or even:
for j := 0 to Contact.ItemProperties.Count -1 do
if Contact.ItemProperties.Item(j).Name <> null then
Memo1.Lines.Add(Contact.ItemProperties.Item(j).Name);
I am not sure but you can even try to access the items in ItemProperties like:
Contact.ItemProperties[j].Name
OK, after spending some quality time in the TLB for office I was able to walk the properties of each contact whether useful or not. The RTFBody property had to be converted from Unicode to ANSI WideString, the attachments as a collection (count was there so easy enough to iterate), and dispatch values I just bypassed. Learned a lot, MSDN Office Interop online docs provided good help in handling field types. The long handed dot notation was necessary because I could not seem to get grouping to work but stepping down each level worked just fine. The important parts follow. As always thanks for the input.
Memo1.Lines.Add(IntToStr(Contacts.Items.Count) + '>');
for i := 1 to Contacts.Items.Count do
begin
Contact := Contacts.Items.Item(i);
{now you can read any property of contact. For example, full name and
email address}
for j := 0 to Contact.ItemProperties.Count -1 do
begin
ContactProperty := Contact.ItemProperties.Item(j);
if not VarIsNull(ContactProperty.Value) then
if ContactProperty.Name = 'RTFBody' then
begin
RTFByte := ContactProperty.Value;
SetString(tANSIStr, PAnsiChar(#RTFByte[0]), Length (ContactProperty.Value));
Memo1.Lines.Add(ContactProperty.Name + ' <' + tANSIstr + '>');
RichEdit1.Lines.Add(tANSIstr);
end
else
if ContactProperty.Name = 'Attachments' then
begin
Attachments := Contact.Attachments;
for k := 1 to Attachments.Count do
begin
ContactAttachment := Attachments.Item(k);
Memo1.Lines.Add('Attachment.FileName <' + ContactAttachment.FileName + '>');
end;
end
end;

Load dataset / query faster

I have an application when at the click of a button all records from a database table get loaded into a cxGrid (via tadquery / datasource). The number of records will increase as users insert them.
I was just wondering if there is anything I need to do or should do to make sure that it doesn't take too long to load all records.
This is what I'm doing when button is clicked:
with query1 do
begin
Close;
SQL.Clear;
SQL.Text := 'SELECT * FROM DBA.Table1;
Open;
end;
Suggestions would be appreciated.
It's not a particularly rigorous test, obviously, but with my test app below, setting GridMode to True results in the dataset being opened and the grid populated in about 125ms (for 7k 22-column rows) compared to 900ms with it set to False.
procedure TForm1.Button1Click(Sender: TObject);
var
T1 : Integer;
begin
if qNames.Active then
qNames.Close;
T1 := GetTickCount;
cxGrid1DBTableView1.DataController.DataModeController.GridMode := CheckBox1.Checked;
qNames.Open;
Caption := IntToStr(qNames.RecordCount) + ':' + IntToStr(GetTickCount - T1) + ':' + IntToStr(qNames.FieldCount);
end;
This is with everything in the cxGrid set to their defaults except that I've set the KeyFieldNames of the DataController to the dataset's PK.

Simple Delphi DBcharting

So, the problem I'm having is that I'm displaying two bars on the graph for each student, I just want one of them. They're the correct height though, so that's good.
This is my Delphi source code;
strlstField := TStringList.Create();
ADOQGetResults.SQL.clear;
ADOQGetResults.SQL.Add(
'SELECT Results.StudentID, SUM(Results.Rawmark) as TRM, StudentInfo.Fname '+
'FROM (StudentInfo INNER JOIN Results ON StudentInfo.StudentID = Results.StudentID) '+
'WHERE (((StudentInfo.StudentID)=Results.StudentID)) AND Results.TestID =12 '+
'GROUP BY StudentInfo.Fname, Results.StudentID'
);
ADOQGetResults.Active := True;
ADOQGetResults.Open;
DBChart1.Title.Text.Clear;
DBChart1.Title.Text.Add('Class leaderboard');
DBChart1.Title.Font.Size := 15;
DBChart1.LeftAxis.Title.Font.Size := 12;
DBChart1.LeftAxis.Title.Caption := 'Total marks';
DBChart1.BottomAxis.Title.Font.Size := 12;
DBChart1.BottomAxis.Title.Caption := 'Student';
//Charting Series
//To Remove Old Series
for intCnt := DBChart1.SeriesCount -1 downto 0 do
DBChart1.Series[intCnt].Free;
//To Add New Series
for intCnt := 1 to ADOQGetResults.FieldCount - 1 do
begin
strlstField.Add(ADOQGetResults.FieldList[intCnt].FieldName);
DBChart1.AddSeries(TBarSeries.Create(nil));
end;
//To set source for Series
for intCnt:= 0 to DBChart1.SeriesCount -1 do
begin
with DBChart1 do begin
Series[intCnt].Clear;
Series[intCnt].Title := strlstField[intCnt];
Series[intCnt].ParentChart := DBChart1;
Series[intCnt].DataSource := ADOQGetResults;
Series[intCnt].XLabelsSource := 'Fname';
Series[intCnt].YValues.ValueSource := 'TRM';
end;
end;
I've been trying to work-out whats going wrong all day, so if anyone can help at all I'd be very grateful!
Here is what the graph looks like right now;
http://oi48.tinypic.com/6qelba.jpg
Why are you looping over EVERY FIELD in the result (you return 3 fields in your query) and adding one series PER field in the result? It's almost like you think that the field count equals your row count or something. Secondly I would venture to guess that something in your query plus your data (that we can't see) could result in you getting more rows in your query result than you were expecting.
Why are you destroying and re-adding series when your query always returns 3 fields, 1 field is not charted, 1 field is the series label source and 1 field is the series value source? Just statically create one series at designtime in your dfm and forget all this crazy runtime stuff. Have you tried double clicking dbchart and adding ONE BarChart series there?
This works and is much less code. You don't need to open a dataset twice, by the way. Note that I'm using the DBDEMOS.mdb database that comes with Delphi here so that everyone can play along. Add a db chart and at DESIGNTIME add ONE barchart series to it. Configure as desired. Use this code. dataset below is a TADODataset.
-
dataset.CommandText := 'select EmpNo,FirstName,Salary from employee';
dataset.Active := True;
DBChart1.Title.Text.Clear;
DBChart1.Title.Text.Add('Class leaderboard');
DBChart1.Title.Font.Size := 15;
DBChart1.LeftAxis.Title.Font.Size := 12;
DBChart1.LeftAxis.Title.Caption := 'Total marks';
DBChart1.BottomAxis.Title.Font.Size := 12;
DBChart1.BottomAxis.Title.Caption := 'Student';
if DBChart1.SeriesCount<1 then
begin
raise Exception.Create('Add series to your chart in the dfm ONCE.');
end;
//To set source for Series
with DBChart1 do begin
Series[0].Title := 'Test';
Series[0].DataSource := dataset;
Series[0].XLabelsSource := 'FirstName';
Series[0].YValues.ValueSource := 'Salary';
end;
Note that this is still more code than you absolutely have to write. You could do most of this if not all in dfm (form designer).

Showing accumulated messages to the user

I want to show the user a summary of all the relevant messages that have occurred during a code execution (e.g. parsing, algorithm, conversion, validation etc). The messages should be showed together when the process is done.
A similar incident might occur none, one or several times. The user should be notified if the incident occurred. There might be several types of incidents.
I'm not sure if the scenario is clear, but maybe some code will help:
PSEUDO-CODE:
begin
//Execute computing process//
repeat
Set a flag if an incident occurs
Set another flag if another incident occurs
until done
//Show message to user//
if AnyFlagIsSet then
ShowPrettyMessageToUser
end;
EXECUTABLE DELPHI CODE:
program Test;
{$APPTYPE CONSOLE}
uses
SysUtils, StrUtils;
var
i: Integer;
tmpFlags: Array[1..4] of Boolean;
tmpMessage: String;
tmpChar: Char;
begin
Randomize;
repeat
//Initialization//
for i := 1 to 4 do
tmpFlags[i] := False;
//Will insident occur?//
for i := 0 to 5 do
begin
if (Random(10) = 0) then tmpFlags[1] := True;
if (Random(10) = 0) then tmpFlags[2] := True;
if (Random(10) = 0) then tmpFlags[3] := True;
if (Random(10) = 0) then tmpFlags[4] := True;
end;
//Show message//
tmpMessage := '';
if tmpFlags[1] then tmpMessage := tmpMessage + IfThen(tmpMessage <> '', #13#10+#13#10) + 'Incident 1';
if tmpFlags[2] then tmpMessage := tmpMessage + IfThen(tmpMessage <> '', #13#10+#13#10) + 'Incident 2';
if tmpFlags[3] then tmpMessage := tmpMessage + IfThen(tmpMessage <> '', #13#10+#13#10) + 'Incident 3';
if tmpFlags[4] then tmpMessage := tmpMessage + IfThen(tmpMessage <> '', #13#10+#13#10) + 'Incident 4';
Writeln('----------');
Writeln(tmpMessage);
Writeln('----------');
Writeln;
Write('Again? (Y/N) ');
Readln(tmpChar);
until tmpChar <> 'y';
end.
The code in the iteration is quiet complex in real life, of course.
And the messages are also more informative, and may even be formatted and multi-lined.
So...
Is there a best practice or pattern that can be used for this?
Any Delphi-component that handles this?
An easy solution would be to use a TStringList to collect all messages. You can then either display the strings in a listbox or concatenate the strings (in this case all messages should be valid sentences).
Pseudocode:
procedure DoSomething(Log : TStrings);
begin
//...
Log.Add ('Some hint.');
//...
Log.Add ('Some error happened.');
//...
end;
DoSomething (Log);
if (Log.Count > 0) then
LogListBox.Items.AddStrings (Log);
For formatted or multi-lined messages you could store HTML strings in the stringlist and use a component that can display HTML formatted text to display the messages.
Edit: If you want no duplicate messages, just do
Log.Duplicates := dupIgnore;
I would (where the memory requirements are not a factor) create a class which uses a hashed stringlist as a catalog, where X number of "logitems" could be inserted as objects. This allows some control over how the items are grouped. By accessing the stringlist using a normal index - you get a linear timeline. But accessing the items through it's hash-keys, you get the items grouped by content. Using a hashed stringlist is much faster to work with since it employes a lookup-table.
For an overall, application-wide solution, I have used the midas library's TClientDataset which has been with Delphi for ages. Just remember to declare "midaslib" as the first unit in your project and it's linked directly into your binary (no need to ship midaslib.dll). This gives you the benefit of fields and sorting by message/error types. As long as the record count dont get above 10k it's both fast and stable to work with.

Resources