Club Builder - Foros de discusión Club Builder
Foro de C++Builder en español
 
 FAQFAQ   BuscarBuscar   MiembrosMiembros   Grupos de UsuariosGrupos de Usuarios   RegistrarseRegistrarse 
 PerfilPerfil   Entra para ver tus mensajes privadosEntra para ver tus mensajes privados   EntrarEntrar 

Sincronización de scroll vertical de dos TListViews

 
Publicar nuevo tema   Responder al tema    Foros de discusión -> Consultas
Ver tema anterior :: Ver tema siguiente  
Autor Mensaje
Mensaje    Asunto: Sincronización de scroll vertical de dos TListViews Responder citando

Busqué en Internet un código que sincronizara el desplazamiento vertical de dos TListViews.
No encontré nada satisfactorio. Traté de adaptar códigos, traté de inventar, en fin, probé un poco de todo, pero si una cosa salía bien en un código, otra siempre salía mal.
Por ejemplo, el siguiente código actualiza el contenido si hacemos clic en la barra de desplazamiento, pero no funciona si arrastramos el botón. Y los botones se desincronizan fácilmente:
Código:

#define lv1 GetScrollPos(ListView1->Handle, SB_VERT);
#define lv2 GetScrollPos(ListView2->Handle, SB_VERT);

void __fastcall TForm4::ListView1ScrollBarWinProcs(TMessage &Mes)
{
   ListView1ScrollBar(Mes);
   if(Mes.Msg==WM_VSCROLL)   {
      int i = lv1
      int k = lv2
      if(i != k)  {
         ListView2->Perform(Mes.Msg,Mes.WParam,Mes.LParam);
      }
   }
}


El siguiente código fue el único que logré sincronizar perfectamente los botones de desplazamiento, pero no funciona para el contenido de las dos TListViews:

Código:

void __fastcall TForm4::ListView1ScrollBarWinProcs(TMessage &Mes)
{
   ListView1ScrollBar(Mes);
   if(Mes.Msg==WM_VSCROLL)   {
      SCROLLINFO scrInfo = { sizeof(SCROLLINFO), SIF_ALL };
      int ratio;
      // Get scrollbar position
      GetScrollInfo(ListView1->Handle, SB_VERT, &scrInfo);
      ratio = scrInfo.nPos;
      SetScrollInfo(ListView2->Handle, SB_VERT, &scrInfo, TRUE);
      //ScrollWindow(ListView2->Handle,1,ratio,NULL,NULL);
      InvalidateRect( ListView2->Handle,  NULL, TRUE );
      UpdateWindow( ListView2->Handle );
      // ListView2->ScrollBy(0, ratio);
      // ListView2->Scroll(0, dif);
   }
}


¿Alguna idea de cómo hacer que esto funcione?

Detalle: ambos TListView son virtuales

Gracias.
Volver arriba
Ver perfil de usuario Enviar mensaje privado
Mensaje    Asunto: Responder citando

Prueba de esta manera:

Código:

void __fastcall TForm4::ListView1ScrollBarWinProcs(TMessage &Mes)
{
  if(Mes.Msg == WM_VSCROLL){
    SCROLLINFO si = {sizeof(SCROLLINFO), SIF_ALL};
    GetScrollInfo(ListView1->Handle, SB_VERT, &si);
    if(LOWORD(Mes.WParam) == SB_LINEUP)
      ListView2->Scroll(0, -si.nPage);
    else if(LOWORD(Mes.WParam) == SB_LINEDOWN)
      ListView2->Scroll(0, si.nPage);
    else if(LOWORD(Mes.WParam) == SB_THUMBTRACK)
      ListView2->Scroll(0, (HIWORD(Mes.WParam)-si.nPos)*si.nPage);
  }
  ListView1ScrollBar(Mes);
}



Saludos.
Volver arriba
Ver perfil de usuario Enviar mensaje privado
Mensaje    Asunto: Responder citando

¡Gracias por responder, escafandra!

El código movió ListView2 pero no se sincronizó con ListView1. Tal vez porque es un TListView virtual. No sé.

Adapté un código que funcionó, pero es un código muy feo. Funcionó, pero lleva tiempo actualizar la sincronización si hay muchos datos en listview:

Código:


void __fastcall TForm4::ListView1ScrollBarWinProcs(TMessage &Mes)
{
   ListView1ScrollBar(Mes);
   if(Mes.Msg==WM_VSCROLL)   {
      ListView2->Items->BeginUpdate();
      SCROLLINFO scrInfo = { sizeof(SCROLLINFO), SIF_ALL };
      GetScrollInfo(ListView1->Handle, SB_VERT, &scrInfo);
      int lv1 = scrInfo.nPos;
      int lv2 = GetScrollPos(ListView2->Handle, SB_VERT);
     while(lv2<lv1)  {
      ListView2->Perform(WM_VSCROLL, MAKELONG(SB_LINEDOWN, 0), 0);
      lv2 = GetScrollPos(ListView2->Handle, SB_VERT);
     }
     while(lv2>lv1)  {
      ListView2->Perform(WM_VSCROLL, MAKELONG(SB_LINEUP, 0), 0);
      lv2 = GetScrollPos(ListView2->Handle, SB_VERT);
     }
     ListView2->Items->EndUpdate();
   }
}



no tengo mas ideas
Volver arriba
Ver perfil de usuario Enviar mensaje privado
Mensaje    Asunto: Responder citando

No dispongo de TVirtualListView así que lo Hice con TListView realizando un Subclassing con el código que te expuse. El resultado es el siguiente:




Saludos
Volver arriba
Ver perfil de usuario Enviar mensaje privado
Mensaje    Asunto: Responder citando

¡Perfecto!

Ciertamente el problema es el modo virtual.

Pero pensé que funcionaría porque, de hecho, el componente que estoy usando es el propio TistView.

Implementé una rutina diferente para cargar los datos, porque cargar millones de filas en un ciclo consumía mucho tiempo, más de 10 minutos; a diferencia de la carga virtual, donde los datos se muestran al instante.

Estoy usando el ejemplo que obtuve de esta página:

https://web.archive.org/web/20040211191410/http://www.bridgespublishing.com/articles/issues/0001/Fast_updates_with_virtual_list_views.htm

Buscaré un poco más. Si encuentro algo, publícalo aquí.

Muchas gracias.
Volver arriba
Ver perfil de usuario Enviar mensaje privado
Mensaje    Asunto: Responder citando

He probado haciendo virtuales los ListView con 3500 elementos y me funciona igual que en el caso de no virtuales.

Para el SubClassing he escrito esta función:
Código:

typedef void __fastcall(__closure *PWndProc)(Messages::TMessage &Message);

//---------------------------------------------------------------------------
void __fastcall SetSubCass(TWinControl *WC, PWndProc newProc, PWndProc *oldProc)
{
  *oldProc = WC->WindowProc;
  WC->WindowProc = newProc;
}


y la uso así:
Código:

__fastcall TForm1::TForm1(TComponent* Owner)
  : TForm(Owner)
{
  SetSubCass(ListView1, SubClassLV1WndProc, &LV1WndProc);
  SetSubCass(ListView2, SubClassLV2WndProc, &LV2WndProc);
}
//---------------------------------------------------------------------------


Siendo LV1WndProc y LV2WndProc punteros a los WndProc originales de cada ListView.

Cada SubClass realiza lo mismo que el otro referenciando a su ListView contrario:
Código:

void __fastcall TForm1::SubClassLV1WndProc(TMessage& Mes)
{
  if(Mes.Msg == WM_VSCROLL){
    SCROLLINFO si = {sizeof(SCROLLINFO), SIF_ALL};
    GetScrollInfo(ListView1->Handle, SB_VERT, &si);
    if(LOWORD(Mes.WParam) == SB_LINEUP)
      ListView2->Scroll(0, -si.nPage);
    else if(LOWORD(Mes.WParam) == SB_LINEDOWN)
      ListView2->Scroll(0, si.nPage);
    else if(LOWORD(Mes.WParam) == SB_THUMBTRACK)
      ListView2->Scroll(0, (HIWORD(Mes.WParam)-si.nPos)*si.nPage);
  }
  LV1WndProc(Mes);
}

void __fastcall TForm1::SubClassLV2WndProc(TMessage& Mes)
{
  if(Mes.Msg == WM_VSCROLL){
    SCROLLINFO si = {sizeof(SCROLLINFO), SIF_ALL};
    GetScrollInfo(ListView2->Handle, SB_VERT, &si);
    if(LOWORD(Mes.WParam) == SB_LINEUP)
      ListView1->Scroll(0, -si.nPage);
    else if(LOWORD(Mes.WParam) == SB_LINEDOWN)
      ListView1->Scroll(0, si.nPage);
    else if(LOWORD(Mes.WParam) == SB_THUMBTRACK)
      ListView1->Scroll(0, (HIWORD(Mes.WParam)-si.nPos)*si.nPage);
  }
  LV2WndProc(Mes);
}


Lo que falta por implementar es la rueda del ratón y el movimiento con teclado.

Pero ahora se me ocurre preguntarte si en tu caso ambos ListView tienen los mismos elementos o no. En mis pruebas ambos tenían los mismos.


Saludos.
Volver arriba
Ver perfil de usuario Enviar mensaje privado
Mensaje    Asunto: Responder citando

Experimentando con ListView vritual con diferente número de elementos, he escrito un código que asegure que el primer elemento visible de la lista esté sincronizado y funciona bastante bien:

Código:

void Listview_SetTopIndex(HWND hListView, int index)
{
  SendMessage(hListView, WM_SETREDRAW, 0, 0);
  ListView_EnsureVisible(hListView, ListView_GetItemCount(hListView)-1, true);
  ListView_EnsureVisible(hListView, index, true);
  SendMessage(hListView, WM_SETREDRAW, 1, 0);
}

void __fastcall TForm1::SubClassLV1WndProc(TMessage& Mes)
{
  LV1WndProc(Mes);
  if(Mes.Msg == WM_VSCROLL || Mes.Msg == WM_MOUSEWHEEL || Mes.Msg == WM_KEYDOWN ){
    int Top = ListView_GetTopIndex(ListView1->Handle);
    Listview_SetTopIndex(ListView2->Handle, Top);
  }
}


Saludos.
Volver arriba
Ver perfil de usuario Enviar mensaje privado
Mensaje    Asunto: Responder citando

¡Simplemente perfecto!

Muchas gracias Razz
Volver arriba
Ver perfil de usuario Enviar mensaje privado
Mostrar mensajes de anteriores:   
Cambiar a:  
No puedes publicar temas nuevos en este foro
No puedes responder a temas en este foro
No puedes editar tus mensajes en este foro
No puedes borrar tus mensajes en este foro
No puedes votar en las encuestas de este foro


Hosting WordPress Indedmedia desde 3,99 €
© 2002–2026 Club Builder · Funciona con phpBB.