Solved

End of memo box

Posted on 2004-09-13
11
605 Views
Last Modified: 2012-06-21
Does anyone know how to perform an action once the an end user has reached the end of the memo box? As per most EULA's do now-a-days.

I have a memobox on my form and would like btnClick to only be enabled once the user has scrolled to the end of the memobox. I know this doesn't ensure that they have read it, but I'm being asked to add this feature and so will try to.

Any help is appreciated. Thanks.
0
Comment
Question by:Narusegawa
  • 4
  • 2
  • 2
  • +3
11 Comments
 
LVL 4

Expert Comment

by:Evarest
ID: 12042664
Please a RichEdit on your form (replace the Memo). Now include a RichEdit1SelectionChange event.

procedure TForm1.RichEdit1SelectionChange(Sender: TObject);
begin
 Button1.Enabled :=RichEdit1.SelStart = length(RichEdit1.Text);
end;

This will enable the Button1 when you reach the end of the RichEdit.

This is possible in TMemo, but then you'll need some more lines of code. Make your life easier and use TRichEdit instead, it's better anyway to use that...

Kind regards,
Evarest
0
 

Author Comment

by:Narusegawa
ID: 12042687
I've just tried this and it requires the user to click inside the richedit at the end. I'd like to enable it once the scrollbar reaches the bottom.

Regards
0
 
LVL 22

Assisted Solution

by:Ferruccio Accalai
Ferruccio Accalai earned 50 total points
ID: 12043002
Well, this is coded using a part of a my previous answer.

First you have to detect the scroll message writing a yourself memo1.windowproc.

Then you have to get the Tscrollinfo parameters to find if you're scrolling to the end of the TMemo

Put a Tmemo with text and a TButton on a form and past this code:

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm1 = class(TForm)
    Memo1: TMemo;
    Button1: TButton;
      procedure FormCreate(Sender: TObject);
      procedure FormDestroy(Sender: TObject);
  private
      { Private declarations }
      FMemoWindowProc: TWndMethod;
     procedure MyMemoWindowProc(var Message: TMessage);
  public
      { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
begin
FMemoWindowProc := Memo1.WindowProc;
 // replace it with new
MEmo1.WindowProc := MyMemoWindowProc;
end;

procedure TForm1.MyMemoWindowProc(var Message: TMessage);
var
ScrollInfo1: TScrollInfo;
begin
// call original window procedure
   FMemoWindowProc(Message);
   if Message.Msg = WM_VScroll then //memo is scrolling
      begin
         ScrollInfo1.cbSize := SizeOf(TScrollInfo); //get the scroll info tag
         ScrollInfo1.fMask := SIF_all; //and get all the parameters
         GetScrollInfo(Memo1.Handle,SB_VERT, ScrollInfo1); //but just for the vertical scrollbar
{now the nMax param is the max scrollbar count of the memo, nPos is the actual Pos and nPage is the number of showed lines (so npos could be used also to get the first showed line). So if the difference between nMax-nPos is less than  the showed lines, it means that we are at the end of the memo, or to say it better, the last line is currently showed, so the scrollbar is at the end of Tmemo}
         Button1.Enabled :=
            (ScrollInfo1.nMax - ScrollInfo1.nPos) <= ScrollInfo1.nPage; //your button is enabled only if we are at the end
     end;
 end;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
// restore original window procedure
Memo1.WindowProc := FMemoWindowProc;
end;

end.

F68 ;-)
0
Live: Real-Time Solutions, Start Here

Receive instant 1:1 support from technology experts, using our real-time conversation and whiteboard interface. Your first 5 minutes are always free.

 
LVL 22

Expert Comment

by:Ferruccio Accalai
ID: 12043008
sorry, there's a more 'end' here, just comment it

procedure TForm1.MyMemoWindowProc(var Message: TMessage);
var
ScrollInfo1: TScrollInfo;
begin
// call original window procedure
   FMemoWindowProc(Message);
   if Message.Msg = WM_VScroll then //memo is scrolling
      begin
         ScrollInfo1.cbSize := SizeOf(TScrollInfo); //get the scroll info tag
         ScrollInfo1.fMask := SIF_all; //and get all the parameters
         GetScrollInfo(Memo1.Handle,SB_VERT, ScrollInfo1); //but just for the vertical scrollbar
{now the nMax param is the max scrollbar count of the memo, nPos is the actual Pos and nPage is the number of showed lines (so npos could be used also to get the first showed line). So if the difference between nMax-nPos is less than  the showed lines, it means that we are at the end of the memo, or to say it better, the last line is currently showed, so the scrollbar is at the end of Tmemo}
         Button1.Enabled :=
            (ScrollInfo1.nMax - ScrollInfo1.nPos) <= ScrollInfo1.nPage; //your button is enabled only if we are at the end
     end;
 //end; <-- this one
end;

Sorry again :)
F68 ;-)
0
 
LVL 5

Expert Comment

by:snehanshu
ID: 12043035
F68,
  You are the man!!
  I had subscribed to this thread to see the soln: thanks for the info!!
...Shu :-)
0
 
LVL 6

Expert Comment

by:bpana
ID: 12043083
I would change the line:
if Message.Msg = WM_VScroll then //memo is scrolling
to
if (Message.Msg = WM_VScroll) or (Message.Msg = WM_MOUSEWHEEL) then
0
 
LVL 22

Expert Comment

by:Ferruccio Accalai
ID: 12043086
Hi shu :)
Yeah, i think also that it's a good solution. In fact it's difficult to find the way to solve it around the web. As you're a smart guy, congratulation for your 'subscribing' :))

F68 ;-)
0
 
LVL 22

Expert Comment

by:Ferruccio Accalai
ID: 12043124
--> if (Message.Msg = WM_VScroll) or (Message.Msg = WM_MOUSEWHEEL) then
Yes bpana, you're right. Good shot :)

F68 ;-)
0
 
LVL 6

Accepted Solution

by:
bpana earned 75 total points
ID: 12043163
another solution would be:

  TMemo = class(StdCtrls.TMemo)
    procedure WMVScroll(var Msg : TWMVScroll);  message WM_VSCROLL;
    procedure WM_MOUSEWHEEL(var Msg : TWMMouseWheel);  message WM_MOUSEWHEEL;
    procedure OnMyScroll;
  end;

procedure TMemo.WMVScroll(var Msg: TWMVScroll);
begin
  inherited;
  OnMyScroll;
end;

procedure TMemo.WM_MOUSEWHEEL(var Msg: TWMMouseWheel);
begin
  inherited;
  OnMyScroll;
end;

procedure TMemo.OnMyScroll;
var
  ScrollInfo1: TScrollInfo;
begin
  ScrollInfo1.cbSize := SizeOf(TScrollInfo);
  ScrollInfo1.fMask := SIF_all;
  GetScrollInfo(Self.Handle,SB_VERT, ScrollInfo1);
  Form1.Button1.Enabled :=
   (ScrollInfo1.nMax - ScrollInfo1.nPos) < ScrollInfo1.nPage;
end;

Bogdan
0
 
LVL 14

Expert Comment

by:Pierre Cornelius
ID: 12043422
Yet another solution (Based on WINAPI  Help):
===============================

unit main;

interface

uses
  Windows, Messages, Forms, StdCtrls, Classes, Controls;

type
  TMyMemo = class(TMemo)
    private
      FOnLastLineVisible: TNotifyEvent;
      procedure CNVScroll(var Message: TWMVScroll); message WM_VSCROLL;
    public
      function LastLnVisible: boolean;
    published
      property OnLastLineVisible: TNotifyEvent read FOnLastLineVisible
                                               write FOnLastLineVisible;
  end;

  TForm1 = class(TForm)
    Button1: TButton;
    procedure FormCreate(Sender: TObject);
    procedure UpdateLbl(Sender: TObject);
  private
    MyMemo: TMyMemo;
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

{ TMyMemo }

procedure TMyMemo.CNVScroll(var Message: TWMVScroll);
begin
  inherited;
  if LastLnVisible then
    if Assigned(FOnLastLineVisible)
    then FOnLastLineVisible(Self);
end;

function TMyMemo.LastLnVisible: boolean;
var FirstLine: integer;
    FmtRect: TRect;
    FmtRectHeight: integer;
    tm: TTextMetric;
    dc: HDC;
begin
  FirstLine:= SendMessage(Handle, EM_GETFIRSTVISIBLELINE, 0, 0);
  SendMessage(Handle, EM_GETRECT, 0, Integer(@FmtRect));
  FmtRectHeight:= FmtRect.Bottom- FmtRect.Top;
  dc:= GetDC(self.Handle);
  SelectObject(dc, Font.Handle);
  GetTextMetrics(dc, tm);
  result:= (FirstLine + (FmtRectHeight div tm.tmHeight) >= Lines.Count);
end;

procedure TForm1.UpdateLbl(Sender: TObject);
begin
  Button1.Enabled:= MyMemo.LastLnVisible;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  MyMemo:= TMyMemo.Create(self);
  MyMemo.Parent:= self;
  MyMemo.Align:= alTop;
  MyMemo.ScrollBars:= ssVertical;
  MyMemo.Text:= '1'#13#10'2'#13#10'3'#13#10'4'#13#10'5'#13#10'6'#13#10'7'#13#10'8'#13#10'9'#13#10'Last';
  MyMemo.OnLastLineVisible:= UpdateLbl;
end;

end.


DFM Form
=======
object Form1: TForm1
  Left = 192
  Top = 114
  Width = 216
  Height = 176
  Caption = 'Form1'
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'MS Sans Serif'
  Font.Style = []
  OldCreateOrder = False
  OnCreate = FormCreate
  PixelsPerInch = 96
  TextHeight = 13
  object Button1: TButton
    Left = 48
    Top = 104
    Width = 75
    Height = 25
    Caption = 'Button1'
    Enabled = False
    TabOrder = 0
  end
end


WINAPI Help Extract
=============
Determining the Visible Area of a Multiline Edit Control

PSS ID Number: Q88387

Authored 24-Aug-1992                  Last modified 27-Jun-1995

The information in this article applies to:
 - Microsoft Windows Software Development Kit (SDK), versions 3.0 and 3.1
 - Microsoft Win32 SDK, versions 3.5, 3.51, and 4.0

SUMMARY
The multiline edit control provided with Microsoft Windows versions
3.0 and 3.1 does not provide a mechanism that allows an application to
determine the currently visible lines of text. This article outlines
an algorithm to provide that functionality.


MORE INFORMATION

The general idea is to determine the first and last visible lines and
obtain the text of those lines from the edit control. The following
steps detail this process:

1. A Windows-3.1-based application can use the newly available
   message, EM_GETFIRSTVISIBLELINE, to determine the topmost visible
   line.

   A Windows-3.0-based application can use a technique described in the
   following Microsoft Knowledge Base article to determine the line
   number of the first visible line:


      ARTICLE-ID: Q68572
      TITLE     : Caret Position & Line Numbers in Multiline Edit Controls

2. Obtain the edit control's formatting rectangle using EM_GETRECT.
   Determine the rectangle's height using this formula:

      nFmtRectHeight = rect.bottom - rect.top;

3. Obtain the line spacing of the font used by the edit control to
   display the text. Use the WM_GETFONT message to determine the font
   used by the edit control. Select this font into a display context

   and use the GetTextMetrics function. The tmHeight field of the
   resulting TEXTMETRIC structure is the line spacing.

4. Divide the formatting rectangle's height (step 2) with the line
   spacing (step 3) to determine the number of lines. Compute the line
   number of the last visible line based on the first visible line
   (step 1) and the number of visible lines.

5. Use EM_GETLINE for each line number from the first visible line to
   the last visible line to determine the visible lines of text.

   Remember that the last visible line may not necessarily be at the
   bottom of the edit control (the control may only be half full). To
   detect this case, use EM_GETLINECOUNT to know the last line and
   compare its number with the last visible line. If the last line
   number is less than the last visible line, your application should
   use EM_GETLINE only on lines between the first and the last line.

Additional reference words: 3.00 3.10 3.50 4.00 95
KBCategory: kbprg

KBSubcategory: UsrCtl



0
 

Author Comment

by:Narusegawa
ID: 12043835
Thanks BPana, that works a treat!
0

Featured Post

Courses: Start Training Online With Pros, Today

Brush up on the basics or master the advanced techniques required to earn essential industry certifications, with Courses. Enroll in a course and start learning today. Training topics range from Android App Dev to the Xen Virtualization Platform.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Suggested Solutions

Objective: - This article will help user in how to convert their numeric value become words. How to use 1. You can copy this code in your Unit as function 2. than you can perform your function by type this code The Code   (CODE) The Im…
Have you ever had your Delphi form/application just hanging while waiting for data to load? This is the article to read if you want to learn some things about adding threads for data loading in the background. First, I'll setup a general applica…
This Micro Tutorial will teach you how to censor certain areas of your screen. The example in this video will show a little boy's face being blurred. This will be demonstrated using Adobe Premiere Pro CS6.
Two types of users will appreciate AOMEI Backupper Pro: 1 - Those with PCIe drives (and haven't found cloning software that works on them). 2 - Those who want a fast clone of their boot drive (no re-boots needed) and it can clone your drive wh…

808 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question