VCL CheckListBox pass selected Item and delete it - delphi

Hi guys I am a Delphi beginner and I have a simple question because finding something online for Delphi seems to be hard.
I have a CheckListBox and when I click on the checkbox of an Item I want to pass it to another form lets say form2 gets created. There I have 2 buttons:
Delete - it should delete the selected Item so the item where the form spawned on. How do I delete the selected Item?
Edit Entry - I need to pass the checked Item values to another form where I can edit the values and save it as a new one. How do I pass the values from the selected Item to another form?
Ty Faded.

Here's a better approach:
Create a new VCL application.
Add a TCheckListBox control with a few items on it:
Drop a TActionList component on the form.
Create two actions: aDelete and aRename. Sets their captions to Delete... and Rename... and their hints to Removes the selected item from the list. and Renames the selected item in the list..
Add the following code to their OnExecute handlers:
procedure TForm1.aDeleteExecute(Sender: TObject);
begin
if CheckListBox1.ItemIndex = -1 then
Exit;
if MessageBox(Handle, 'Do you want to delete the selected item?',
'Delete', MB_ICONQUESTION or MB_YESNO) <> IDYES then
Exit;
CheckListBox1.DeleteSelected;
end;
procedure TForm1.aRenameExecute(Sender: TObject);
var
S: string;
begin
if CheckListBox1.ItemIndex = -1 then
Exit;
S := CheckListBox1.Items[CheckListBox1.ItemIndex];
if InputQuery('Rename', 'Please enter the new name:', S) then
CheckListBox1.Items[CheckListBox1.ItemIndex] := S;
end;
Add the following code to their OnUpdate handlers:
procedure TForm1.aDeleteUpdate(Sender: TObject);
begin
aDelete.Enabled := CheckListBox1.ItemIndex <> -1;
end;
procedure TForm1.aRenameUpdate(Sender: TObject);
begin
aRename.Enabled := CheckListBox1.ItemIndex <> -1;
end;
Drop a TPopupMenu on the form. Name it pmList. Add two menu items. Set their Action properties to aDelete and aRename, respectively. This will automatically give the items the caption, hint, and hotkeys of the actions:
Now assign pmList to the check list box control's PopupMenu property.
Test the application. Notice that the context menu items are enabled only if an item is selected; otherwise, they are both disabled. (This is thanks to the OnUpdate handlers. It would be very sloppy of us to skip these. But notice that we still verify that an item is selected in the OnExceute handlers. In quality software, you always use both belt and braces.)
Of course, we must map the Del and F2 keys to the delete and rename actions. We could use the ShortCut properties of the actions, but that would make these keys delete and rename in this list even if another GUI control has focus, and that's very bad. Instead, we add an OnKeyDown handler to the check list box control itself:
procedure TForm1.CheckListBox1KeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
begin
case Key of
VK_DELETE:
aDelete.Execute;
VK_F2:
aRename.Execute;
end;
end;

Related

Empty a TMemo with Ctrl+Enter

What I'm trying to accomplish:
User enters text into a TMemo box
If they press Enter it creates a new line
If they press Ctrl+Enter it moves the text to another box and empties the TMemo
I'm using this code [KeyPreview is True]:
procedure TFMsg.FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
begin
if (Shift = [ssCtrl]) and (Key = $0D) then
begin
Key := 0;
btnSendClick(Sender); //this moves the text and empties the TMemo box
end;
end;
What's actually happening:
Ctrl+Enter sends the text to the other box
The TMemo empties but seems to accept the Enter key as the cursor sits flashing on the second line
Any help gratefully received. Thank you!
The best way to handle this is as follows:
Create an action list, or action manager, or re-use an existing one.
Add an action that clears the memo and moves to the next one. You'll need to check that the active control really is a memo.
Give the action the shortcut that you desire, CTRL+ENTER.
Note that you don't need to attach the action to anything. It's mere presence is enough to ensure that the shortcut will be handled.
For compound keyboard actions using modifier keys it's always simplest to use an action shortcut and so keep at arm's length from the lower level keyboard handling code.
Your action handler might look like this:
if ActiveControl is TMemo then
begin
Memo := TMemo(ActiveControl);
Text := Memo.Text;
Memo.Clear;
SelectNext(Memo, True, True);
if ActiveControl is TMemo then
begin
Memo := TMemo(ActiveControl);
Memo.Text := Text;
end;
end;
In this code I'm assuming that there are multiple memos and the text is moved from one memo to the next one in the tab order. But your needs may well differ. In which case I'm sure it will be obvious what you need to do for your scenario.
Use the Memo's OnKeyPress event for that:
procedure TFMsg.Memo1KeyPress(Sender: TObject; var Key: Char);
begin
if (key=#10) and (GetKeyState(VK_CONTROL)<0) then
begin
key:=#0;
btnSendClick(Sender);
end;
end;
Please note that you have to check for Line Feed (#10), not for Carriage Return (#13).
property WantReturns: Boolean;

TListBox Multiselect Style

Is it possible to change the Multiselect behaviour with a standard TListBox?
I would like items to only be multiselected by holding the Ctrl key, not the Shift key.
I have TActions which update depending on items selected, eg:
TAction(Sender).Enabled := ListBox1.ItemIndex <> -1;
The controls assigned to the action flicker when a listbox item is selected when holding shift to multiselect, this does not happen by holding ctrl key only.
So I would like to use only Ctrl key to multiselect.
Simply put, how can I:
Multiselect TListBox
Use Ctrl to multiselect
Shift has no effect
Thanks :)
Item selection is processed by the default window procedure of the underlying list box api control. Because of this there's no VCL property that would do this. Nevertheless, users might not like this, but you can change the behavior by handling WM_LBUTTONDOWN message that is post the the control which triggers item selection. Subclass the control in any way you like. Or, since WM_LBUTTONDOWN is posted, you can use OnMessage event of an ApplicationEvents component. Or, just as one example below, if the control is immediately parented by the form, you can use the notification sent to the parent:
type
TForm1 = class(TForm)
..
private
procedure WMParentNotify(var Msg: TWMParentNotify); message WM_PARENTNOTIFY;
..
procedure TForm1.WMParentNotify(var Msg: TWMParentNotify);
var
Pt: TPoint;
State: TKeyboardState;
begin
if (Msg.Event = WM_LBUTTONDOWN) then begin
Pt := SmallPointToPoint(SmallPoint(Msg.XPos, Msg.YPos));
MapWindowPoints(Handle, ListBox1.Handle, Pt, 1);
if PtInRect(ListBox1.ClientRect, Pt) then begin
GetKeyboardState(State);
State[VK_SHIFT] := 0;
SetKeyboardState(State);
end;
end;
inherited;
end;

Delphi menu choice

My question is how to catch which menu item was pressed in some form? For an example :
I have a form with a button. When I pressed the button the menu of the application will be on focus and the child form wait to choose a menu item. After I choose one the child form show a message with the name of the menu item which I pressed.
Can anyone tell me how to do this?
Thanks in advance!
Something like this is a direct answer to your question:
procedure TMyForm.MenuItemClick(Sender: TObject);
begin
ShowMessage((Sender as TMenuItem).Caption);
end;
This event handler should be connected to each menu item that you wish to behave this way.
It seems that you want some centralised logging or monitoring of the execution of menu items. If you use actions and associate these with your menu items then you can get an application wide notification that an action has been executed. Drop a TApplicationEvents object onto your main form and handle its OnActionExecute event. Like this:
procedure TMyForm.ApplicationEvents1ActionExecute(Action: TBasicAction; var Handled: Boolean);
begin
ShowMessage((Action as TAction).Caption);
end;
This will fire whenever any event in your app is executed.
1/ By default you set all the TMenuItem OnClick event handler to Nil.
2/ When you click the form button you assign an event to each TMenuItem, this event will be only called once, and will record the 'trigger'.
3/ When the event is called you reset all the TMenuitem.OnClick to Nil
4/ To make this easyer, you store all your MenuItems in a TList.
example:
global variables (private declaration in TMyForm):
MyTriggerItem: TMenuItem; // used as pointer
MyMenuItemList: TList; // used to store all TMenuItem which are 'listened to'
your TButton handler:
Procedure TMyForm.ButtonClick(Sender: TObject);
Var
i: Integer;
Begin
For i:= 0 To Pred(MyMenuItemList.Count) Do TMenuItem(MyMenuItemList[i]).OnCLick := CommonMenuItemClick;
End;
your TMenuItem event handler:
Procedure TMyForm.CommonMenuItemClick(Sender: TObject);
Var
i: Integer;
Begin
MyTriggerItem := TMenuItem(Sender);
For i:= 0 To Pred(MyMenuItemList.Count) Do TMenuItem(MyMenuItemList[i]).OnCLick := Nil;
End;

Delphi: How to Assign an Up Arrow Keyboard Shortcut to Action/MenuItem, and Keep It Actual for Navigating the List Control (ListBox/VTV)?

Please assist me: How to assign an up arrow keyboard shortcut to action or menu item, and keep it actual for navigating the list control (e.g. ListBox/Virtual Treeview/other) at the same time?
Thanks!
You comment:
And how about the Winamp player? It has Volume Up/Volume Down features assigned to the up arrow key and down arrow key correspondingly.. Okay, if that impossible in Delphi, then ...
but it certainly is possible, it just isn't a good idea to do it, and against the Windows User Experience Interaction Guidelines.
But if you're set on implementing it, here's how. Override the following method in your form class that contains the action components:
function IsShortCut(var Message: TWMKey): Boolean; override;
and in it you can prevent the Up and Down key from triggering the actions they are shortcuts for:
function TWeirdForm.IsShortCut(var Message: TWMKey): Boolean;
begin
if (Message.CharCode in [VK_UP, VK_DOWN])
// insert test whether message needs to go to the focused control instead
and (...)
then begin
// insert calls to code that should be executed instead
Result := False;
exit;
end;
inherited;
end;
Note that you should test for the correct shift state too, and check that your code doesn't break any other window behaviour users expect, like moving of the window with the arrow keys.
On the form properties set KeyPreview := true
then on KeyUp event of the form write event to check if you Up key is pressed and make it call the menu item (on this case menu item called Action1):
procedure TForm1.FormKeyUp(Sender: TObject; var Key: Word; Shift: TShiftState);
begin
if (Key = VK_UP) and (ActiveControl = ListBox1)then
Action11.Click;
end;
procedure TForm1.Action11Click(Sender: TObject);
begin
if ListBox1.ItemIndex >=0 then
ShowMessage(ListBox1.Items[ListBox1.ItemIndex]);
end;
If you need the Action1 to be executed even if they Current Control isn't the listbox, remove the and part of the IF statement

Delphi 2009: Pass component name onclick event then set property

I have a custom component of type TSpeedButton that has two extra properties defined:
CommentHeading: string;
CommentText: string;
I set CommentHeading at design time.
When the speed button is pressed a memo is shown with a button beneath it for saving its contents. The procedure that handles this:
procedure CustomSpeedButton1Click(Sender: TObject);
begin
Receiver := CustomSpeedButton1.Name; // possibly used to save the memo text back to this speedbuttons property after comments are submitted
ViewComments(CustomSpeedButton1.CommentTitle,CustomSpeedButton1.CommentText);
end;
And the ViewComments procedure itself:
procedure ViewComments(comment_caption:string; comment_text:string);
begin
label15.Hide; // label showing editing in progress, hidden until user begins typing
Button1.Enabled := false; // the button for saving the memo text, hidden until user begins typing
CommentsBox.Visible := true; // pop up the comment box at the bottom of the form
CommentsBox.Caption := 'Comments: ' + comment_caption;
CommentsMemo.Text := comment_text; // if there are existing comments assign them to memo
end;
The contents of the memo need to be assigned to the CommentText property of the custom SpeedButton.
What I was initially thinking was that I could pass the component name to a variable when the custom SpeedButton gets pressed and then retrieve that name when the save button on the memo is pressed and use it to assign the memo text to the speedbuttons CommentText property. But then I realized that to do this I'd have to use some kind of case..of statement that checked for each possible speedbutton name and then assign the memo value to its properties and this just seems ridiculously tedious.
Is there an easier way to assign the memo text to the speedbutton that opened the memo to begin with?
Ultimately, you're asking how to tell the ViewComments function which button's properties it's working with.
Do you understand what the Sender parameter is doing in the OnClick event? It's telling the event handler which object's event is being handled. It's serving precisely the role that you're looking to bring to the ViewComments function.
That's what Mason was getting at in his answer. Rather than pass all the property values, pass the object itself:
procedure ViewComments(CommentButton: TCustomSpeedButton);
Then call it from all your buttons' event handlers:
procedure TForm1.CustomSpeedButton1Click(Sender: TObject);
begin
ViewComments(CustomSpeedButton1);
end;
procedure TForm1.CustomSpeedButton2Click(Sender: TObject);
begin
ViewComments(CustomSpeedButton2);
end;
No strings, no case statements, no lookups.
That should answer your question, but you can do it even better. Remember what I said before about the Sender parameter? When someone clicks the first button, the Sender parameter of that OnClick handler will be the button, so we can rewrite the first event handler like this:
procedure TForm1.CustomSpeedButton1Click(Sender: TObject);
begin
ViewComments(Sender as TCustomSpeedButton);
end;
And you can rewrite the second event handler like this:
procedure TForm1.CustomSpeedButton2Click(Sender: TObject);
begin
ViewComments(Sender as TCustomSpeedButton);
end;
Hmm. They're the same. Having two identical functions is wasteful, so get rid of one and rename the other so it doesn't sound button-specific:
procedure TForm1.CommentButtonClick(Sender: TObject);
begin
ViewComments(Sender as TCustomSpeedButton);
end;
Then set the OnClick properties of both buttons to refer to that one event handler. You can't do that just by double-clicking the property in the Object Inspector. You'll need to either type the name yourself, choose it from the drop-down list, or assign the event property at run time:
CustomSpeedButton1.OnClick := CommentButtonClick;
CustomSpeedButton2.OnClick := CommentButtonClick;
I'd also like to encourage you to use more meaningful names for your controls. That Label15 is particularly egregious. How can you remember that the fifteenth label is the one that indicates that editing is in progress? Call it EditInProgressLabel, for instance.
Since you're already passing extra variables around, why not just pass the SpeedButton itself? Then you won't need to look up the reference.
A small change to your code should do the trick:
procedure TForm1.CustomSpeedButton1Click(Sender: TObject);
var
btn: TCustomSpeedButton;
begin
btn := Sender as TCustomSpeedButton;
Receiver := btn.Name;
ViewComments(btn.CommentTitle, btn.CommentText);
end;
and after editing the comment:
procedure TForm1.StoreComments(comment: string);
var
btn: TCustomSpeedButton;
begin
btn := FindComponent(Receiver) as TCustomSpeedButton;
btn.CommentText := comment;
end;
You can also memorize the button itself instead of just it's name.

Resources