Ir al contenido


Foto

[MULTILENGUAJE] Como colocar CheckBoxes en la cabecera e Items de un ListView

CheckBoxes ListView cabecera Items Selecionar todos los Items

  • Por favor identifícate para responder
4 respuestas en este tema

#1 escafandra

escafandra

    Advanced Member

  • Administrador
  • 4.107 mensajes
  • LocationMadrid - España

Escrito 20 enero 2017 - 10:12

Preguntaron como colocar un CheckBox y seleccionar con el todos los Items de un ListView en C++ y quería dejar en DA Una solución basada en una clase que realiza un subclassing del formulario para controlar las notificaciones que envían la cabecera y el ListView. La clase agrega un ChecBox en la cabecera de la primera columna y en cada Item. También genera dos eventos, uno cuando se marca el CheckBox de la caberera y otro cuando se marca el de un Item, de esta forma tendremos control desde el formulario de lo que está pasando. Cuando se marca la cabecera, automáticamente se marcan todos los Items y cuando están marcados todos los Items individualmente, se marca la cabecera.
 
Esta funcionalidad aparece desde Vista en adelante.
 
El código de la clase es este:


delphi
  1. unit LVCheckBox;
  2.  
  3. //--------------------------------------------------------------------------------------------------
  4. // TLVCheckBox (Versión Hook estilo C++)
  5. // escafandra 2017
  6. // Clase para manejo de CheckBox en la cabecera e Items de un ListView
  7.  
  8. interface
  9.  
  10. uses Windows, Messages, CommCtrl;
  11.  
  12. const
  13. HDN_ITEMSTATEICONCLICK: integer = $FFFFFEC4;
  14. HDF_CHECKBOX = $0040;
  15. HDF_CHECKED = $0080;
  16. HDF_FIXEDWIDTH = $0100;
  17. HDS_CHECKBOXES = $0400;
  18.  
  19. type
  20. PNMHEADER = ^TagNMHEADER;
  21. // PNMLISTVIEW = ^TagNMLISTVIEW;
  22.  
  23. TOnHeaderChecked = procedure(Checked: boolean) of object;
  24. TOnItemChecked = procedure(Index: integer; Checked: boolean) of object;
  25.  
  26. type
  27. TLVCheckBox = class
  28. private
  29. hListView: HWND;
  30. OldWndProc: Pointer;
  31. function WndProc(Handle: HWND; Msg: DWORD; WParam: Longint; LParam: Longint): Longint; stdcall;
  32. public
  33. OnHeaderChecked: TOnHeaderChecked;
  34. OnItemChecked: TOnItemChecked;
  35. constructor Create; overload;
  36. constructor Create(hListView: HWND); overload;
  37. destructor Destroy; override;
  38. procedure SetHeaderCheck;
  39. procedure SetHandle(hLV: HWND);
  40. end;
  41.  
  42. implementation
  43.  
  44.  
  45. function DefWndProc(Handle: HWND; Msg: DWORD; WParam: Longint; LParam: Longint): Longint; stdcall;
  46. var
  47. pLVCheckBox: TLVCheckBox;
  48. begin
  49. pLVCheckBox:= TLVCheckBox(GetWindowLong(Handle, GWL_USERDATA));
  50. if pLVCheckBox <> nil then
  51. Result:= pLVCheckBox.WndProc(Handle, Msg, WParam, LParam)
  52. else
  53. Result:= DefWindowProc(Handle, Msg, WParam, LParam);
  54. end;
  55.  
  56. constructor TLVCheckBox.Create;
  57. begin
  58. OnHeaderChecked:= nil;
  59. OnItemChecked:= nil;
  60. SetHandle(0);
  61. end;
  62.  
  63. constructor TLVCheckBox.Create(hListView: HWND);
  64. begin
  65. OnHeaderChecked:= nil;
  66. OnItemChecked:= nil;
  67. SetHandle(hListView);
  68. end;
  69.  
  70. function TLVCheckBox.WndProc(Handle: HWND; Msg: DWORD; WParam: Longint; LParam: Longint): Longint; stdcall;
  71. var
  72. pNMH: PNMHEADER;
  73. pNMLV: PNMLISTVIEW;
  74. HeaderChecked: bool;
  75. i: integer;
  76. begin
  77. if Msg = WM_NOTIFY then
  78. begin
  79. // Notificación del cambio del CheckBox de la cabecera
  80. pNMH:= PNMHEADER(LParam);
  81. if pNMH.hdr.code = HDN_ITEMSTATEICONCLICK then
  82. begin
  83. if (0 <> (pNMH.PItem.mask and HDI_FORMAT)) and (0 <> (pNMH.PItem.fmt and HDF_CHECKBOX)) then
  84. begin
  85. HeaderChecked:= (0 = (pNMH.pitem.fmt and HDF_CHECKED));
  86. if @OnHeaderChecked <> nil then
  87. OnHeaderChecked(HeaderChecked);
  88. for i:= 0 to ListView_GetItemCount(hListView)-1 do
  89. ListView_SetCheckState(hListView, i, HeaderChecked);
  90. end;
  91. end;
  92.  
  93. // Notificación del cambio del CheckBox de un Item
  94. if pNMH.hdr.code = LVN_ITEMCHANGED then
  95. begin
  96. pNMLV:= PNMLISTVIEW(LParam);
  97. if (pNMLV.uChanged and LVIF_STATE) <> 0 then
  98. begin
  99. if @OnItemChecked <> nil then
  100. OnItemChecked(pNMLV.iItem, ListView_GetCheckState(hListView, pNMLV.iItem) <> 0);
  101. SetHeaderCheck;
  102. end;
  103. end;
  104. end;
  105. Result:= CallWindowProc(OldWndProc, Handle, Msg, WParam, LParam);
  106. end;
  107.  
  108. procedure TLVCheckBox.SetHeaderCheck;
  109. var
  110. HeaderChecked: boolean;
  111. i: integer;
  112. hHeader: HWND;
  113. HDI: HD_ITEM;
  114. begin
  115. HeaderChecked:= true;
  116. for i:= 0 to ListView_GetItemCount(hListView)-1 do
  117. begin
  118. if ListView_GetCheckState(hListView, i) = 0 then
  119. begin
  120. HeaderChecked:= false;
  121. break;
  122. end;
  123. end;
  124.  
  125. hHeader:= ListView_GetHeader(hListView);
  126. HDI.mask:= HDI_FORMAT;
  127. Header_GetItem(hHeader, 0, HDI);
  128. if HeaderChecked then
  129. HDI.fmt:= HDI.fmt or HDF_CHECKED
  130. else
  131. HDI.fmt:= HDI.fmt and not HDF_CHECKED;
  132. Header_SetItem(hHeader, 0, HDI);
  133. end;
  134.  
  135. procedure TLVCheckBox.SetHandle(hLV: HWND);
  136. var
  137. hHeader: HWND;
  138. HD: HD_ITEM;
  139. begin
  140. if (hLV <> INVALID_HANDLE_VALUE) and (hLV <> hListView) then
  141. begin
  142. if hLV = 0 then
  143. begin
  144. SetWindowLong(GetParent(hListView), GWL_USERDATA, 0);
  145. SetWindowLong(GetParent(hListView), GWL_WNDPROC, LongInt(OldWndProc));
  146. end;
  147. if hLV <> 0 then
  148. begin
  149. ListView_SetExtendedListViewStyle(hLV, LVS_EX_CHECKBOXES or LVS_EX_FULLROWSELECT);
  150. //hHeader = (HANDLE)SendMessage(hListView, LVM_GETHEADER, 0, 0);
  151. hHeader:= ListView_GetHeader(hLV);
  152. SetWindowLong(hHeader, GWL_STYLE, GetWindowLong(hHeader, GWL_STYLE) or HDS_CHECKBOXES);
  153. ZeroMemory(@HD, sizeof(HD_ITEM));
  154. HD.mask:= HDI_FORMAT;
  155. Header_GetItem(hHeader, 0, HD);
  156. HD.fmt:= HD.fmt or HDF_CHECKBOX or HDF_FIXEDWIDTH;
  157. Header_SetItem(hHeader, 0, HD);
  158.  
  159. SetWindowLong(GetParent(hLV), GWL_USERDATA, LongInt(self));
  160. OldWndProc:= Pointer(SetWindowLong(GetParent(hLV), GWL_WNDPROC, LongInt(@DefWndProc)));
  161. end;
  162. hListView:= hLV;
  163. end;
  164. end;
  165.  
  166. destructor TLVCheckBox.Destroy;
  167. begin
  168. OnHeaderChecked:= nil;
  169. OnItemChecked:= nil;
  170. SetHandle(0);
  171. end;
  172.  
  173. end.

Un ejemplo de uso:


delphi
  1. unit Unit1;
  2.  
  3. interface
  4.  
  5. uses
  6. Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  7. Dialogs, ComCtrls, CommCtrl, XPMan, LVCheckBox, StdCtrls;
  8.  
  9. type
  10. TForm1 = class(TForm)
  11. ListView1: TListView;
  12. XPManifest1: TXPManifest;
  13. Button1: TButton;
  14. Button2: TButton;
  15. procedure FormCreate(Sender: TObject);
  16. procedure Button1Click(Sender: TObject);
  17. procedure Button2Click(Sender: TObject);
  18. procedure FormClose(Sender: TObject; var Action: TCloseAction);
  19. private
  20. LVC: TLVCheckBox;
  21. procedure OnHeaderChecked(Checked: boolean);
  22. procedure OnItemChecked(Index: integer; Checked: boolean);
  23.  
  24. public
  25. { Public declarations }
  26. end;
  27.  
  28. var
  29. Form1: TForm1;
  30.  
  31. implementation
  32.  
  33. {$R *.dfm}
  34.  
  35. procedure TForm1.FormCreate(Sender: TObject);
  36. begin
  37. LVC:= TLVCheckBox.Create(ListView1.Handle);
  38. LVC.OnHeaderChecked:= OnHeaderChecked;
  39. LVC.OnItemChecked:= OnItemChecked;
  40. end;
  41.  
  42. procedure TForm1.Button1Click(Sender: TObject);
  43. begin
  44. LVC.SetHandle(ListView1.Handle);
  45. LVC.OnHeaderChecked:= OnHeaderChecked;
  46. LVC.OnItemChecked:= OnItemChecked;
  47. end;
  48.  
  49. procedure TForm1.Button2Click(Sender: TObject);
  50. begin
  51. LVC.SetHandle(0);
  52. end;
  53.  
  54. procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
  55. begin
  56. LVC.Free;
  57. end;
  58.  
  59.  
  60. procedure TForm1.OnHeaderChecked(Checked: boolean);
  61. begin
  62. Windows.Beep(500, 100);
  63. end;
  64.  
  65. procedure TForm1.OnItemChecked(Index: integer; Checked: boolean);
  66. begin
  67. Windows.Beep(1000, 100);
  68. end;
  69.  
  70.  
  71. end.

Probado en Delphi 7 y Berlin.
 
 
 25118827b516a26c65a95afab864eb4co.gif
 

 

 

Subo el código.

 

 

Saludos.

Archivos adjuntos


  • 0

#2 Agustin Ortu

Agustin Ortu

    Advanced Member

  • Moderadores
  • PipPipPip
  • 831 mensajes
  • LocationArgentina

Escrito 20 enero 2017 - 10:50

No tienen los ListView una propiedad Checkboxes que hace justamente esto? O es algo que agregaron más adelante?
  • 0

#3 escafandra

escafandra

    Advanced Member

  • Administrador
  • 4.107 mensajes
  • LocationMadrid - España

Escrito 20 enero 2017 - 11:10

No tienen los ListView una propiedad Checkboxes que hace justamente esto? O es algo que agregaron más adelante?

 

La funcionalidad de el checkbox en la cabecera apareció con Vista, en los Items ya estaba en winXP. Ninguna versión de compiladores visuales previos aportan propiedades en ListView para hecerlo por motivos obvios y los posteriores tardan en adaptarse.

 

Mucha gente usa compiladores antiguos como el mítico Delphi7,  Builder 5/6 o antiguas versiones de Visual Studio,  en ellos hay que usar trucos cómo el publicado y que gestiona el propio Windows solo en lo referente a la visualización de los checkboxes y notificaciones, no en cuanto a su funcionalidad.

 

En el truco he programado que al marcar la cabecera se marquen los Items pero podríamos hacer otra cosa, windows ahí no se mete.

 

El caso es parecido al de las flechitas del ListView, Windows permite ponerlas pero no por defecto.

 

El truco podría colocar a capón controles checkbox para funcionar en WinXP,  puesto que esa versión no lo contempla, pero no lo he hecho.

 

El truco sirve también como estudio de como conocer y manejar las notificaciones del ListView de Windows a bajo nivel que es lo que usan los componentes de la VCL.

 

 

Saludos.


  • 1

#4 escafandra

escafandra

    Advanced Member

  • Administrador
  • 4.107 mensajes
  • LocationMadrid - España

Escrito 20 enero 2017 - 05:39

Publico también la versión C++ probada en BCB5, BCB6 y Berlin

cpp
  1. //--------------------------------------------------------------------------------------------------
  2. // TLVCheckBox (Versión Hook estilo C++)
  3. // escafandra 2017
  4. // Clase para manejo de CheckBox en la cabecera e Items de un ListView
  5.  
  6.  
  7. #ifndef LVCheckBoxCPP
  8. #define LVCheckBoxCPP
  9.  
  10. #include <Windows.h>
  11. #include <Commctrl.h>
  12.  
  13. #define HDF_CHECKBOX   0x0040
  14. #define HDF_CHECKED    0x0080
  15. #define HDF_FIXEDWIDTH 0x0100
  16.  
  17. #define HDS_CHECKBOXES 0x0400
  18.  
  19. #ifndef HDN_ITEMSTATEICONCLICK
  20.   #define HDN_ITEMSTATEICONCLICK 0xFFFFFEC4
  21. #endif
  22.  
  23. #ifndef STRICT
  24.   typedef int  (__stdcall *PLRESULT)();
  25. #else
  26.   typedef WNDPROC PLRESULT;
  27. #endif
  28.  
  29. //typedef void (__fastcall *POnHeaderChecked)(bool Checked);
  30. //typedef void (__fastcall *POnItemChecked)(int Item, bool Checked);
  31. typedef void __fastcall(__closure* POnItemChecked)(int Item, bool Checked);
  32. typedef void __fastcall(__closure* POnHeaderChecked)(bool Checked);
  33.  
  34.  
  35. class TLVCheckBox
  36. {
  37.   private:
  38.   HWND hListView;
  39.   int Focusfmt;
  40.   int FocusIndex;
  41.   PLRESULT OldWndProc;
  42.   static LRESULT __stdcall DefWndProc(HWND hWnd, UINT Msg, WPARAM WParam, LPARAM LParam)
  43.   {
  44.     TLVCheckBox* pLVCheckBox = (TLVCheckBox*)GetWindowLongPtr(hWnd, GWL_USERDATA);
  45.     if(pLVCheckBox)
  46.       return pLVCheckBox->WndProc(hWnd, Msg, WParam, LParam);
  47.     else
  48.       return DefWindowProc(hWnd, Msg, WParam, LParam);
  49.   }
  50.  
  51.   LRESULT __stdcall WndProc(HWND hWnd, UINT Msg, WPARAM WParam, LPARAM LParam)
  52.   {
  53.     if(Msg == WM_NOTIFY){
  54.       // Notificación del cambio del CheckBox de la cabecera
  55.       NMHEADER* pNMH = (NMHEADER*)LParam;
  56.       //if(pNMH->hdr.hwndFrom == hListView)
  57.         if(pNMH->hdr.code == HDN_ITEMSTATEICONCLICK){
  58.           if(pNMH->pitem->mask & HDI_FORMAT && pNMH->pitem->fmt & HDF_CHECKBOX){
  59.             bool HeaderChecked = !(pNMH->pitem->fmt & HDF_CHECKED);
  60.             if(OnHeaderChecked)
  61.               OnHeaderChecked(HeaderChecked);
  62.             for(int i = 0; i < ListView_GetItemCount(hListView); i++)
  63.               ListView_SetCheckState(hListView, i, HeaderChecked);
  64.           }
  65.         }
  66.       // Notificación del cambio del CheckBox de un Item
  67.       if(pNMH->hdr.code == LVN_ITEMCHANGED){
  68.         NMLISTVIEW* pNMLV = (NMLISTVIEW*)LParam;
  69.         if (pNMLV->uChanged & LVIF_STATE){
  70.           if(OnItemChecked)
  71.             OnItemChecked(pNMLV->iItem, ListView_GetCheckState(hListView, pNMLV->iItem));
  72.           SetHeaderCheck();
  73.         }
  74.       }
  75.     }
  76.     return CallWindowProc(OldWndProc, hWnd, Msg, WParam, LParam);
  77.   }
  78.  
  79.   public:
  80.   POnHeaderChecked OnHeaderChecked;
  81.   POnItemChecked OnItemChecked;
  82.   void __fastcall SetHeaderCheck()
  83.   {
  84.     BOOL HeaderChecked = TRUE;
  85.     for(int i = 0; i < ListView_GetItemCount(hListView); i++) {
  86.       if(!ListView_GetCheckState(hListView, i)){
  87.          HeaderChecked = FALSE;
  88.          break;
  89.       }
  90.     }
  91.  
  92.     HWND hHeader = ListView_GetHeader(hListView);
  93.     HDITEM HDI = {0};
  94.     HDI.mask = HDI_FORMAT;
  95.     Header_GetItem(hHeader, 0, &HDI);
  96.     if(HeaderChecked)
  97.       HDI.fmt |= HDF_CHECKED;
  98.     else
  99.       HDI.fmt &= ~HDF_CHECKED;
  100.     Header_SetItem(hHeader, 0, &HDI);
  101.   }
  102.  
  103.   void SetHandle(HWND hLV)
  104.   {
  105.     if(hLV != INVALID_HANDLE_VALUE && hLV != hListView){
  106.       if(!hLV){
  107.         SetWindowLong(GetParent(hListView), GWL_USERDATA, 0);
  108.         SetWindowLong(GetParent(hListView), GWL_WNDPROC, (LONG)OldWndProc);
  109.       }
  110.       if(hLV){
  111.         ListView_SetExtendedListViewStyle(hLV, LVS_EX_CHECKBOXES | LVS_EX_FULLROWSELECT);
  112.         //hHeader = (HANDLE)SendMessage(hLV, LVM_GETHEADER, 0, 0);
  113.         HWND hHeader = ListView_GetHeader(hLV);
  114.         SetWindowLong(hHeader, GWL_STYLE, GetWindowLong(hHeader, GWL_STYLE) | HDS_CHECKBOXES);
  115.         HDITEM HD = {0};
  116.         HD.mask = HDI_FORMAT;
  117.         Header_GetItem(hHeader, 0, &HD);
  118.         HD.fmt |= HDF_CHECKBOX | HDF_FIXEDWIDTH;
  119.         Header_SetItem(hHeader, 0, &HD);
  120.  
  121.         SetWindowLongPtr(GetParent(hLV), GWL_USERDATA, (LONG)this);
  122.         OldWndProc = (PLRESULT)SetWindowLongPtr(GetParent(hLV), GWL_WNDPROC, (LONG)TLVCheckBox::DefWndProc);
  123.       }
  124.       hListView = hLV;
  125.     }
  126.   }
  127.  
  128.   TLVCheckBox(HWND hListView = 0)
  129.   {
  130.     OnHeaderChecked = 0;
  131.     OnItemChecked = 0;
  132.     SetHandle(hListView);
  133.   }
  134.  
  135.   ~TLVCheckBox() {SetHandle(0);}
  136. };
  137. #endif

Y un ejemplo de uso muy similar a Delphi

cpp
  1. //---------------------------------------------------------------------------
  2. #include <vcl.h>
  3. #pragma hdrstop
  4.  
  5. #include "Unit1.h"
  6.  
  7. //---------------------------------------------------------------------------
  8. #pragma package(smart_init)
  9. #pragma resource "*.dfm"
  10. TForm1 *Form1;
  11.  
  12. //---------------------------------------------------------------------------
  13. __fastcall TForm1::TForm1(TComponent* Owner)
  14. : TForm(Owner)
  15. {
  16. LVC.SetHandle(ListView1->Handle);
  17. LVC.OnItemChecked = &OnItemChecked;
  18. LVC.OnHeaderChecked = &OnHeaderChecked;
  19. }
  20. //---------------------------------------------------------------------------
  21.  
  22. void __fastcall TForm1::OnHeaderChecked(bool Checked)
  23. {
  24. Beep(500, 100);
  25. // Aquí seguimos actuando según el cambio del ChecBox de la cabecera
  26. }
  27. //---------------------------------------------------------------------------
  28.  
  29. void __fastcall TForm1::OnItemChecked(int Item, bool Checked)
  30. {
  31. Beep(1000, 100);
  32. // Aquí seguimos actuando según el Item y si cambia su estado a Checked = true
  33. }
  34. //---------------------------------------------------------------------------
  35.  
  36. void __fastcall TForm1::Button1Click(TObject *Sender)
  37. {
  38. LVC.SetHandle(ListView1->Handle);
  39. LVC.OnItemChecked = &OnItemChecked;
  40. LVC.OnHeaderChecked = &OnHeaderChecked;
  41. }
  42. //---------------------------------------------------------------------------
  43.  
  44. void __fastcall TForm1::Button2Click(TObject *Sender)
  45. {
  46. LVC.SetHandle(0);
  47. }
  48. //---------------------------------------------------------------------------


25118827b516a26c65a95afab864eb4co.gif

Saludos.

Archivos adjuntos


  • 1

#5 monchito_elroro

monchito_elroro

    Advanced Member

  • Miembros
  • PipPipPip
  • 259 mensajes

Escrito 19 febrero 2017 - 10:42

Gracias maestro por compartir sus conocimientos con los que menos sabemos :) :)

Que tenga un excelente inicio de semana (y)


  • 0




IP.Board spam blocked by CleanTalk.