Delphi: StringGrid, позиция и контекстное меню - PullRequest
2 голосов
/ 19 августа 2010

У меня проблема с использованием TStringGrid и всплывающего меню

Я хочу узнать строку / столбец ячейки, которая была последней активной при выборе элемента из моего всплывающего меню.Однако, когда я нажимаю на всплывающее меню, StringGrid.Row возвращается как -1.

Я пытался использовать MouseToCell как часть OnClick, но даже после установки SG.Row он по-прежнему возвращается как -1 вподпрограммы всплывающих меню ... Я подозреваю, что проблема в том, что Grid теряет фокус.

Существуют ли какие-либо решения для этого, которые не требуют, чтобы OnClick устанавливал глобальную переменную?используя список действий, связанный с элементами во всплывающем меню, чтобы убедиться в согласованности действий между панелью инструментов и всплывающим меню

Ответы [ 5 ]

7 голосов
/ 19 августа 2010

Боюсь, я не до конца понимаю, что вы имеете в виду.Когда я щелкаю левой кнопкой мыши ячейку в сетке строк, она выделяется, но не тогда, когда я щелкаю ее правой кнопкой мыши.Когда я щелкаю по нему правой кнопкой мыши, появляется всплывающее меню (если назначено), и на MenuItemClick я могу легко прочитать row и col, выбранные в данный момент.Смотрите пример видео .

I угадать , что вы действительно хотите это: вы хотите щелкнуть правой кнопкой мыши, чтобы изменить активную ячейку, а также левой кнопкой мыши.Это легко сделать:

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;
1 голос
/ 30 сентября 2016

Другой способ узнать строку выбора в TStringGrid (она действительно единственная):

YourstringGrid.Selection.Top;
YourstringGrid.Selection.Bottom;

Если выбрана только одна строка, они должны совпадать.

Я никогда не вижу, что .Selection.··· терпит неудачу, хотя я видел, что YourstringGrid.Row, похоже, не может получить строку выбора, часто он возвращает -1, когда вы думаете, что он должен возвратить другие значения (см. 4 пункта в конце, чтобы понять, почему кажетсяпотерпеть неудачу, но это действительно не ошибка, когда он возвращает -1, ... это понятие недооценено).

Выделение и ячейка с foucs - это не одно и то же ... .Selection для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, чтобы убедиться, что все выделения всегда полные строки / с), см. простой код (обратите внимание, мне все равно, какая ячейка выбирается, по концепции выбор должен идти на полную строку или на полную строку, а не на ячейку, опять же редкая концепция, внутренняя реализация не думает, что вы хотите что-то делать при изменении выбранной ячейки, поэтомуу этого события, как предполагается, нет кода, я поместил этот код, чтобы исправить ошибку выбора, не являющуюся полной строкой / секциями):

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, чтобы узнать, какая строка выбрана, неверно в концепции ... выбудет видна строка ячейки с фокусом, ничего не имеющего отношения к ячейкам с точным отбором.

Я никогда не вижу неудач в этом:

  • . Выбор. ··· всегда говоритВы выбрали выбранную область (область показана как выделенная)
  • . Строка, если ей присвоено значение (а сетка имеет 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, Cursors и Spacebar). Так что не верьте .Row и .Col, чтобы знать что-либо о выборе (что неправильно по концепции), всегда используйте .Selection.···.

Надеюсь, это поможет мне не набраться оборотов, пока я не пойму это:

  1. .Selection.··· представляет только одну прямоугольную область выбранных ячеек (не имеет значения, если goRowSelect - True или False).
  2. .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 единице).
  3. .Row дает номер строки той специальной ячейки, на которой нарисован пунктирный прямоугольник, независимо от того, находится она внутри выделения или вне выделения, также может быть -1, если ни одна ячейка не имеет фокуса. Это не имеет ничего общего с выбором.
  4. .Col дает номер col той специальной ячейки, на которой нарисован пунктирный прямоугольник, независимо от того, находится она внутри выделения или вне выделения, также может быть -1, если ни одна ячейка не имеет фокуса. Это не имеет ничего общего с выбором.

Понимая, что четыре вещи, которые я могу получить, станут чем-то в прошлом. Мне потребовалось слишком много времени, чтобы понять две разные концепции: ячейка с фокусом («.Row» и «.Col») и выделенные ячейки (.Selection.···).

P.D .: Будьте свободны поделиться этой информацией!

0 голосов
/ 04 февраля 2015

Если вам нужно / нужно щелкнуть правой кнопкой мыши по ячейке и переместить фокус на нее (как обычно это делается с помощью щелчка левой кнопкой мыши), вы можете использовать код, который я использую для этого:

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), так как нашел его в Интернете.

Я помещаю такой хак (объявление типа) сразу после того, как реализация использует (если он мне нужен более одного раза на одном модуле), или как в коде непосредственно перед процедурой (если он мне нужен только один раз); Оба способа работают хорошо и делают код более понятным.

0 голосов
/ 22 августа 2010

Хм ... Я не могу продублировать проблему в моем D2010.

Быстро подумал, что, возможно, проблема возникает из-за того, что у вас не выбрано ни одной строки? Будет ли предварительная установка строки StringGrid, скажем, 0 первой в справке OnCreate вашей формы?

0 голосов
/ 19 августа 2010

В одном из моих элементов управления на основе TStringGrid я использую событие MouseDown / MouseUp для обработки этого всплывающего меню, потому что у меня есть два разных контекстных меню, в зависимости от того, какую область TStringGrid вы щелкнули. Работает как шарм. Просто убедитесь, что вы звоните унаследовано ДО вашего кода.

-
Обратите внимание, что в порядке вызова событий при всплывающем контекстном меню есть что-то странное. Точнее говоря, когда вы нажимаете RMB и всплывающее меню появляется, событие MouseUp вызывается не сразу. Он вызывается при следующем нажатии кнопки мыши (любой кнопки).

См. Также: TStringGrid - OnMouseUp не вызывается!

...