Scroll notification dans un TRichEdit

chesnetda Messages postés 9 Date d'inscription mercredi 31 mai 2006 Statut Membre Dernière intervention 8 février 2010 - 26 mars 2008 à 10:44
chesnetda Messages postés 9 Date d'inscription mercredi 31 mai 2006 Statut Membre Dernière intervention 8 février 2010 - 27 mars 2008 à 12:01
Bonjour à tous,
J'ai un question à priori simple, mais usr laquelle je butte depuis plusieurs jours: je voudrais savoir quand un utilisateur clique sur le scrollbar vertical d'un richedit et où il clique: su le bouton du haut, du bas, sur le "curseur" (thumb) et s'il le déplace, de combien...
Dans le Oncreate de ma Form, je met en place le masque d'événements:

MouseEvtMsk:= SendMessage(RichEdit1.Handle, EM_GETEVENTMASK, 0, 0);
   SendMessage(RichEdit1.Handle, EM_SETEVENTMASK, 0,
               MouseEvtMsk or ENM_MOUSEEVENTS or ENM_SELCHANGE or ENM_SCROLL);

et je traite les messages au niveau de la form. J'ai identifié deux messages: EN_VSCROLL envoyé avec un WM_COMMAND qui réagit à tout, sauf au clic sur le thumb de l'ascenseur lui-même, et WM_MOUSEACTIVATE qui est envoyé avec le filtre de message, qui réagit à tout clic de souris dans le richedit.
J'ai fait des tests dans tous les sens, je n'ai pas identifié d'autres messages.
Bref, je ne vois pas comment, par exemple je peux suivre le déplacement du thumb si l'utilisateur le délace à la souris...

procedure TForm1.WndProc(var Msg: TMessage);
begin
if (Msg.Msg = WM_COMMAND) and (Msg.LParam=RichEdit1.handle)
  then begin
       if (HIWORD(Msg.WParam)=EN_VSCROLL)
       then begin

            (* Ce message est généré par clics sur les boutons de l'ascenseur et scroll
                avec le clavier, y compris la frappe dans la partie "client" du
               richedit *)             
            end;
        end;   if (Msg.Msg WM_NOTIFY) and (PNMHDR(Msg.lParam).hwndFrom RichEdit1.handle)
   then begin
        if (PNMHDR(Msg.lParam).code = EN_MSGFILTER)
        then begin
               if PMSGFILTER(Msg.lparam).msg = WM_MOUSEACTIVATE
               then begin

                  (*todo: comment être sûr que le scroll vient de l'ascenceur et
                          comment le suivre ? *)
                    end;
             end;
        end;
  inherited;
end;

Après avoir fouiné MSDN, Delphifr et usé Google jusqu'à la corde, je sèche.
Si quelqu'un peut me mettre sur la voie...
Serait-ce plus facile en dérivant une classe du RichEdit ?
Cordialement,
David C.

2 réponses

chesnetda Messages postés 9 Date d'inscription mercredi 31 mai 2006 Statut Membre Dernière intervention 8 février 2010
26 mars 2008 à 17:37
Bon, j'ai un peu progressé: je peux savoir quand le scrolling est terminé et avoir la position réelle du scrollbar. Je peux le suivre pendant qu'on le déplace.
Par contre, la molette de la souris continue à me casser les pieds: je peux savoir quand on l'utilise, mais pas quand le scrolling est terminé. Or c'est pourtant là que je pourrais connaître la nouvelle position du scrollbar...

J'ai essayé toutes les combinaisons pour le filtre d'événement et le mieux c'est :

MouseEvtMsk:= SendMessage(RichEdit1.Handle, EM_GETEVENTMASK, 0, 0);
SendMessage(RichEdit1.Handle, EM_SETEVENTMASK, 0, MouseEvtMsk or ENM_MOUSEEVENTS or ENM_SCROLLEVENTS);

Ajouter ENM_SCROLL ne donne rien de plus, apparement (les messages passent par WM_COMMAND, mais je n'ai pas plus d'info utiles).

[ VScrollInfo: TScrollInfo ]

procedure TForm1.WndProc(var Msg: TMessage);
begin
if (Msg.Msg WM_NOTIFY) and (PNMHDR(Msg.lParam).hwndFrom RichEdit1.handle)
then begin
if (PNMHDR(Msg.lParam).code = EN_MSGFILTER)
then begin
case PMSGFILTER(Msg.lparam).msg of
// WM_MOUSEWHEEL : caption:= 'MOUSEWHEEL';
// WM_MOUSEACTIVATE: caption:= 'MOUSEACTIVATE'; // clic sur ascenseur vide (non actif; grisé)
// WM_LBUTTONDOWN: caption:= 'LBUTTONDOWN'; // clic zone cliente
WM_VSCROLL : begin
case (LOWORD(PMSGFILTER(Msg.lparam).wParam)) of
SB_THUMBTRACK: begin // tracking de la scrollbar
VScrollInfo.fMask:=SIF_TRACKPOS;
GetScrollInfo(RichEdit1.handle,SB_VERT,VScrollInfo);
caption:=IntToStr(VScrollInfo.nTrackPos);
end;

// pour ce qui suit, la nouvelle position n'est pas encore mise à jour
// quand le message arrive !

SB_PAGEUP,
SB_PAGEDOWN,
SB_LINEUP,
SB_LINEDOWN : begin
VScrollInfo.fMask:=SIF_POS;
GetScrollInfo(RichEdit1.handle,SB_VERT,VScrollInfo);
caption:=IntToStr(VScrollInfo.nPos);
end;
// mise à jour de la position quand le scoll erst terminé
// sauf pour la molette qui ne se signale pas, grrrr....
SB_ENDSCROLL : begin
VScrollInfo.fMask:=SIF_POS;
GetScrollInfo(RichEdit1.handle,SB_VERT,VScrollInfo);
caption:=IntToStr(VScrollInfo.nPos);
end;
else caption:= intTostr(LOWORD(PMSGFILTER(Msg.lparam).wParam));
end;
end;
end;
end;
end;
inherited;
end;
0
chesnetda Messages postés 9 Date d'inscription mercredi 31 mai 2006 Statut Membre Dernière intervention 8 février 2010
27 mars 2008 à 12:01
Salut à tous,
J'ai trouvé une solution *bricolage* qui semble fonctionner.
Pour mémoire, le but de l'opération est de pouvoir connaître la nouvelle position de l'ascenseur du scrollbar vertical d'un TRichEdit lors d'un scrolling, soit à la molette de la souris, soit en utilisant le scrollbar directement (flèches, glisser du curseur etc...).
Si un des experts de Delphifr connaît une solution plus élégante, je suis preneur ;-)

procedure TForm1.FormCreate(Sender: TObject);
Var MouseEvtMsk: word;
begin
   MouseEvtMsk:= SendMessage(RichEdit1.Handle, EM_GETEVENTMASK, 0, 0);
   SendMessage(RichEdit1.Handle, EM_SETEVENTMASK, 0,
                   MouseEvtMsk or ENM_MOUSEEVENTS or ENM_SELCHANGE or ENM_SCROLLEVENTS);
end; 

// on intercepte l'événement "molette" et on déclenche le scolling d'une seule ligne à la fois en suivant le sens de la molette
// (haut ou bas donne un Wheeldelta positif ou négatif). SB_ENDSCROLL est envoyé pour forcer la mise à jour de la
// position du curseur du scrollbar

procedure TForm1.RichEdit1MouseWheel(Sender: TObject; Shift: TShiftState;
  WheelDelta: Integer; MousePos: TPoint; var Handled: Boolean);
begin
   if WheelDelta >0
   then RichEdit1.Perform(WM_VSCROLL, MAKELPARAM(SB_LINEUP, 0),0)
   else RichEdit1.Perform(WM_VSCROLL, MAKELPARAM(SB_LINEDOWN, 0),0);
   RichEdit1.Perform(WM_VSCROLL, MAKELPARAM(SB_ENDSCROLL, 0),0);
   Handled:=True;               // l'événement ne sera pas géré au-delà d'ici
end;

// on traite lesnotifications envoyése à Form1 par le Richedit;
// VScrollInfo: TScrollInfo;

Procedure TForm1.WndProc(var Msg: TMessage);
begin   if (Msg.Msg WM_NOTIFY) and (PNMHDR(Msg.lParam).hwndFrom RichEdit1.handle)
   then begin
        // Astuce: Pour annuler l'événement on peut utiliser: PMSGFILTER(Msg.lparam).msg := 0
        if (PNMHDR(Msg.lParam).code = EN_MSGFILTER)
        then begin
             case PMSGFILTER(Msg.lparam).msg of
                   WM_VSCROLL : begin
                                case (LOWORD(PMSGFILTER(Msg.lparam).wParam)) of
                                      SB_THUMBTRACK: begin              // suivi du curseur
                                                     VScrollInfo.fMask:=SIF_TRACKPOS;
                                                     GetScrollInfo(RichEdit1.handle,SB_VERT,VScrollInfo);
                                                     caption:=IntToStr(VScrollInfo.nTrackPos);
                                                     end;
                                      SB_PAGEUP,
                                      SB_PAGEDOWN,
                                      SB_LINEUP,
                                      SB_LINEDOWN  : begin
                                                     VScrollInfo.fMask:=SIF_POS;
                                                     GetScrollInfo(RichEdit1.handle,SB_VERT,VScrollInfo);
                                     // la position retrounée ici est celle du moment de la demande de scrolling
                                                     caption:= IntToStr(VScrollInfo.nPos);
                                                     end;
                                   SB_ENDSCROLL   : begin
                                                     VScrollInfo.fMask:=SIF_POS;
                                                     GetScrollInfo(RichEdit1.handle,SB_VERT,VScrollInfo);
                                    // La position retorunée ici est celle après le scrolling
                                                     caption:=IntToStr(VScrollInfo.nPos);
                                                     end;
                                        end;
                                end;
                   end;
             end;
        end;
  inherited;  // le traitement doit se poursuivre
end;

Désolé pour l'indentation pas très canonique, mais je préfère avoir les begin/end alignés verticalement, je trouve ça plus facile à lire...
Cordialement.
0
Rejoignez-nous