Here are a few little gems I have picked up on my travels that work a treat.
Move to a particular line in a Memo...
function MemoMoveToLine(LineNo: integer);
begin
with Memo1 do
begin
SelStart := Perform(EM_LINEINDEX, LineNo, 0);
Perform(EM_SCROLLCARET, LinePos, 0);
end;
end;
What this is doing is calling TControl.Perform. This function actually uses a Windows message to perform some task, in this case placing the cursor at a line number, and then scrolling to that line. Let's take a look at using that function...
var
CurrentLine: integer;
begin
// Get the current position
with Memo1 do
CurrentLine := Perform(EM_LINEFROMCHAR, SelStart, 0);
AddLotsOfLines;
// Move to the line we started at
MemoMoveToLine(CurrentLine);
// Now Move to the end
MemoMoveToLine(Memo1.Lines.Count);
// Now Move to the start
MemoMoveToLine(0);
end;
And there you have it.
Ramblings
I'm well into packing in the evenings now and surrounded by boxes. The truck comes in 2 days to take my furniture to Hamilton where I will start work as Software Development Manager about mid month. I look forward to the challange of a new team and new company.
Although it will be hard to leave the Mount and this wonderful job and environment, my family is still at the Mount so I'll be back often. Its only a short drive to get here to see them and I'll probably end up with a caravan here for easy weekend stays.
I'll still be codeing in Delphi for my own programs (and sanity), always have, always will. This will ensure that the blog stays relative to Delphi, although I might introduce a few new subjects as well.
Just one thing - Perform() uses a Windows message, but it doesn't use messagING. Calling Perform() results in a direct call to the underlying WindowProc to process a message immediately. There's no posting or sending involved.
ReplyDeletei.e. you bypass the message queue.
I don't think it's a significant distinction in this case, but it is an important distinction, never-the-less.
Thanks Jolyon, always bang on in good feedback. Much appreciated, I'll update my post.
ReplyDeleteSteve
Excellent tips Steve,
ReplyDeleteAs it happens I currently have a project meandering about in my head that is still "percolating" and it will involve quite a bit of work with TMemo [in it's current manifestation] ... these tips will certainly come in handy.
Many thanks,
Hi Steve,
ReplyDeleteI think function MemoMoveToLine needs result type, or I am wrong?
Hi again TDelphiHobbyist, and thanks for your nice comments.
ReplyDeleteIssam Ali - Not necessary in this situation. However, I suppose you should check to see if LineNo is greater than Memo1.Lines.Count.
I haven't tested it out to see if it produces an exception if it is, but you could give it that value of Memo1.Lines.Count if it is greater.
Well spotted.
Steve
Or implemented as a class helper (so that you can use just Memo1.LineNo:=NewLineNo)
ReplyDeleteTYPE
TMemoHelper = CLASS HELPER FOR TMemo
STRICT PRIVATE
FUNCTION GetLineNo : INTEGER;
PROCEDURE SetLineNo(NewLineNo : INTEGER);
PUBLIC
PROPERTY LineNo : INTEGER Read GetLineNo Write SetLineNo;
END;
FUNCTION TMemoHelper.GetLineNo : INTEGER;
BEGIN
Result:=Perform(EM_LINEFROMCHAR,SelStart,0)
END;
PROCEDURE TMemoHelper.SetLineNo(NewLineNo : INTEGER);
BEGIN
IF NewLineNo<0 THEN NewLineNo:=0;
IF NewLineNo>Lines.Count THEN NewLineNo:=Lines.Count;
SelStart:=Perform(EM_LINEINDEX,NewLineNo,0);
Perform(EM_SCROLLCARET,LinePos,0); // Should LinePos be NewLineNo ?? //
END;