source: ProjectTemplates/FileMenuProject/Packages/Common/UListViewSort.pas

Last change on this file was 507, checked in by chronos, 7 years ago
  • Modified: Update Common package.
  • Added: Support for color theme.
File size: 15.3 KB
Line 
1unit UListViewSort;
2
3// Date: 2010-11-03
4
5{$mode delphi}
6
7interface
8
9uses
10 {$IFDEF Windows}Windows, CommCtrl, {$ENDIF}Classes, Graphics, ComCtrls, SysUtils,
11 Controls, DateUtils, Dialogs, SpecializedList, Forms, Grids, StdCtrls, ExtCtrls,
12 LclIntf, LMessages, LclType, LResources;
13
14type
15 TSortOrder = (soNone, soUp, soDown);
16
17 TListViewSort = class;
18
19 TCompareEvent = function (Item1, Item2: TObject): Integer of object;
20 TListFilterEvent = procedure (ListViewSort: TListViewSort) of object;
21
22 { TListViewSort }
23
24 TListViewSort = class(TComponent)
25 private
26 FListView: TListView;
27 FOnCompareItem: TCompareEvent;
28 FOnFilter: TListFilterEvent;
29 FOnCustomDraw: TLVCustomDrawItemEvent;
30 {$IFDEF Windows}FHeaderHandle: HWND;{$ENDIF}
31 FColumn: Integer;
32 FOrder: TSortOrder;
33 FOldListViewWindowProc: TWndMethod;
34 FOnColumnWidthChanged: TNotifyEvent;
35 procedure DoColumnBeginResize(const AColIndex: Integer);
36 procedure DoColumnResized(const AColIndex: Integer);
37 procedure DoColumnResizing(const AColIndex, AWidth: Integer);
38 procedure SetListView(const Value: TListView);
39 procedure ColumnClick(Sender: TObject; Column: TListColumn);
40 procedure Sort(Compare: TCompareEvent);
41 procedure DrawCheckMark(Item: TListItem; Checked: Boolean);
42 procedure GetCheckBias(var XBias, YBias, BiasTop, BiasLeft: Integer;
43 const ListView: TListView);
44 procedure ListViewCustomDrawItem(Sender: TCustomListView;
45 Item: TListItem; State: TCustomDrawState; var DefaultDraw: Boolean);
46 procedure ListViewClick(Sender: TObject);
47 procedure UpdateColumns;
48 procedure SetColumn(const Value: Integer);
49 procedure SetOrder(const Value: TSortOrder);
50 {$IFDEF WINDOWS}
51 procedure NewListViewWindowProc(var AMsg: TMessage);
52 {$ENDIF}
53 public
54 List: TListObject;
55 Source: TListObject;
56 constructor Create(AOwner: TComponent); override;
57 destructor Destroy; override;
58 function CompareTime(Time1, Time2: TDateTime): Integer;
59 function CompareInteger(Value1, Value2: Integer): Integer;
60 function CompareString(Value1, Value2: string): Integer;
61 function CompareBoolean(Value1, Value2: Boolean): Integer;
62 procedure Refresh;
63 published
64 property ListView: TListView read FListView write SetListView;
65 property OnCompareItem: TCompareEvent read FOnCompareItem
66 write FOnCompareItem;
67 property OnFilter: TListFilterEvent read FOnFilter
68 write FOnFilter;
69 property OnCustomDraw: TLVCustomDrawItemEvent read FOnCustomDraw
70 write FOnCustomDraw;
71 property OnColumnWidthChanged: TNotifyEvent read FOnColumnWidthChanged
72 write FOnColumnWidthChanged;
73 property Column: Integer read FColumn write SetColumn;
74 property Order: TSortOrder read FOrder write SetOrder;
75 end;
76
77 { TListViewFilter }
78
79 TListViewFilter = class(TWinControl)
80 private
81 FOnChange: TNotifyEvent;
82 FStringGrid1: TStringGrid;
83 procedure GridDoOnKeyUp(Sender: TObject; var Key: Word; Shift: TShiftState);
84 procedure GridDoOnResize(Sender: TObject);
85 public
86 constructor Create(AOwner: TComponent); override;
87 procedure UpdateFromListView(ListView: TListView);
88 function TextEntered: Boolean;
89 function TextEnteredCount: Integer;
90 function TextEnteredColumn(Index: Integer): Boolean;
91 function GetColValue(Index: Integer): string;
92 property StringGrid: TStringGrid read FStringGrid1 write FStringGrid1;
93 published
94 property OnChange: TNotifyEvent read FOnChange write FOnChange;
95 property Align;
96 property Anchors;
97 property BorderSpacing;
98 end;
99
100procedure Register;
101
102
103implementation
104
105procedure Register;
106begin
107 RegisterComponents('Common', [TListViewSort, TListViewFilter]);
108end;
109
110{ TListViewFilter }
111
112procedure TListViewFilter.GridDoOnKeyUp(Sender: TObject; var Key: Word;
113 Shift: TShiftState);
114begin
115 if Assigned(FOnChange) then
116 FOnChange(Self);
117end;
118
119procedure TListViewFilter.GridDoOnResize(Sender: TObject);
120begin
121 FStringGrid1.DefaultRowHeight := FStringGrid1.Height;
122end;
123
124constructor TListViewFilter.Create(AOwner: TComponent);
125begin
126 inherited Create(AOwner);
127 FStringGrid1 := TStringGrid.Create(Self);
128 FStringGrid1.Align := alClient;
129 FStringGrid1.Parent := Self;
130 FStringGrid1.Visible := True;
131 FStringGrid1.ScrollBars := ssNone;
132 FStringGrid1.FixedCols := 0;
133 FStringGrid1.FixedRows := 0;
134 FStringGrid1.RowCount := 1;
135 FStringGrid1.Options := [goFixedHorzLine, goFixedVertLine, goVertLine,
136 goHorzLine, goRangeSelect, goEditing, goAlwaysShowEditor, goSmoothScroll];
137 FStringGrid1.OnKeyUp := GridDoOnKeyUp;
138 FStringGrid1.OnResize := GridDoOnResize;
139end;
140
141procedure TListViewFilter.UpdateFromListView(ListView: TListView);
142var
143 I: Integer;
144begin
145 with FStringGrid1 do begin
146 Options := Options - [goEditing, goAlwaysShowEditor];
147 //Columns.Clear;
148 while Columns.Count > ListView.Columns.Count do Columns.Delete(Columns.Count - 1);
149 while Columns.Count < ListView.Columns.Count do Columns.Add;
150 for I := 0 to ListView.Columns.Count - 1 do begin
151 Columns[I].Width := ListView.Columns[I].Width;
152 end;
153 Options := Options + [goEditing, goAlwaysShowEditor];
154 end;
155end;
156
157function TListViewFilter.TextEntered: Boolean;
158begin
159 Result := TextEnteredCount > 0;
160end;
161
162function TListViewFilter.TextEnteredCount: Integer;
163var
164 I: Integer;
165begin
166 Result := 0;
167 for I := 0 to FStringGrid1.ColCount - 1 do begin
168 if FStringGrid1.Cells[I, 0] <> '' then begin
169 Inc(Result);
170 end;
171 end;
172end;
173
174function TListViewFilter.TextEnteredColumn(Index: Integer): Boolean;
175begin
176 Result := FStringGrid1.Cells[Index, 0] <> '';
177end;
178
179function TListViewFilter.GetColValue(Index: Integer): string;
180begin
181 if (Index >= 0) and (Index < StringGrid.Columns.Count) then
182 Result := StringGrid.Cells[Index, 0]
183 else Result := '';
184end;
185
186{ TListViewSort }
187
188{$IFDEF WINDOWS}
189procedure TListViewSort.NewListViewWindowProc(var AMsg: TMessage);
190var
191 vColWidth: Integer;
192 vMsgNotify: TLMNotify absolute AMsg;
193 Code: Integer;
194begin
195 // call the old WindowProc of ListView
196 FOldListViewWindowProc(AMsg);
197
198 // Currently we care only with WM_NOTIFY message
199 if AMsg.Msg = WM_NOTIFY then
200 begin
201 Code := NMHDR(PHDNotify(vMsgNotify.NMHdr)^.Hdr).Code;
202 case Code of
203 HDN_ENDTRACKA, HDN_ENDTRACKW:
204 DoColumnResized(PHDNotify(vMsgNotify.NMHdr)^.Item);
205
206 HDN_BEGINTRACKA, HDN_BEGINTRACKW:
207 DoColumnBeginResize(PHDNotify(vMsgNotify.NMHdr)^.Item);
208
209 HDN_TRACKA, HDN_TRACKW:
210 begin
211 vColWidth := -1;
212 if (PHDNotify(vMsgNotify.NMHdr)^.PItem<>nil)
213 and (PHDNotify(vMsgNotify.NMHdr)^.PItem^.Mask and HDI_WIDTH <> 0)
214 then
215 vColWidth := PHDNotify(vMsgNotify.NMHdr)^.PItem^.cxy;
216
217 DoColumnResizing(PHDNotify(vMsgNotify.NMHdr)^.Item, vColWidth);
218 end;
219 end;
220 end;
221end;
222{$ENDIF}
223
224procedure TListViewSort.DoColumnBeginResize(const AColIndex: Integer);
225begin
226end;
227
228procedure TListViewSort.DoColumnResizing(const AColIndex, AWidth: Integer);
229begin
230end;
231
232procedure TListViewSort.DoColumnResized(const AColIndex: Integer);
233begin
234 if Assigned(FOnColumnWidthChanged) then
235 FOnColumnWidthChanged(Self);
236end;
237
238procedure TListViewSort.ColumnClick(Sender: TObject; Column: TListColumn);
239begin
240 if Column.Index = Self.Column then begin
241 if FOrder = soUp then FOrder := soDown
242 else if FOrder = soDown then FOrder := soUp
243 else FOrder := soUp;
244 end else Self.Column := Column.Index;
245 Refresh;
246 UpdateColumns;
247end;
248
249procedure TListViewSort.SetOrder(const Value: TSortOrder);
250begin
251 FOrder := Value;
252 UpdateColumns;
253end;
254
255procedure TListViewSort.SetColumn(const Value: Integer);
256begin
257 FColumn := Value;
258 UpdateColumns;
259end;
260
261procedure TListViewSort.SetListView(const Value: TListView);
262begin
263 if FListView = Value then Exit;
264 if Assigned(FListView) then
265 ListView.WindowProc := FOldListViewWindowProc;
266 FListView := Value;
267 FListView.OnColumnClick := ColumnClick;
268 FListView.OnCustomDrawItem := ListViewCustomDrawItem;
269 FListView.OnClick := ListViewClick;
270 FOldListViewWindowProc := FListView.WindowProc;
271 {$IFDEF WINDOWS}
272 FListView.WindowProc := NewListViewWindowProc;
273 {$ENDIF}
274end;
275
276procedure TListViewSort.Sort(Compare: TCompareEvent);
277begin
278 if (List.Count > 0) then
279 List.Sort(Compare);
280end;
281
282procedure TListViewSort.Refresh;
283begin
284 if Assigned(FOnFilter) then FOnFilter(Self)
285 else if Assigned(Source) then
286 List.Assign(Source) else
287 List.Clear;
288 if ListView.Items.Count <> List.Count then
289 ListView.Items.Count := List.Count;
290 if Assigned(FOnCompareItem) and (Order <> soNone) then Sort(FOnCompareItem);
291 //ListView.Items[-1]; // Workaround for not show first row if selected
292 ListView.Refresh;
293 // Workaround for not working item selection on first row
294 //if not Assigned(ListView.Selected) then begin
295 // ListView.Items.Count := 0;
296 // ListView.Items.Count := List.Count;
297 //end;
298 //if ListView.Items.Count > 0 then
299 // ListView.Items[0].Selected := True;
300 //ListView.Selected := nil;
301 UpdateColumns;
302end;
303
304const
305 //W_64: Integer = 64; {Width of thumbnail in ICON view mode}
306 H_64: Integer = 64; {Height of thumbnail size}
307 CheckWidth: Integer = 14; {Width of check mark box}
308 CheckHeight: Integer = 14; {Height of checkmark}
309 CheckBiasTop: Integer = 2; {This aligns the checkbox to be in centered}
310 CheckBiasLeft: Integer = 3; {In the row of the list item display}
311
312function TListViewSort.CompareBoolean(Value1, Value2: Boolean): Integer;
313begin
314 if Value1 > Value2 then Result := 1
315 else if Value1 < Value2 then Result := -1
316 else Result := 0;
317end;
318
319function TListViewSort.CompareInteger(Value1, Value2: Integer): Integer;
320begin
321 if Value1 > Value2 then Result := 1
322 else if Value1 < Value2 then Result := -1
323 else Result := 0;
324end;
325
326function TListViewSort.CompareString(Value1, Value2: string): Integer;
327begin
328 Result := AnsiCompareStr(Value1, Value2);
329// if Value1 > Value2 then Result := -1
330// else if Value1 < Value2 then Result := 1
331// else Result := 0;
332end;
333
334function TListViewSort.CompareTime(Time1, Time2: TDateTime): Integer;
335begin
336 Result := DateUtils.CompareDateTime(Time1, Time2);
337end;
338
339constructor TListViewSort.Create(AOwner: TComponent);
340begin
341 inherited;
342 List := TListObject.Create;
343 List.OwnsObjects := False;
344end;
345
346destructor TListViewSort.Destroy;
347begin
348 List.Free;
349 inherited;
350end;
351
352procedure TListViewSort.DrawCheckMark(Item: TListItem; Checked:
353 Boolean);
354var
355 TP1: TPoint;
356 XBias, YBias: Integer;
357 PenColor: TColor;
358 BrushColor: TColor;
359 BiasTop, BiasLeft: Integer;
360 Rect1: TRect;
361 lRect: TRect;
362 ItemLeft: Integer;
363begin
364 XBias := 0;
365 YBias := 0;
366 BiasTop := 0;
367 BiasLeft := 0;
368 Item.Left := 0;
369 GetCheckBias(XBias, YBias, BiasTop, BiasLeft, ListView);
370 PenColor := ListView.Canvas.Pen.Color;
371 BrushColor := ListView.Canvas.Brush.Color;
372 //TP1 := Item.GetPosition;
373 lRect := Item.DisplayRect(drBounds); // Windows 7 workaround
374 TP1.X := lRect.Left;
375 TP1.Y := lRect.Top;
376 //ShowMessage(IntToStr(Item.Index) + ', ' + IntToStr(GetScrollPos(Item.ListView.Handle, SB_VERT)) + ' ' +
377 // IntToHex(Integer(Item), 8) + ', ' + IntToStr(TP1.X) + ', ' + IntToStr(TP1.Y));
378
379// if Checked then
380 ListView.Canvas.Brush.Color := clWhite;
381 ItemLeft := Item.Left;
382 ItemLeft := 23; // Windows 7 workaround
383
384 Rect1.Left := ItemLeft - CheckWidth - BiasLeft + 1 + XBias;
385 //ShowMessage(IntToStr(Tp1.Y) + ', ' + IntToStr(BiasTop) + ', ' + IntToStr(XBias));
386 Rect1.Top := Tp1.Y + BiasTop + 1 + YBias;
387 Rect1.Right := ItemLeft - BiasLeft - 1 + XBias;
388 Rect1.Bottom := Tp1.Y + BiasTop + CheckHeight - 1 + YBias;
389 //ShowMessage(IntToStr(Rect1.Left) + ', ' + IntToStr(Rect1.Top) + ', ' + IntToStr(Rect1.Right) + ', ' + IntToStr(Rect1.Bottom));
390
391 ListView.Canvas.FillRect(Rect1);
392 //if Checked then ListView.Canvas.Brush.Color := clBlack
393 ListView.Canvas.Brush.Color := clBlack;
394 ListView.Canvas.FrameRect(Rect1);
395 ListView.Canvas.FrameRect(Rect(Rect1.Left - 1, Rect1.Top - 1,
396 Rect1.Right + 1, Rect1.Bottom + 1));
397 if Checked then begin
398 ListView.Canvas.Pen.Color := clBlack;
399 ListView.Canvas.MoveTo(ItemLeft - BiasLeft - 2 + XBias - 2,
400 Tp1.Y + BiasTop + 3 + YBias);
401 ListView.Canvas.LineTo(ItemLeft - BiasLeft - (CheckWidth div 2) + XBias,
402 Tp1.Y + BiasTop + (CheckHeight - 4) + YBias);
403 ListView.Canvas.LineTo(ItemLeft - BiasLeft - (CheckWidth - 3) + XBias,
404 Tp1.Y + BiasTop + (CheckHeight div 2) + YBias - 1);
405
406 ListView.Canvas.MoveTo(ItemLeft - BiasLeft - 2 - 1 + XBias - 2,
407 Tp1.Y + BiasTop + 3 + YBias);
408 ListView.Canvas.LineTo(ItemLeft - BiasLeft - (CheckWidth div 2) - 1 + XBias,
409 Tp1.Y + BiasTop + (CheckHeight - 4) + YBias);
410 ListView.Canvas.LineTo(ItemLeft - BiasLeft - (CheckWidth - 3) - 1 + XBias,
411 Tp1.Y + BiasTop + (CheckHeight div 2) + YBias - 1);
412 end;
413 //ListView.Canvas.Brush.Color := ListView.Color;
414 ListView.Canvas.Brush.Color := BrushColor;
415 ListView.Canvas.Pen.Color := PenColor;
416end;
417
418procedure TListViewSort.GetCheckBias(var XBias, YBias, BiasTop, BiasLeft: Integer;
419 const ListView: TListView);
420begin
421 XBias := 0;
422 YBias := 0;
423 if ListView.ViewStyle = vsICON then
424 begin
425 YBias := H_64 - CheckHeight;
426 XBias := 0;
427 end;
428 BiasTop := CheckBiasTop;
429 BiasLeft := CheckBiasLeft;
430 if ListView.ViewStyle <> vsReport then
431 begin
432 BiasTop := 0;
433 BiasLeft := 0;
434 end;
435end;
436
437procedure TListViewSort.ListViewCustomDrawItem(Sender: TCustomListView;
438 Item: TListItem; State: TCustomDrawState; var DefaultDraw: Boolean);
439begin
440 if Assigned(Item) then begin
441 if ListView.Checkboxes then
442 DrawCheckMark(Item, Item.Checked);
443 if Assigned(FOnCustomDraw) then
444 FOnCustomDraw(Sender, Item, State, DefaultDraw);
445 end;
446end;
447
448procedure TListViewSort.ListViewClick(Sender: TObject);
449var
450 Item: TListItem;
451 Pos: TPoint;
452 DefaultDraw: Boolean;
453begin
454 Pos := ListView.ScreenToClient(Mouse.CursorPos);
455 Item := ListView.GetItemAt(Pos.X, Pos.Y);
456 //ShowMessage(IntToStr(Item.Index) + ', ' + IntToStr(Pos.X) + ', ' + IntToStr(Pos.Y));
457 if Assigned(Item) and (Pos.X < 20) then begin
458
459 Item.Checked := not Item.Checked;
460 //ShowMessage(IntToStr(Item.Index) + ', ' +BoolToStr(Item.Checked));
461 if Assigned(ListView.OnChange) then
462 ListView.OnChange(Self, Item, ctState);
463 DefaultDraw := False;
464 ListViewCustomDrawItem(ListView, Item, [], DefaultDraw);
465 //ListView.UpdateItems(Item.Index, Item.Index);
466 end;
467end;
468
469procedure TListViewSort.UpdateColumns;
470{$IFDEF Windows}
471const
472 HDF_SORTUP = $0400;
473 HDF_SORTDOWN = $0200;
474 SortOrder: array[TSortOrder] of Word = (0, HDF_SORTUP, HDF_SORTDOWN);
475var
476 Item: THDItem;
477 I: Integer;
478begin
479 if Assigned(FListView) then begin
480 FHeaderHandle := ListView_GetHeader(FListView.Handle);
481 for I := 0 to FListView.Columns.Count - 1 do begin
482 FillChar(Item, SizeOf(THDItem), 0);
483 Item.Mask := HDI_FORMAT;
484 Header_GetItem(FHeaderHandle, I, Item);
485 Item.fmt := Item.fmt and not (HDF_SORTDOWN or HDF_SORTUP);
486 if (Column <> -1) and (I = Column) then
487 Item.fmt := Item.fmt or SortOrder[FOrder];
488 Header_SetItem(FHeaderHandle, I, Item);
489 end;
490 end;
491end;
492{$ELSE}
493begin
494end;
495{$ENDIF}
496
497end.
Note: See TracBrowser for help on using the repository browser.