views:

570

answers:

3

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?

+2  A: 

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.

Mason Wheeler
That still begs the question of how to tell the 'save' button which SpeedButton opened the memo without having to use a case..of statement? I'm trying to avoid any kind of huge lookups because there are going to be hundreds of these comment buttons on the form.
Rafe
I don't quite understand what the problem is. Associate one SpeedButton reference with each comment, and when you open a comment, give it a reference to the button that opened it. Then when you save, whichever reference you have is the button to assign it back to. No lookups required.
Mason Wheeler
A: 

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.

Uwe Raabe
Just wanted to add that this is actually the second part of the solution! Trimming down the multiple onclick events using Rob's code above was part of the problem, but saving the comments was the other half of the issue and with a little fiddling this code does the trick! Thanks Uwe!
Rafe
+2  A: 

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.

Rob Kennedy
Rob, you are a Delphi GOD!!! That is exactly the information I was looking for! And Mason, if you're reading this I apologize for not getting what you mean the first time but I only just picked up Delphi again within the past month after being away from it since around 2002 so I'm still trying to remember all the concepts, tricks, etc. Rob, appreciate your note about the label naming - that particular label was just a quickly placed 'hack' to show the user that they were actually in edit mode. Prior to release compilation I always adjust labels and such to reflect their actual usage.
Rafe