Go Premium for a chance to win a PS4. Enter to Win

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 624
  • Last Modified:

End of memo box

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
Narusegawa
Asked:
Narusegawa
  • 4
  • 2
  • 2
  • +3
2 Solutions
 
EvarestCommented:
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
 
NarusegawaAuthor Commented:
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
 
Ferruccio AccalaiSenior developer, analyst and customer assistance Commented:
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
VIDEO: THE CONCERTO CLOUD FOR HEALTHCARE

Modern healthcare requires a modern cloud. View this brief video to understand how the Concerto Cloud for Healthcare can help your organization.

 
Ferruccio AccalaiSenior developer, analyst and customer assistance Commented:
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
 
snehanshuCommented:
F68,
  You are the man!!
  I had subscribed to this thread to see the soln: thanks for the info!!
...Shu :-)
0
 
bpanaCommented:
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
 
Ferruccio AccalaiSenior developer, analyst and customer assistance Commented:
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
 
Ferruccio AccalaiSenior developer, analyst and customer assistance Commented:
--> if (Message.Msg = WM_VScroll) or (Message.Msg = WM_MOUSEWHEEL) then
Yes bpana, you're right. Good shot :)

F68 ;-)
0
 
bpanaCommented:
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
 
Pierre CorneliusCommented:
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
 
NarusegawaAuthor Commented:
Thanks BPana, that works a treat!
0

Featured Post

Free Tool: SSL Checker

Scans your site and returns information about your SSL implementation and certificate. Helpful for debugging and validating your SSL configuration.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

  • 4
  • 2
  • 2
  • +3
Tackle projects and never again get stuck behind a technical roadblock.
Join Now