update TListView item - delphi

I want to update values in a TListView. I tryed this
...
lvProcess : TListView;
liEdit : TlistItem;
...
liEdit:=lvProcess.Items.Item[1];
liEdit.Caption:='11';
liEdit.SubItems.ValueFromIndex[0]:='22';
liEdit.SubItems.ValueFromIndex[1]:='33';
...
this should do what I want, but after this, the values of the subitems are this ones '=22' and '=33'
I don't want the equal character to be added.
Can anyone help me? I don't know if this is the right way to edit/update a listitem
thanks

You could try the following :
with LvProcess.Items[1] do
begin
Caption := '11';
SubItems.Strings[0] := '22';
SubItems.Strings[1] := '33';
end;
And if you're updating many items at once it is better to surround the update in a way like this:
try
lvProcess.Items.BeginUpdate;
//Do your updates
finally
lvProcess.Items.EndUpdate;
end;

SubItems is a TStrings, so if you want to update the string values, do so like this:
SubItems[0] := '22'
SubItems[1] := '33'
The way you're doing it now, you're using the TStrings as Key/Value list. That's a nice feature of TStrings when you want it, but probably not what you intend.

Related

TCheckListBox get count checked item

I have a simple question. How to get the count of checked item in CheckBoxListBox without using a loop?
TCheckListBox does not provide the option that you are looking for. A loop through its Checked[] property is required.
If you were using Delphi, you could create a class helper to hide that loop. But class helpers are not available in C++.
you can use a function like this.
function GetCheckedCount(CH:TCheckListBox):Integer;
var I:Integer;
begin
Result := 0;
for i := 0 to ch.Items.Count - 1 do
if ch.Checked[i] then inc(result);
end;
Also, SelCount is the number of "selected" items when MultiSelect is true ,Not number of "Checked" items
In Delphi you can [*] do the following:
TCustomMultiSelectListControl(TheBox).MultiSelect := True;
and then SelCount works:
CountOfCheckedItems := TheBox.SelCount;
Isn't the equivalent possible in C++?
[*] Although it causes other problems.

Assigning the selected items in a TxpComboBox to a variable

I'm attempting to assign all the selected items in a TxpListBox to a TStringList.
My initial thought was to do something like
Function AssignListBoxToList(ComponentName : TxpListBox) : Boolean;
var
slComponentValue : TStringList;
begin
slComponentValue := TStringList.Create;
slComponentValue.Add(ComponentName.Items);
end;
But it throws the following exception Incompatible types: 'String' and 'TString'.
Is there a way to either create a TStringList of TStrings, or is it safe to use String instead of TString in my TxpListBox, and/or am I missing something.
TxpListBox is a TListBox with a modified look to fit in with the Windows XP design aesthetic.
It looks like TxpComboBox.Items might be a TStrings descendent (like the standard TComboBox.Items). If that's the case, something like this should work:
slComponentValue := TStringList.Create;
slComponentValue.Add(ComponentName.Items[ComponentName.ItemIndex]);
Your function won't work as is, though, because it doesn't return slComponentValue.
It's generally not a good idea (without a specific reason to do so) to return an object from a function, because it's not clear where the responsibility lies to free it. I prefer to make that more clear by having a procedure accept an already-created instance of an object instead:
procedure AssignComboBoxToList(ComponentName : TxpComboBox;
ListToFill: TStrings) : Boolean;
begin
Assert(Assigned(ListToFill));
ListToFill.Add(ComponentName.Items[ComponentName.ItemIndex);
end;
You can then use it like this:
slComponentValue := TStringList.Create;
try
AssignComboBoxToList(YourComboBox, slComponentValue);
if slComponentValue.Count > 0 then
// Do whatever with the slComponentValue list
finally
slComponentValue.Free;
end;
However, as you're only dealing with a single string, it might be easier to just use a single string; there's not really a TStringList neededhere:
strResult := YourComboBox.Items[YourComboBox.ItemIndex];
With that being said, TComboBox doesn't support multiple selections; TListBox does, but TComboBox displays a drop down list and allows selecting of a single item, making your question somewhat unclear.

Delphi how to save 3 datasources with one save button?

I got a problem with saving all values from 3 datasources into a SMDBGrid with another datasouce.
I got AdressID, ContactpersonID and RelationID.
Those all dont match each others.
The problem is that my SMDBGrid has another datasource then those 3.
I wanna save them with one button.
Tried many ways but can't find a good result.
this is the code i use right now for my Insert button:
procedure TFRelatiebeheer.ToolButton1Click(Sender: TObject);
begin
DRelatiebeheer.ContactpersonID.Insert;
DRelatiebeheer.RelationID.Insert;
DRelatiebeheer.AdressID.Insert;
end;
This is the code i use for my save button right now
if (DRelatiebeheer.ContactpersonID.State in dsEditModes) then
if not (DRelatiebeheer.ContactpersonID.State in [dsInsert]) then
begin
KJSMDBGrid1.RefreshData;
KJPanel4.Visible := True;
end
else
begin
if (DRelatiebeheer.ContactpersonID.State IN dsEditModes) then
DRelatiebeheer.ContactpersonID.Post;
if (DRelatiebeheer.AdressID.State IN dsEditModes) then
DRelatiebeheer.AdressID.Post;
end;
Hope you have a good sight for what I am doing right now, if not please notify.
I got the problem with the datasources that need to be saved on 1 click and then be refreshed in the database and in the Grid.
That means that when I insert a Contactperson there needs to be a AdressID and a RelationID coupled with it.
After that the grid needs to reload all of the data.
Focusing on the given problem
Depending on the intended behavior (should it be possible posting only one or two table(s) or is it necessary to post all tables) the first thing to do would be to ensure that the tables can be posted. You coulds create a function for each table e.g. CanAdressIDBePosted:Boolean to check if required fields are already entered. The condition of the table ContactpersonID would contain additional conditions: needed fields are entered AND CanAdressIDBePosted AND CanRelationIDBePosted. You could create an Action which would be bound on your button with an OnUpdate event which could look like this:
procedure TForm1.PostActionUpdate(Sender: TObject);
begin
TAction(Sender).Enabled := CanAdressIDBePosted and CanContactpersonIDBePosted and CanRelationIDBePosted;
// depending on your requirements (e.g. no need to post RelationID if not entered) it also could be
TAction(Sender).Enabled := CanAdressIDBePosted or CanContactpersonIDBePosted ;
end;
procedure TForm1.PostActionExecute(Sender: TObject);
begin
if CanAdressIDBePosted then AdressID.Post; // ensure ID fields will be generated
if CanRelationIDBePosted then RelationID.Post; // ensure ID fields will be generated
if CanContactpersonIDBePosted then
begin
ContactpersonID.FieldByName('AdressID').value := AdressID.FieldByName('ID').Value;
ContactpersonID.FieldByName('RelationID').value := RelationID.FieldByName('ID').Value;
end;
DateSetBoundToTheGrid.Requery;
// furthor actions you need
end;
Function TForm1.CanAdressIDBePosted:Boolean;
begin
// example implementation
Result := (AdressID.State in [dsEdit,dsInsert]) and (not AdressID.FieldByName('NeededField').IsNull);
end;
Function TForm1.CanContactpersonIDBePosted:Boolean;
begin
// example implementation
Result := (ContactpersonID.State in [dsEdit,dsInsert]) and (not ContactpersonID.FieldByName('NeededField').IsNull)
and CanAdressIDBePosted and CanRelationIDBePosted;
end;
An addidtional Action should be created to cancel if needed:
procedure TForm1.CancelActionExecute(Sender: TObject);
begin
AdressID.Cancel;
RelationID.Cancel;
ContactpersonID.Cancel;
end;
procedure TForm1.CancelActionUpdate(Sender: TObject);
begin
TAction(Sender).Enabled := (AdressID.State in [dsEdit,dsInsert])
or (RelationID.State in [dsEdit,dsInsert])
or (ContactpersonID.State in [dsEdit,dsInsert]);
end;
In general I am not sure if the approach you took ist the best which can be taken, since from the structure given IMHO it should be possible to assign already existing relations and adresses to new generated contactpersons, but that would be another question.
This code just looks somewhat random to me. What SHOULD happen there ?
if (DRelatiebeheer.ContactpersonID.State in dsEditModes) then
// remember this check (1)
if not (DRelatiebeheer.ContactpersonID.State in [dsInsert]) then
// this check better written as "...State = dsInsert"
begin
// why don't you call DRelatiebeheer.ContactpersonID.Post to save chanegs ?
KJSMDBGrid1.RefreshData;
KJPanel4.Visible := True;
end
else
begin
if (DRelatiebeheer.ContactpersonID.State IN dsEditModes) then
// you already checked this above (1), why check again ?
DRelatiebeheer.ContactpersonID.Post;
if (DRelatiebeheer.AdressID.State IN dsEditModes) then
DRelatiebeheer.AdressID.Post;
end;
// so what about DRelatiebeheer.RelationID ?
For what i may deduce, you don't have to make any complex if-ladders, you just have to literally translate your words to Delphi. You want to save three tables and then refresh the grid. Then just do it.
procedure TFRelatiebeheer.SaveButtonClick(Sender: TObject);
begin
DRelatiebeheer.ContactpersonID.Post;
DRelatiebeheer.RelationID.Post;
DRelatiebeheer.AdressID.Post;
DatabaseConnection.CommitTrans;
KJSMDBGrid1.RefreshData;
KJPanel4.Visible := True;
end;
Just like you was told in your other questions.
Delphi save all values from different datasources with 1 save button
Delphi set Panel visible after post
PS. ToolButton1Click - plase, DO rename the buttons. Believe me when you have 10 buttons named Button1, Button2, ...Button10 you would never be sure what each button should do and would mix everything and make all possible program logic errors.

How to let all items in multi-level PopupMenu act as one radiogroup?

I have a PopupMenu with submenus and only one item in total shall be checked at a time. GroupIndex and RadioItem properties do not work outside of the respective submenus as far as I have tried.
I have found this piece of code to check a PopupMenu and its direct sub-components but I haven't had any luck with creating a popup-wide variety of this.
I need a solution that is fast - the PopupMenu has 4x14 entries, always iterating through all menus and subentries can't be the best solution for this, I suppose.
Is there a simple property for this that I am missing or is the rocky path of iteration my only option?
Add all 56 items as actions to one ActionList and give all GroupIndex properties the same value.
Now, add menu-items, sub-menu's and sub-sub-menu's in any tree-like fashion and link each of them to an action. Checking one menu-item, wherever positioned, will automatically uncheck all others.
Et voilĂ !
NGLN's answer is better, but if you really don't want or don't like to use an ActionList, then this routine will also do:
procedure CheckMenuItem(Item: TMenuItem);
procedure UncheckMenu(Menu: TMenuItem; GroupIndex: Byte);
var
I: Integer;
begin
if Menu.RadioItem and (Menu.GroupIndex = GroupIndex) then
Menu.Checked := False;
for I := 0 to Menu.Count - 1 do
UncheckMenu(Menu[I], GroupIndex);
end;
begin
if (not Item.Checked) and Item.RadioItem and (Item.GroupIndex <> 0) then
begin
UncheckMenu(Item.GetParentMenu.Items, Item.GroupIndex);
Item.Checked := True;
end;
end;

TStrings vs TStringList in TCheckListBox

In Delphi 7, I'm using a TCheckListBox. I want it to use a TStringList rather than a TStrings, so I can set Duplicates to dupIgnore, and Sorted to TRUE.
Can I just do this:
Form1 = class(TObject
CheckListBox1: TCheckListBox; // created by the IDE
end;
procedure TForm1.FormCreate
begin
CheckListBox1.Items.Free;
CheckListBox1.Items := TStringList.Create;
CheckListBox1.Items.Sorted := TRUE;
CheckListBox1.Items.Duplicates := dupIgnore;
end;
Is this safe? Any caveats or suggestions?
EDIT: Removed declaration for MyStringList and added .Items to the last two assignment lines.
EDIT 2: Trying to compile the above, it looks like I'd have to cast the two final lines like this:
TStringList(CheckListBox1.Items).Sorted := TRUE;
TStringList(CheckListBox1.Items).Duplicates := dupIgnore;
Although I might be able to get this to run, I'm asking the question because just getting it to run doesn't mean it will always run or is safe.
You don't control what class TCheckListBox uses to store its items. Assigning the Items property a value only assigns its items to the internal storage.
Also, you shouldn't call Items.Free;. TCheckListBox depends on its internal instance of TListBoxStrings.
To answer your edits in your question: Don't hard-cast the Items property to TStringList, either. The typecast is wrong (the instance exposed by Items is not a TStringList) and will only cause problems.
Edit, to suggest a workaround for what you seem to try to achieve: To keep the checklistbox sorted, you can set its Sorted property to True. To avoid duplicates, you can check the list before adding an item in code.

Resources