Delphi: StringGrid, позиция и контекстное меню
У меня проблема с использованием TStringGrid и всплывающего меню
Я хочу знать строку / столбец ячейки, которая была последней активной при выборе элемента из моего всплывающего меню. Однако когда я нажимаю на всплывающее меню, StringGrid.Row возвращается как -1.
Я пытался использовать MouseToCell как часть OnClick, но даже после установки SG.Row он по-прежнему возвращается как -1 в подпрограммах всплывающих меню... Я подозреваю, что проблема в том, что Grid теряет фокус.
Есть ли какие-либо решения для этого, которые не требуют OnClick установки глобальной переменной?
Я использую список действий, связанный с элементами во всплывающем меню, чтобы убедиться в согласованности действий между панелью инструментов и всплывающим меню.
5 ответов
Боюсь, что я не совсем понимаю, что вы имеете в виду. Когда я щелкаю левой кнопкой мыши ячейку в сетке строк, она выделяется, но не тогда, когда я щелкаю ее правой кнопкой мыши. Когда я щелкаю правой кнопкой мыши по нему, появляется всплывающее меню (если назначено), и на MenuItemClick
Я могу легко прочитать row
а также col
в настоящее время выбран. Смотрите пример видео.
Я думаю, что вы действительно хотите это: вы хотите, чтобы щелчки правой кнопкой мыши изменили активную ячейку, а также левой кнопкой мыши. Это легко сделать:
procedure TForm1.StringGrid1MouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
if Button = mbRight then
StringGrid1.Perform(WM_LBUTTONDOWN, 0, MakeLParam(Word(X), Word(Y)));
end;
Другой способ узнать строку выборки в TStringGrid (она действительно единственная):
YourstringGrid.Selection.Top;
YourstringGrid.Selection.Bottom;
Если выбрана только одна строка, они должны совпадать.
я никогда не видел .Selection.···
сбой, хотя я видел, что YourstringGrid.Row, похоже, не может получить строку выбора, часто он возвращает -1, когда вы думаете, что он должен возвращать другие значения (см. 4 пункта в конце, чтобы понять, почему кажется, что происходит сбой, но это действительно не ошибка, когда возвращается -1, ... это понятие недооценено).
Выделение и ячейка с фуками не одно и то же... .Selection
для выбора, .Row
а также .Col
предназначены для ячейки с фокусом и не имеют ничего общего с выделением, это может быть ячейка с фокусом, в то время как выборка представляет собой общий диапазон ячеек (обе являются разными понятиями).
Кроме того, я обнаружил, что YourstringGrid.Row<>YourstringGrid.Selection.Top
может быть правдой. Когда ячейка, которая имеет фокус, находится не в верхнем ряду выделения.
Некоторые взломы, трюки, код и т. Д., Показанные в Интернете, предназначены только для случаев, когда goRowSelect=False
если установлено значение True, такие подпрограммы не работают должным образом, используйте их с осторожностью.
Подсказка: на TStringGrid, которая имеет goRowSelect=True
очень глючно выделять кодом больше строки, меняя .Selection
иногда не обновлять .Row
(они не изменяют фактическую ячейку, которая имеет фокус), поэтому, если кто-то хочет выбрать только одну строку, лучше назначить значение строки непосредственно .Row
,
Помните: на TStringGrid с goRowSelect=True
говорить о том, какая ячейка имеет фокус, смысла нет, поэтому при ее кодировании они вообще не имеют в виду такое (.Row
а также .Col
не должен быть прочитан, когда goRowSelect=True
). Другими словами: если у вас всегда выбрана полная строка, какой смысл проверять ячейку, в которой находится фокус, такой ячейки нет, это полная строка и т. Д.... подумайте так, чтобы не рассердиться из-за ошибок на внутренняя реализация при смешивании сфокусированной ячейки на goRowSelect=True
TStringGrid.
Тоже самое худшее: ти goRowSelect=True
и некоторые комбинации клавиш (Shift+Cursors) и щелчки мышью, вы можете сделать редкий выбор, например, две или три ячейки в столбце, но не полные строки; помни это goRowSelect=True
и сетка показывает только некоторые ячейки из выбранных строк, и если вы читаете .Selection
он говорит вам, что не все ячейки в строке выбраны (True=(TheGrid.FixedCols+#<TheGrid.Selection.Left)
) где # может быть больше 1. Опять же, остерегайтесь таких ошибок... я могу только сказать... я всегда перехватываю изменения выбора и принудительно выбираю целые строки (я помещаю код в OnSelectCell, чтобы гарантировать, что весь выбор всегда полон row/s), см. простой код (заметьте, мне все равно, какая ячейка выбирается, по концепции выбор должен идти на полную строку или на целые строки, а не на ячейку; опять же редкая концепция, внутренняя реализация не думает, что вы Я хочу сделать что-то, когда выбранная ячейка изменится, поэтому у этого события, как предполагается, нет кода, я поместил этот код, чтобы ИСПРАВИТЬ ошибку, не являющуюся полной строкой / строками)
procedure TYourForm.YourGridSelectCell(Sender: TObject; ACol, ARow: Integer; var CanSelect: Boolean);
begin
YourGrid.Selection:=TGridRect(Rect(YourGrid.FixedCols,YourGrid.Selection.Top,YourGrid.FixedCols,YourGrid.Selection.Bottom));
end;
Упрощение: TStringGrid слишком глючит, я поймал его, говоря ".Row=13", в то же время .Selection.Top=2
а также .Selection.Bottom=5
; как активная строка может быть за пределами выделения? и т.д. Это потому, что ".Row" (а также .Col
) не говорите о выбранной строке, говорите о том, какая ячейка имеет фокус, поэтому видя .Row
знать, какая строка выбрана, неверно в концепции... вы увидите строку ячейки, которая имеет фокус, ничего не связанного с ячейками точного отбора.
Я никогда не вижу неудачи в этом:
- .Selection. ··· всегда сообщает выбранную область (область показана как выбранная)
- .Row, если присвоено значение (и сетка имеет
goRowSelect=True
), весь этот ряд выбран (я никогда не пробовал это, не имеяgoRowSelect=True
), но только один ряд.
Не говоря уже о том, хотите ли вы много раз взломать TStringGrid и сделать его многострочным выбором с более чем одним непрерывным выбором одновременно (например, множественный выбор ListBox); это делает вещи очень хаотичными из-за всех ошибок, которые имеет TStringGrid в управлении свойствами.Row и.Selection.
Для таких множественных сеток я всегда рекомендую использовать неофициальный компонент VCL вместо TStringGrid, если я плохо помню, он называется TMultiSelectStringGrid, у вас есть свойство.Selected для каждой ячейки, строки и столбца, которые считываются / Написать в состоянии. Он действительно отлично работает, когда вы хотите выбрать множественный выбор с нажатой клавишей Ctrl, также отлично работает с многострочным выделением и выделением из нескольких столбцов... просто сделайте цикл над строками, столбцами или ячейками, чтобы проверить, какие из них выбраны, и ведьмы нет. Он также имеет свойство .RightMouseSelect
(если я не помню плохо), который делает щелчок правой кнопкой мыши, чтобы выбрать, и это хорошо.
Предупреждение, не весь код, который можно выбрать при щелчке правой кнопкой мыши, является правильным... многим из них не важно, есть ли у вас множественный выбор или нет... никогда не устанавливайте .Row
если вы хотите сохранить более одной выбранной строки, просто проверьте, находится ли мышь вне выделенной области, прежде чем изменять .Selection
иначе щелчок правой кнопкой мыши может сделать выбор для изменения... так, как можно всплывающее меню для более чем одной строки (пример использования: запись всплывающего меню называется удалением для более чем одной записи одновременно).
Другими словами (код, который я действительно использую для стандарта VCL TStringGrid):
procedure TYourForm.YourGridMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
var
ACol,ARow:Integer;
begin
YourGrid.MouseToCell(X,Y,ACol,ARow);
if goRowSelect in YourGrid.Options
then begin // TStingGrig with full row selected, no individual cell must be selected
if (ARow<YourGrid.Selection.Top)
or
(YourGrid.Selection.Bottom<ARow)
then begin // Where clicked is outside the actual rows that are selected
YourGrid.Row:=ARow;
end;
end
else begin // TStingGrig where individual cells can be selected
if (ARow<YourGrid.Selection.Top)
or
(YourGrid.Selection.Bottom<ARow)
or
(ACol<YourGrid.Selection.Left)
or
(YourGrid.Selection.Right<ACol)
then begin // Where clicked is outside the actual selection
YourGrid.Selection:=TGridRect(Rect(ACol,ARow,ACol,ARow)); // Select the clicked cell
end;
end;
end;
Примечание: у меня действительно есть этот код в процедуре на модуле, и я вызываю эту процедуру, передавая ссылку на Grid и координаты X, Y; хорошо сказать правду, что модуль, который я использую, является полным взломом TStringGrid с объявлением type TStringGrid=class(Grids.TStringGrid)
так что я могу использовать визуальный дизайн и иметь дополнительные функции на всех них; просто убедитесь, что хак работает, чтобы добавить такой модуль на interface
uses
секция в конце списка юнитов (или, по крайней мере, после гридов, никогда до гридов).
Специальные подсказки для управления всплывающим меню были или не были показаны, и что всплывающее меню, чтобы показать:
- Сделай это на мероприятии
OnMouseDown
никогдаOnMouseUp
ниOnClick
, так далее.; или всплывающее меню будет отображаться до изменения выбора... и иногда, когда изменение выбора (по коду) всплывающее окно будет скрыто сразу. - Если сделано
OnMouseDown
нет необходимости заставлять всплывающее меню показываться кодом, оно будет делать это нормально; Более того, вы можете отменить показ всплывающего окна, например, если щелкнуть за пределами данных ячеек, или же можете иметь разные всплывающие окна для FixedCols, FixedRows, также для каждой ячейки вы можете иметь разные всплывающие окна (я говорю о всплывающих окнах времени разработки, вы также можете динамически создавать всплывающие записи до их показа и т. д.), всегда указывать код для того, что вы хотите сделать на событииOnMouseDown
, говоря о меню опупа, созданных во время разработки; если всплывающее окно создается во время выполнения, думайте так же: показывать или не показывать наOnMouseDown
, построение меню самостоятельноOnPopup
Событие меню.
Основная хитрость заключается в том, чтобы сделать выборку на мероприятии OnMouseDown
, он запускается перед отображением всплывающего меню.
Ах, в моем коде я не возражаю против того, какая кнопка была нажата, так как если щелкнуть левой кнопкой мыши внутри нескольких выбранных строк, она также будет работать как обычно (мой код не вносит никаких изменений ни в выделение, ни в строку, и т. Д. Он действительно ничего не делает см if
s), но вы увидите изменения выбора только в одной строке.
Остерегайтесь, выбор также может быть изменен на множественный выбор с помощью левой кнопки мыши, удерживая ее нажатой, затем переместите мышь и поднимите левую кнопку, которая выберет более одной ячейки / строки. Все эти способы, которыми пользователь должен делать выборки, делают внутреннюю реализацию стандартного компонента настолько ошибочной, что не все комбинации действий были приняты во внимание, пока он был внутренне закодирован.
Попробуйте нажать Ctrl или Shift, пока левая мышь нажата, и вы двигаете мышь по стандартной сетке без кода вообще, ecetp код на OnMouseMove, чтобы показать .Row
, .Col
а также .Selection.···
, увидит то, о чем вы никогда не подумаете. Я вижу один раз, рассказывая .Col
значение было несколько миллионов (невозможное значение, так как сетка имеет только несколько столбцов), то же самое для .Row
(отличается от значения, когда происходит сбой на.Col).
Так что не верьте значениям, возвращенным с .Row
а также .Col
если вы думаете о том, что такое выделение (неправильное понятие, они выражают, какая ячейка имеет фокус, ничего не связано с тем, что такое выделение) но вы можете использовать их, чтобы выбрать одну и только одну строку или столбец (столбец, только если используется взломанная сетка, которая позволяет это, или использовать сетку с goRowSelect=False
и имитировать выбор столбца самостоятельно).
И, пожалуйста, всегда имейте это в виду (пожалуйста, делайте это всегда):
- Клетка, которая имеет фокус (
.Row
а также.Col
может сказать, какой из них) может быть вне фактического выбора, да, он может быть вне (среди них очень трудно принудительно вызвать неудачу, это происходит, не нужно ничего кодировать, просто используя щелчок мышью и комбинации с Alt, Ctrl, Shift, Курсоры и Пробел могут произойти). Так что не верь.Row
ни.Col
знать что-либо о выборе (что неправильно по концепции), всегда использовать.Selection.···
,
Надеюсь, что это поможет не получить напор, как у меня, пока я не пойму это:
.Selection.···
представляет только одну прямоугольную область выбранных ячеек (не имеет значения, еслиgoRowSelect
верно или неверно)..Selection:=TGrigRect(Rect(Left,Top,Right,Bottom));
это лучший способ изменить фактический выбор на другой (убедитесь самиLeft<=Right
а такжеTop<=Bottom
или все может пойти очень плохо); думать о коде как об этом:.Selection:=TGrigRect(Rect(Min(Left,Right),Min(Top,Bottom),Max(Right,Left),Max(Bottom,Top));
(см.Min
а такжеMax
, они вMaths
единица измерения)..Row
дает номер строки той специальной ячейки, на которой нарисован пунктирный прямоугольник, независимо от того, находится ли она внутри выделения или вне выделения, также может быть -1, если ни одна ячейка не имеет фокуса. Это не имеет ничего общего с выбором..Col
дает число col той специальной ячейки, на которой нарисован прямоугольник с точками, независимо от того, находится она внутри выделения или вне выделения, также может быть равно -1, если ни одна ячейка не имеет фокуса. Это не имеет ничего общего с выбором.
Понимая, что четыре вещи, я могу получить заголовки быть чем-то в прошлом. Мне потребовалось слишком много времени, чтобы понять две разные концепции: ячейка с фокусом (".Row" и ".Col") и выделенные ячейки (.Selection.···
).
PD: будьте свободны, чтобы поделиться этой информацией!
В одном из моих элементов управления на основе TStringGrid я использую событие MouseDown/MouseUp для обработки этого всплывающего меню, потому что у меня есть два разных контекстных меню, в зависимости от того, в какой области TStringGrid вы щелкнули. Работает как шарм. Просто убедитесь, что вы вызываете унаследованный ДО вашего кода.
-
Обратите внимание, что в порядке вызова событий при всплывающем контекстном меню есть что-то странное. Точнее говоря, когда вы нажимаете RMB и всплывающее меню появляется, событие MouseUp вызывается не сразу. Он вызывается при следующем нажатии кнопки мыши (любой кнопки).
Смотрите также: TStringGrid - OnMouseUp не вызывается!
Хм... Я не могу продублировать проблему в моем D2010.
Быстрая мысль, что, возможно, проблема возникает из-за того, что вы не выбрали ни одной строки? Будет ли предварительная установка строки StringGrid, скажем, 0 первой в справке OnCreate вашей формы?
Если вы хотите / нуждаетесь в щелчке правой кнопкой мыши на ячейке, переместите фокус на нее (как обычно это делается с помощью щелчка левой кнопкой мыши), вы можете использовать код, который я использую для этого:
type TStringGridHacked=class(Grids.TStringGrid); // This is to have access to hidden (and very usefull) methods, like MoveCol, MoveRow, FocusCell, etc
procedure TMyForm.TheStringGridMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
var
ACol,ARow:Integer;
begin
if mbRight=Button
then begin // Right mouse button clicked
TheStringGrid.MouseToCell(X,Y,ACol,ARow); // Convert X,Y coordinates of mouse to cell Col & Row
if (FixedCols<=ACol)and(FixedRows<=ARow)
then begin // Cell is not a header one
TStringGridHacked(TheStringGrid).FocusCell(ACol,ARow,True); // Send focus to such cell doing only one move, so triggering SelectCell only once
end;
end;
end;
Я использовал этот взломать type TStringGridHacked=class(Grids.TStringGrid)
очень долго, так как я нашел это в интернете.
Я помещаю такой хак (объявление типа) сразу после того, как реализация использует (если он мне нужен более одного раза на одном модуле), или как в коде непосредственно перед процедурой (если он мне нужен только один раз); Оба способа работают хорошо и делают код более понятным.