Почему дочерние элементы управления TStringGrid не работают должным образом?
Я ставлю галочки (TCheckBox
) в сетке строк (TStringGrid
) в первом столбце. Флажки отображаются нормально, расположены правильно и реагируют на мышь, светясь при наведении на них курсора. Однако, когда я нажимаю на них, они не переключаются. Они реагируют на щелчок и выделяют, но, наконец, фактическое Checked
свойство не меняется. Что делает его более загадочным, так это то, что у меня нет кода, изменяющего эти значения, когда они есть, и при этом у меня даже нет OnClick
событие, назначенное этим флажкам. Кроме того, по умолчанию эти флажки сняты, но при отображении они отмечены.
Флажки создаются вместе с каждой записью, которая добавляется в список, и на нее ссылаются внутри указателя записи, который назначен объекту в ячейке, где должен быть установлен флажок.
Взлом строки сетки для подсветки ячеек:
type
THackStringGrid = class(TStringGrid); //used later...
Запись, содержащая флажок:
PImageLink = ^TImageLink;
TImageLink = record
...other stuff...
Checkbox: TCheckbox;
ShowCheckbox: Bool;
end;
Создание / Уничтожение флажка:
function NewImageLink(const AFilename: String): PImageLink;
begin
Result:= New(PImageLink);
...other stuff...
Result.Checkbox:= TCheckbox.Create(nil);
Result.Checkbox.Caption:= '';
end;
procedure DestroyImageLink(AImageLink: PImageLink);
begin
AImageLink.Checkbox.Free;
Dispose(AImageLink);
end;
Добавление строк в сетку:
//...after clearing grid...
//L = TStringList of original filenames
if L.Count > 0 then
lstFiles.RowCount:= L.Count + 1
else
lstFiles.RowCount:= 2; //in case there are no records
for X := 0 to L.Count - 1 do begin
S:= L[X];
Link:= NewImageLink(S); //also creates checkbox
Link.Checkbox.Parent:= lstFiles;
Link.Checkbox.Visible:= Link.ShowCheckbox;
Link.Checkbox.Checked:= False;
Link.Checkbox.BringToFront;
lstFiles.Objects[0,X+1]:= Pointer(Link);
lstFiles.Cells[1, X+1]:= S;
end;
Обработчик события OnDrawCell Grid:
procedure TfrmMain.lstFilesDrawCell(Sender: TObject; ACol, ARow: Integer;
Rect: TRect; State: TGridDrawState);
var
Link: PImageLink;
CR: TRect;
begin
if (ARow > 0) and (ACol = 0) then begin
Link:= PImageLink(lstFiles.Objects[0,ARow]); //Get record pointer
CR:= lstFiles.CellRect(0, ARow); //Get cell rect
Link.Checkbox.Width:= Link.Checkbox.Height;
Link.Checkbox.Left:= CR.Left + (CR.Width div 2) - (Link.Checkbox.Width div 2);
Link.Checkbox.Top:= CR.Top;
if not Link.Checkbox.Visible then begin
lstFiles.Canvas.Brush.Color:= lstFiles.Color;
lstFiles.Canvas.Brush.Style:= bsSolid;
lstFiles.Canvas.Pen.Style:= psClear;
lstFiles.Canvas.FillRect(CR);
if lstFiles.Row = ARow then
THackStringGrid(lstFiles).DrawCellHighlight(CR, State, ACol, ARow);
end;
end;
end;
Вот как это выглядит при нажатии...
Что может быть причиной этого? Это определенно не меняет Checked
собственность в любом месте моего кода. При размещении в сетке из-за самих флажков происходит странное поведение.
РЕДАКТИРОВАТЬ
Я сделал краткий тест, я поместил обычный TCheckBox
на форме. Проверить / снять флажок нормально. Тогда в моей форме OnShow
событие, я изменил флажок Parent
к этой сетке. На этот раз я получаю такое же поведение, не переключаясь при нажатии. Поэтому кажется, что TCheckBox
не реагирует должным образом, когда у него есть другой элемент управления в качестве родителя. Как это побороть?
3 ответа
TStringGrid
"s WMCommand
Обработчик не позволяет дочерним элементам управления обрабатывать сообщения (кроме InplaceEdit).
Таким образом, вы можете использовать, например, вставленный класс (на основе кода Питера Белоу) или нарисовать элементы управления руками, как советуют некоторые люди. Вот код вставленного класса:
uses
Grids;
type
TStringGrid = class(Grids.TStringGrid)
private
procedure WMCommand(var AMessage: TWMCommand); message WM_COMMAND;
end;
implementation
procedure TStringGrid.WMCommand(var AMessage: TWMCommand);
begin
if EditorMode and (AMessage.Ctl = InplaceEditor.Handle) then
inherited
else
if AMessage.Ctl <> 0 then
begin
AMessage.Result := SendMessage(AMessage.Ctl, CN_COMMAND,
TMessage(AMessage).WParam, TMessage(AMessage).LParam);
end;
end;
В Delphi7 по крайней мере я делаю это:
Вам нужно нарисовать флажок в ячейке и синхронизировать его с массивом логических значений (здесь fChecked[]
), который указывает состояние флажка в каждой строке. Затем в DrawCell
часть TStringGrid
:
var
cbstate: integer;
begin
...
if fChecked[Arow] then cbState:=DFCS_CHECKED else cbState:=DFCS_BUTTONCHECK;
DrawFrameControl(StringGrid.canvas.handle, Rect, DFC_BUTTON, cbState);
...
end;
Чтобы получить флажок для ответа на пробел, используйте KeyDown
событие и заставить перекрасить:
if (Key = VK_SPACE) And (col=ColWithCheckBox) then begin
fChecked[row]:=not fChecked[row];
StringGrid.Invalidate;
key:=0;
end;
Аналогичный подход необходим для OnClick
метод.
Можно ли использовать VirtualTreeView в режиме toReportMode (эмуляция TListView) вместо сетки?
Можете ли вы использовать TDBGrid поверх таблицы в памяти, такой как NexusDB или TClientDataSet?
Гадкий подход будет представлять флажок как письмо с пользовательским шрифтом - как WinDings или http://fortawesome.github.com/Font-Awesome
Последний вариант наиболее прост в реализации, но наиболее уродлив и нагляден и негибок в обслуживании - бизнес-логика смешивается с обработчиками событий VCL