Foros Club Delphi

Foros Club Delphi (https://www.clubdelphi.com/foros/index.php)
-   C++ Builder (https://www.clubdelphi.com/foros/forumdisplay.php?f=13)
-   -   ¿existe un evento OnScrollBoxPositionScrollBarChange? (https://www.clubdelphi.com/foros/showthread.php?t=86742)

aguml 25-09-2014 21:47:49

¿existe un evento OnScrollBoxPositionScrollBarChange?
 
Explico, tengo un TImage dentro de un TScrollBox y la imagen es mas grande que es scrollbox. Lo que quiero es capturar el evento que se produzca al mover uno de los scrolls para hacer algo cuando cambie la posicion de la imagen. Otra cosa ¿Como se hace para que, al mover el scroll se vea en vivo la imagen moviendose sin tener que esperar a que suelte el boton izquierdo del raton?

escafandra 26-09-2014 01:04:17

Coloca la propiedad Tracking de ambas barras a true.

Saludos.

aguml 26-09-2014 01:38:52

supongo que eso que me indicas es para que se vea el desplazamiento en vivo ¿No? Para el tema de capturar los eventos de las scrolls he estado buscando mucho y hablan de hacerlo de dos maneras. Una es haciendo subclassing de un tscrollbox y añadiendole esos eventos y gestionar en ellos lo que quiera. La otra manera que he visto es usando el ScrollBox1->WndProc() para gestionar el mensaje ahi. ¿Que modo es mejor? ¿Y por que es mejor uno que otro?

escafandra 26-09-2014 12:17:50

Para conseguir lo que buscas, tienes dos opciones: realizas un subclassing sobre el objeto dado o derivas tu propia clase.

En windows existe una función de tratamiento de mensajes para cada ventana, si cambias la función, estas haciendo un subclassing. Derivar tu clase TscrollBox puede ser útil si vas a usarla más veces, aprovechando el código.

En la VCL, la función WndProc es la de tratamiento de mensajes del control. Tienes herramientas para cambiarla (subclassing) o reescribirla (clases derivadas)

Aquí tienes un ejemplo de subclassing en delphi


Saludos.

aguml 26-09-2014 12:42:28

Bueno, lo he conseguido hacer de las dos maneras. Os pongo los dos proyectos para el que quiera ver el codigo y demás:

1ª opcion: https://mega.co.nz/#!A0MwQCBL!OyZZSR...1IxfB_HocNGV9o

2ª opcion: https://mega.co.nz/#!p0FwyAgZ!8o6hRi...IHAQCjs-4q7Zmc

Las dos van genial aunque creo que es mejor opcion la que creo una subclase de ScrollBox donde le añado los eventos aunque no me entero de casi nada lo que se hace ya que ha sido practicamente un copy/paste de otro sitio con algunas modificaciones que le hice. Lo peor de este metodo es que tengo que crear e inicializar el componente en ejecucion mediante codigo ya que no puedo insertarlo como se haria con un componente TScrollBox normal. Si alguien lo ve y me sabe decir como convertirlo en un componente visual para poder añadirlo como el original se lo agradecería.

aguml 26-09-2014 14:15:49

Bueno amigos, he conseguido crear un componente al que he llamado ScrollBoxWithScrollsEvents y funciona perfectamente. Si alguien quiere examinarlo por si ve algo mal pues me haria un favor.
Aqui el enlace al componente: https://mega.co.nz/#!9pUjwCoK!R1104L...3W1txWTpWql7RE

Espero que le pueda ayudar a otros. A mi si me ha ayudado a aprender como crear un componente descendiente de otro y la verdad es que es muy util. :D

escafandra 26-09-2014 14:48:01

Estaba preparando un ejemplo, pero veo que ya resolviste el problema. Aún así lo dejo.

Fíjate en este código basado en el tuyo. Es un componente visual que debes instalar:
Menu->Component->Intall Component

Código:

//---------------------------------------------------------------------------

#ifndef NewScrollBoxH
#define NewScrollBoxH
//---------------------------------------------------------------------------
#include <SysUtils.hpp>
#include <Classes.hpp>
#include <Controls.hpp>
#include <Forms.hpp>
//---------------------------------------------------------------------------
class PACKAGE TNewScrollBox : public TScrollBox
{
  public:
        __fastcall TNewScrollBox(TComponent* Owner);

  private:
        TNotifyEvent FOnVerticalScroll;
        TNotifyEvent FOnHorizontalScroll;

  private:
        void __fastcall WMHScroll(TMessage& Msg);
        void __fastcall WMVScroll(TMessage& Msg);

  __published:
        __property TNotifyEvent OnVerticalScroll =
        {read=FOnVerticalScroll,
        write=FOnVerticalScroll};
        __property TNotifyEvent OnHorizontalScroll =
        {read=FOnHorizontalScroll,
        write=FOnHorizontalScroll};

  public:
        BEGIN_MESSAGE_MAP
        MESSAGE_HANDLER(WM_HSCROLL, TMessage, WMHScroll)
        MESSAGE_HANDLER(WM_VSCROLL, TMessage, WMVScroll)
        END_MESSAGE_MAP(TScrollBox)
};
//---------------------------------------------------------------------------
#endif

Código:

//---------------------------------------------------------------------------

#include <vcl.h>

#pragma hdrstop

#include "NewScrollBox.h"
#pragma package(smart_init)
//---------------------------------------------------------------------------
// ValidCtrCheck is used to assure that the components created do not have
// any pure virtual functions.
//

static inline void ValidCtrCheck(TNewScrollBox *)
{
        new TNewScrollBox(NULL);
}

//---------------------------------------------------------------------------
__fastcall TNewScrollBox::TNewScrollBox(TComponent* Owner)
        : TScrollBox(Owner)
{
}
void __fastcall TNewScrollBox::WMHScroll(TMessage& Msg)
{
        if (FOnHorizontalScroll != NULL)
        {
                FOnHorizontalScroll(this);
        }
        TScrollBox::Dispatch(&Msg);
}

void __fastcall TNewScrollBox::WMVScroll(TMessage& Msg)
{
        if (FOnVerticalScroll != NULL)
        {
                FOnVerticalScroll(this);
        }
        TScrollBox::Dispatch(&Msg);
}
//---------------------------------------------------------------------------
namespace Newscrollbox
{
        void __fastcall PACKAGE Register()
        {
                TComponentClass classes[1] = {__classid(TNewScrollBox)};
                RegisterComponents("Samples", classes, 0);
        }
}
//---------------------------------------------------------------------------

Te subo el proyecto del nuevo componente.


Saludos.

aguml 26-09-2014 15:38:43

Pues como yo lo hice jejeje. Me costó porque nunca habia hecho algo asi pero es muy util saberlo hacer.

escafandra 26-09-2014 18:17:01

El tratamiento de mensajes podría hacerse reescribiendo la función virtual WndProc de TNewScrollBox directamente. El resultado es el mismo.

Saludos.

ecfisa 26-09-2014 18:54:39

Hola.

Si escafandra, si se va a aplicar eventualmente a un control, esta última opción que mencionas la considero mas práctica por lo sencilla de implementar .

header
Código PHP:

...
class 
TForm1 : public TForm {
...
private:    
   
TWndMethod OldWndProc;
   
void __fastcall NewWndProc(TMessageMessage);
   
void __fastcall vertScroll(TObject *Sender);
   
void __fastcall horzScroll(TObject *Sender);
   ...
}; 

code
Código PHP:

..
void __fastcall TForm1::FormCreate(TObject *Sender)
{
  
OldWndProc ScrollBox1->WindowProc;
  
ScrollBox1->WindowProc NewWndProc;
}

void __fastcall TForm1::NewWndProc(TMessageMessage)
{
  if(
Message.Msg == WM_VSCROLL)
    
vertScroll(ScrollBox1);
  else if( 
Message.Msg == WM_HSCROLL)
    
horzScroll(ScrollBox1);
  
OldWndProc(Message);
}

void __fastcall TForm1::vertScroll(TObject *Sender) {
  
Caption "Scroll vertical";
}

void __fastcall TForm1::horzScroll(TObject *Sender) {
  
Caption "Scroll horizontal";
}

void __fastcall TForm1::FormDestroy(TObject *Sender)
{
  
ScrollBox1->WindowProc OldWndProc;


En cambio, optaría por la primera si se justificara por la reutilización del código (como en el caso de un componente).

Es decir, si voy a usar algo una vez en una aplicación y muy rara vez nuevamente, escribiría el código mas corto y simple si con él obtengo el mismo resultado (pero es mi humilde opinion).

Saludos :)

escafandra 26-09-2014 20:41:05

ecfisa, no hablo de cambiar la función sino de sobreescribir la función virtual WndProc del control que estamos creando, derivado de un descendente TWinControl. Es mi técnica preferida.

Saludos.

aguml 26-09-2014 20:50:05

interesante, ¿Como seria eso amigo? ¿Puedes crear un ejemplo básico?

ecfisa 26-09-2014 21:29:37

Cita:

Empezado por escafandra (Mensaje 482200)
ecfisa, no hablo de cambiar la función sino de sobreescribir la función virtual WndProc del control que estamos creando...

Ha, te entendí mal entonces... Yo hablaba de evitar derivar la clase para un uso casual.

Cita:

Empezado por aguml (Mensaje 482202)
interesante, ¿Como seria eso amigo? ¿Puedes crear un ejemplo básico?

No veo en línea al amigo escafandra, pero te pongo un ejemplo muy básico de lo que creo a que se refiere (luego me corregirá si me equivoco):

header:
Código PHP:

class TForm1 : public TForm {
__published:
private:
  
void __fastcall FMouseDown(TMessagemsg);
protected:
  
virtual void __fastcall WndProc(TMessagemsg);
public:
  
__fastcall TForm1(TComponentOwner);
}; 

code:
Código PHP:

void __fastcall TForm1::FMouseDown(TMessagemsg) {

    
Caption Format("Click en: (%3d, %3d)",
      
ARRAYOFCONST(((int)msg.LParamLo, (int)msg.LParamHi)));
}

void __fastcall TForm1::WndProc(TMessagemsg) {

  if (
msg.Msg == WM_LBUTTONDOWN)
    
FMouseDown(msg);
  
TForm::WndProc(msg);


Saludos :)

aguml 26-09-2014 22:25:07

mmmm es muy similar a la de sustituir la funcion. En el ejemplo que pusiste, al ser un form, ¿Cmo harias para cear un form y que sea el unico form del proyecto? Cuando creas una aplicacion el c++builder ya te añade el form pero si quieres que use el tuyo modificado en vez del estandar ¿Como lo haces? Ya se que no tiene nada que ver y que es un ejemplo pero me dio curiosidad.

aguml 27-09-2014 00:40:18

bien, lo he vuelto a ver y me di cuenta que no creas ninguna clase sino que modificas la que hay. En ese caso es muy facil porque es la unidad .cpp y .h del form1 pero si fuese por ejemplo de un timage no está el .cpp ni el .h por lo que para hacer eso mismo tendrias que crear una derivada ¿No? Ademas ¿Que ventaja tiene sobreescribir la funcion contra reemplazarla? Supongo que se obtiene exactamente el mismo resultado ¿No?

escafandra 27-09-2014 01:09:08

ecfisa, a eso me refería.

Pongo un ejemplo sobre el control de aguml:

Código:

//---------------------------------------------------------------------------

#ifndef NewScrollBoxH
#define NewScrollBoxH
//---------------------------------------------------------------------------
#include <SysUtils.hpp>
#include <Classes.hpp>
#include <Controls.hpp>
#include <Forms.hpp>
//---------------------------------------------------------------------------
class PACKAGE TNewScrollBox : public TScrollBox
{
  public:
        __fastcall TNewScrollBox(TComponent* Owner);

  private:
        TNotifyEvent FOnVerticalScroll;
        TNotifyEvent FOnHorizontalScroll;

  protected:
        virtual void __fastcall WndProc(TMessage& msg);

  __published:
        __property TNotifyEvent OnVerticalScroll =
        {read=FOnVerticalScroll,
        write=FOnVerticalScroll};
        __property TNotifyEvent OnHorizontalScroll =
        {read=FOnHorizontalScroll,
        write=FOnHorizontalScroll};
};
//---------------------------------------------------------------------------
#endif

Código:

//---------------------------------------------------------------------------

#include <vcl.h>

#pragma hdrstop

#include "NewScrollBox.h"
#pragma package(smart_init)
//---------------------------------------------------------------------------
// ValidCtrCheck is used to assure that the components created do not have
// any pure virtual functions.
//

static inline void ValidCtrCheck(TNewScrollBox *)
{
        new TNewScrollBox(NULL);
}

//---------------------------------------------------------------------------
__fastcall TNewScrollBox::TNewScrollBox(TComponent* Owner)
        : TScrollBox(Owner)
{
  FOnHorizontalScroll = 0;
  FOnVerticalScroll = 0;
}

void __fastcall TNewScrollBox::WndProc(TMessage& msg)
{
  if(msg.Msg == WM_HSCROLL && FOnHorizontalScroll) FOnHorizontalScroll(this);
  if(msg.Msg == WM_VSCROLL && FOnVerticalScroll) FOnVerticalScroll(this);
  TScrollBox::WndProc(msg);
}

//---------------------------------------------------------------------------
namespace Newscrollbox
{
        void __fastcall PACKAGE Register()
        {
                TComponentClass classes[1] = {__classid(TNewScrollBox)};
                RegisterComponents("Samples", classes, 0);
        }
}
//---------------------------------------------------------------------------

aguml, ecfisa si crea una nueva clase puesto que TForm1 ya es derivada de TForm, lo que pasa es que la crea Builder.
Una explicación sencilla de una función virtual: una función virtual tiene la característica que se hereda, como todas, pero si la sobreescribes funciona la de la clase hija, incluso si la llamas desde un puntero a la clase hija pero del tipo clase madre. El compilador averigua a quién te refieres y aplica la función correcta. En este caso la función WinProc es virtual desde TWinControl y es la encargada del tratamiento de mensajes de Windows. En este caso, la sobreescritura debe terminar llamando a la función de la clase madre para que trate el resto de mensajes.

Fíjate en esto:
Código:

TNewScrollBox *SC = new TNewScrollBox(this);
TWinControl *WC = SC;
SC->WndProc(msg);  // LLama a la función WinProc de TNewScrollBox porque SC lo es.

Saludos.

escafandra 27-09-2014 02:02:10

Perdón, el último código de ejemplo sería así
Código:

TNewScrollBox *SC = new TNewScrollBox(this);
TWinControl *WC = SC;
WC->WndProc(msg);  // LLama a la función WinProc de TNewScrollBox porque SC lo es

Saludos.

aguml 27-09-2014 23:41:53

gracias por la aclaracion.

escafandra 27-09-2014 23:55:19

Ya conoces dos formas de tratamiento de mensajes, una de más alto nivel y la otra más cercana a la API de Windows. Usa la que te guste o te convenga.
Al realizar subclassing en ventanas de Windows (mediante inyección de código), el conocimiento de la segunda te será más útil. En tus aplicaciones, cualquiera de las formas te resultará eficaz.

Saludos.

aguml 28-09-2014 14:53:19

cuando dices hacer subclasing de ventanas windows ¿Te refieres a hacer un inline a un proceso externo? ¿Algo asi como un hook pero en vez de a una api hacerlo sobre el mismo codigo del proceso?

escafandra 28-09-2014 19:44:50

Cita:

Empezado por aguml (Mensaje 482244)
cuando dices hacer subclasing de ventanas windows ¿Te refieres a hacer un inline a un proceso externo? ¿Algo asi como un hook pero en vez de a una api hacerlo sobre el mismo codigo del proceso?

Puede ser en nuestro proceso o en otro, mediante inyección de código, una dll por ejemplo, en otro proceso y desde él hacer subclassing en la ventana que nos interese. Cambias la función de tratamiento de la ventana en cuestión por la tuya obligando a responder a los mensajes como tu decidas. esto se hace con la API SetWindowLong con nIndex = GWL_WNDPROC. deberás guardar el puntero de la función original para usarla si es preciso y para devolver a su estado original la ventana en cuestión. esto se hace con GetWindowLong con nIndex = GWL_WNDPROC. En el foro tienes algún ejemplo como este (en un proceso externo) o este otro (en nuestro proceso).

En realidad no se le puede llamar hook en sentido estricto, aunque se podría decir que es un hook al WindProc de una ventana. Sobre hooks también tienes información en el foro y en DA.


Saludos.


La franja horaria es GMT +2. Ahora son las 04:15:32.

Powered by vBulletin® Version 3.6.8
Copyright ©2000 - 2026, Jelsoft Enterprises Ltd.
Traducción al castellano por el equipo de moderadores del Club Delphi