3

TStringGrid とポップアップ メニューの使用に問題があります

ポップアップ メニューからアイテムを選択したときに最後にアクティブだったセルの行/列を知りたいです。ただし、ポップアップ メニューをクリックすると、StringGrid.Row が -1 として返されます。

OnClick の一部として MouseToCell を使用してみましたが、SG.Row を設定した後でも、PopUp メニュー ルーチンで -1 として返されます... 問題は、Grid がフォーカスを失っていることだと思います。

OnClick でグローバル変数を設定する必要のない解決策はありますか?

ツールバーとポップアップ メニューの間でアクションが一貫していることを確認するために、ポップアップ メニューの項目にリンクされたアクション リストを使用しています。

4

5 に答える 5

10

おっしゃっている意味が十分に理解できていないことをお詫び申し上げます。文字列グリッドのセルを左クリックすると選択されますが、右クリックしても選択されません。右クリックすると、ポップアップ メニューが表示され (割り当てられている場合)、現在選択されているとMenuItemClickを簡単に読み取ることができます。例のビデオを参照してください。rowcol

あなたが実際にこれを望んでいると思います.左クリックと同様に右クリックでアクティブセルを変更したい. これは簡単にできます:

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;
于 2010-08-19T16:10:35.973 に答える
1

TStringGrid で選択された行を知る別の方法 (実際にはこれが唯一の方法です)。

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

選択された行が 1 つしかない場合は、それらが一致する必要があります。

YourstringGrid.Row.Selection.···が選択行の取得に失敗しているように見えるのを見たときに、失敗することはありません。多くの場合、他の値を返さなければならないと思うと -1 が返されます (失敗したように見える理由を理解するには、最後の 4 つのポイントを参照してください。 -1 が返された場合は、実際には失敗ではありません。これは基本的に理解されている概念です)。

選択とフォーカスのあるセルは同じものではありません....Selection選択用で.Rowあり.Col、フォーカスのあるセル用であり、選択に関連するものは何もありません。選択がセルの完全に異なる範囲である間、フォーカスのあるセルにすることができます(どちらも異なる概念です)。

YourstringGrid.Row<>YourstringGrid.Selection.Topまた、それが真である可能性があることを発見しました。フォーカスのあるセルが選択範囲の一番上の行にない場合。

インターネットで公開されているいくつかのハック、トリック、コードなどgoRowSelect=Falseは、True に設定するとそのようなルーチンがうまく機能しない場合にのみ使用されます。注意して使用してください。

goRowSelect=Trueヒント:複数の行をコードで選択するのは非常にバグが多いTStringGridでは、変更.Selectionしても更新されないことがあります.Row(フォーカスのある実際のセルは変更されません)。行の値を に直接割り当てる方がよいでしょう.Row

覚えておいてください: TStringGrid ではgoRowSelect=True、どのセルにフォーカスがあるかについて話すことには意味がありません。したがって、それをコーディングするときは、そのようなことをまったく念頭に置いていません (.Rowそして、.Colいつでも読んではいけませんgoRowSelect=True)。言い換えれば、常に行全体が選択されている場合、フォーカスのあるセルをチェックする必要がある場合、そのようなセルはなく、行全体であるなど...バグに悩まされないように考えてくださいgoRowSelect=TrueTStringGridでフォーカスされたセルを混合するときの内部実装。

また、最悪の場合:goRowSelect=Trueいくつかのキーボードの組み合わせ (Shift + カーソル) とマウス クリックを使用すると、列内の 2 つまたは 3 つのセルなどのまれな選択を行うことができますが、行全体を選択することはできません。goRowSelect=Trueそれがあり、グリッドには選択された行の一部のセルのみが表示されることを覚えておいてください。読む.Selectionと、行のすべてのセルが選択されているわけではないことがわかります(True=(TheGrid.FixedCols+#<TheGrid.Selection.Left)ここでも、そのようなバグに注意してください...私が言えることは...私は常に選択の変更をトラップし、完全な行をselectectにすることを強制します(すべての選択が常に完全であることを確認するために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 はバグが多すぎ.Selection.Top=2ます.Selection.Bottom=5。選択範囲外のアクティブな行をどのようにすることができますか? これは、'.Row' (および も.Col) が選択された行について話さず、どのセルにフォーカスがあるかについて話しているためです。そのため、.Rowどの行が選択されているかを知ることは概念的に間違っています...フォーカスのあるセルで、実際の選択セルとは関係ありません。

私はこれが失敗するのを見たことがない:

  • .Selection.・・・常に選択範囲を教えてくれる(選択中と表示されている範囲)
  • .Rowに値が割り当てられている場合(およびグリッドに がある場合goRowSelect=True)、その行全体が選択されます( なしで試したことはありませんgoRowSelect=True)が、1行のみです。

言うまでもなく、TStringGrid を何度もハックして、複数の連続選択を同時に行う複数行の選択 (ListBox の複数選択のように) にしたい場合は言うまでもありません。これは、TStringGrid が .Row および .Selection プロパティの管理に関して持っているすべてのバグのために、事態を非常に混乱させます。

このような複数選択グリッドの場合、TStringGrid の代わりに非公式の VCL コンポーネントを常に使用することをお勧めします。覚えていない場合は、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;

注: 私は実際にユニットのプロシージャにそのコードを持っており、グリッド参照と X、Y 座標を渡してそのプロシージャを呼び出します。私が使用するユニットは TStringGrid の完全なハックであり、 as として宣言されているtype TStringGrid=class(Grids.TStringGrid)ので、ビジュアル デザインを使用して、それらすべてに余分な機能を追加できます。ユニットリストの最後にあるセクションにそのようなユニットを追加するようにハックが機能することを確認してinterface usesください(または少なくともグリッドの後、決してグリッドの前ではありません)。

ポップアップ メニューを表示するかどうか、およびどのポップアップ メニューを表示するかを制御するための特別なヒント:

  • eventで実行し、 onではなくOnMouseDown、neverなどで行います。または、選択が変更される前にポップアップメニューが表示されます...そして、選択が(コードによって)変更されると、ポップアップがすぐに非表示になります。OnMouseUpOnClick
  • OnMouseDownポップアップ メニューを強制的にコードで表示する必要がない場合は、通常どおり表示されます。また、表示されるポップアップをキャンセルすることもできます。たとえば、セル データの外側をクリックした場合、または FixedCols、FixedRows ごとに異なるポップアップを表示したり、セルごとに異なるポップアップを表示したりできます (設計時のポップアップについて説明します。また、表示される前に動的にポップアップ エントリを作成することもできますOnMouseDown。popup が実行時に作成される場合、同じことを考えてください: で表示するかどうか、独自のメニュー イベントOnMouseDownでのメニューの構築。OnPopup

基本的なトリックは、イベントOnMouseDownで選択変更を行うことです。ポップアップ メニューが表示される前に発生します。

ああ、私のコードでは、どのボタンがクリックされたかは気にしません。複数の行が選択されている状態で左クリックすると、通常どおりに動作するからです (私のコードは、選択や行などに変更を加えません。実際には何もしません)。 、ifs) を参照してください) が、選択が 1 行だけに変更されていることがわかります。

注意してください。選択は、マウスを左クリックして保持し、次にマウスを動かして左ボタンを持ち上げることで、複数のセル/行を選択する複数選択に変更することもできます。ユーザーが選択しなければならないこのすべての方法は、標準コンポーネントの内部実装を非常にバグのあるものにし、内部的にコーディングされている間、アクションのすべての組み合わせが考慮されていませんでした。

左マウスが押されている間にandCtrlまたはを押してみてくださいShift。コードをまったく使用せずに標準グリッド上でマウスを動かしています。値が数百万(グリッドには数列しかないため、不可能な値)であると言っていることがありますが、 (「.Col」で失敗した場合とは異なる値)と同じです。.Row.Col.Selection.···.Col.Row

したがって、返される値を信じないでください。選択.Row.Colは何かを考えている場合は (間違った概念です。それらはどのセルにフォーカスがあるかを表し、選択とは何の関係もありません)。ただし、それらを使用して、1 つの行または列のみを選択することができます (ハッキングされたグリッドを使用する場合は列のみ、またはグリッドを使用goRowSelect=Falseして独自の列選択をシミュレートする場合)。

そして、常にこれを念頭に置いてください(常に実行してください):

  • フォーカスのあるセル (どれがどれかを教え.Row.Colくれるかもしれません) は、実際の選択範囲外にある可能性があります。マウスの移動、クリック、およびAltCtrlShift、カーソルとの組み合わせSpacebarが発生する可能性があります)。したがって、選択について何も信頼し.Rowたり.Col、知ったりしないでください (これは概念上間違っています)。常に を使用して.Selection.···ください。

私がこれを理解するまで私が持っていたように、これが頭を悩ませないのに役立つことを願っています:

  1. .Selection.···は、選択されたセルの 1 つの長方形領域のみを表します (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点線の長方形が描画されている特別なセルの列番号を返します。セルが選択範囲内にあるか、選択範囲外にあるかに関係なく、セルにフォーカスがない場合は -1 になります。セレクションとは関係ありません。

4 つのことを理解すると、headages が過去のものになる可能性があります。フォーカスのあるセル (「.Row」と「.Col」) と選択されたセル ( ) という 2 つの異なる概念を理解するのに時間がかかりすぎました.Selection.···

PD: この情報を自由に共有してください!

于 2016-09-30T11:18:48.797 に答える
0

うーん... D2010 で問題を再現できません。

行を選択していないために問題が発生している可能性があります。フォームの OnCreate ヘルプで StringGrid の行を最初に 0 にプリセットしますか?

于 2010-08-22T05:11:41.313 に答える
0

セルを右クリックしてフォーカスを移動する必要がある場合は(通常は左クリックで行うように)、そのために使用するコードを使用できます。

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)を長い間使用してきました。

実装が使用する直後に(同じユニットで複数回必要な場合)、またはプロシージャの直前のコードにあるように(一度だけ必要な場合)、そのようなハック(型宣言)を配置します。どちらの方法でも問題なく機能し、コードがより明確になります。

于 2015-02-04T13:47:13.087 に答える
0

私の TStringGrid ベースのコントロールの 1 つで、クリックした TStringGrid の領域に応じて 2 つの異なるコンテキスト メニューがあるため、MouseDown/MouseUp イベントを使用してそのポップアップ メニューを処理しています。魅力のように機能します。コードの前にinheritedを呼び出すようにしてください。

--
コンテキスト メニューをポップアップしたときにイベントが呼び出される順序がおかしいことに注意してください。より正確には、RMB を押してポップアップ メニューがポップアップしても、MouseUp イベントはすぐには呼び出されません。次にマウス ボタン (任意のボタン) を押したときに呼び出されます。

これも参照してください: TStringGrid - OnMouseUp は呼び出されません!

于 2010-08-19T19:59:30.417 に答える