Solved

End of memo box

Posted on 2004-09-13
11
596 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
Comment Utility
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
Comment Utility
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
Comment Utility
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
 
LVL 22

Expert Comment

by:Ferruccio Accalai
Comment Utility
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
Comment Utility
F68,
  You are the man!!
  I had subscribed to this thread to see the soln: thanks for the info!!
...Shu :-)
0
How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

 
LVL 6

Expert Comment

by:bpana
Comment Utility
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
Comment Utility
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
Comment Utility
--> 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
Comment Utility
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
Comment Utility
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
Comment Utility
Thanks BPana, that works a treat!
0

Featured Post

Why You Should Analyze Threat Actor TTPs

After years of analyzing threat actor behavior, it’s become clear that at any given time there are specific tactics, techniques, and procedures (TTPs) that are particularly prevalent. By analyzing and understanding these TTPs, you can dramatically enhance your security program.

Join & Write a Comment

Hello everybody This Article will show you how to validate number with TEdit control, What's the TEdit control? TEdit is a standard Windows edit control on a form, it allows to user to write, read and copy/paste single line of text. Usua…
Introduction I have seen many questions in this Delphi topic area where queries in threads are needed or suggested. I know bumped into a similar need. This article will address some of the concepts when dealing with a multithreaded delphi database…
Get a first impression of how PRTG looks and learn how it works.   This video is a short introduction to PRTG, as an initial overview or as a quick start for new PRTG users.
When you create an app prototype with Adobe XD, you can insert system screens -- sharing or Control Center, for example -- with just a few clicks. This video shows you how. You can take the full course on Experts Exchange at http://bit.ly/XDcourse.

772 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

Need Help in Real-Time?

Connect with top rated Experts

12 Experts available now in Live!

Get 1:1 Help Now