Delphi Tips 
-----------------------------

キーワード:Additional

>> Index

01/02 StringGrid の内容更新を高速に行う
09/16 StringGrid でセル編集終了のイベントを得たい
09/16 TStringGrid の列の ReSize イベントの取得
02/11 TSplitter をドラッグ中にヒント文字列が表示された時の不具合
02/08 StringGrid で マウスのある Cell 内容に応じた Hint を出したい
02/08 StringGridで選択セルのハイライト表示を無くしたい
02/08 TStringGridのソート
02/08 StringGrid/DBGrid でのセル編集の動作を細かく指定する

最終更新: 9092 日前

0190  D1   D2   D3   D4   D5   D6   D7   3.1   95   98    作成: 1999/05/19 osamu rev 1.2
   B1   B3   B4   B5   B6   B7   NT3   NT4   2K   XP  更新: 2000/01/02 osamu 編集
StringGrid の内容更新を高速に行う

| 少々データ量が大きいので時間を稼ぐため、画面表示の Update を
| 停止したいのですが TStringGrid に BeginUpdate/EndUpdate は
| 無いようです。

【解法1】

描画更新の一次停止
  SendMessage(StringGrid1.Handle, WM_SETREDRAW, 0, 0);

描画更新の再開
  SendMessage(StringGrid1.Handle, WM_SETREDRAW, 1, 0);
  StringGrid1.Refresh;

この方法は、すべての TWinControl に対して有効です。

【解法2】

描画更新の一次停止
  StringGrid1.Rows[0].BeginUpdate;

描画更新の再開
  StringGrid1.Rows[0].EndUpdate;

Rows[0] は Rows[1] でも何でも構いませんが、停止と開始で同じ添字を使わなければなりません。Cols[x] も使えます。一つの Rows[x] または Cols[x] に対して BeginUpdate すると StringGrid のすべての表示更新が停止になります。

BeginUpdate は TStrings の仮想メンバ関数なので、TMemo.Lines とか TListBox.Items などで BeginUpdate を使って TMemo/TListBox などの変更を高速に行うこともできます。

【メモ】

描画更新の再開を必ず行うために、BeginUpdate/EndUpdate は try/finally で保護した方が良いです。

StringGrid1.Rows[0].BeginUpdate;
try
  // 更新処理
finally
  StringGrid1.Rows[0].EndUpdate;
end;

また、BeginUpdate を繰り返し呼んだ場合には EndUpdate を同じ回数呼ばなければ描画は再開されません。さらに、BeginUpdate よりも多く EndUpdate を呼んでしまうとおかしなことになってしまうので注意が必要です。
参照: [Delphi-ML:38146] [builder:19539] <コンポーネント >

0263  D1   D2   D3   D4   D5   D6   D7   3.1   95   98    作成: 1999/09/16 osamu rev 1.1
   B1   B3   B4   B5   B6   B7   NT3   NT4   2K   XP  更新: 1999/09/16 osamu 編集
StringGrid でセル編集終了のイベントを得たい

StringGrid で表計算のような動作をさせようとすると、セル編集終了のイベントで、関係し合うセルの再計算を行いたくなります。OnSetEditText というイベントが利用できそうなのですが、このイベントはユーザのキー入力の一文字ごとに発生するため使えません。

で、少なくとも Delphi3 では、以下のようにすると、セル編集終了時を検出することができます。

これは、セル編集終了時に、OnSetEditText が2度続けて同じ Value 値で呼び出されるという現象を利用しています。Delphi の今後のバージョンで動作が保証されるわけではありませんので、注意が必要です。また、Options に goAlwaysShowEditor が含まれている場合には、Enter キーで値を確定することができないという不具合(?)が生じます。

procedure TForm1.StringGrid1SetEditText(Sender: TObject; ACol, ARow: Integer;
        const Value: String);
const
    MagicValue = 'd0308|ybh<_lfds$t083q()'#1#5; // 入力値としてありえない値
    PreviousEditorValue: string = MagicValue;   // C でいう static 変数の代り
begin
    if Value<>PreviousEditorValue then begin    // まだ編集中
        PreviousEditorValue:= Value;
        Exit;
    end;

    // 編集終了時には、同じ値が二度続けて送られてくる
    case ACol of
    0: ;
    1: ;  // ここで入力後の処理
    2: ;
    end;

    // 次回のために絶対にありえない文字列を代入
    PreviousEditorValue:= MagicValue;
end;
参照: [Delphi-ML:42663] <コンポーネント >

0262  D1   D2   D3   D4   D5   D6   D7   3.1   95   98    作成: 1999/09/16 osamu rev 1.1
   B1   B3   B4   B5   B6   B7   NT3   NT4   2K   XP  更新: 1999/09/16 osamu 編集
TStringGrid の列の ReSize イベントの取得

ColWidthsChanged を overrideすることで、実現できます。

■interface

TStringGrid_Ex = class(TStringGrid)
private
  FOnColWidthsChange:TNotifyEvent;
ptotected
  procedure ColWidthsChanged; override;
published
  property OnColWidthsChange:TNotifyEvent
    read  FOnColWidthsChange
    write FOnColWidthsChange;
end;

■implementation

procedure TStringGrid_Ex.ColWidthsChange;
begin
  inherited;
  if Assigned(FOnColWIdthsChange) then
    FOnColWidthsChange(Self);
end;


■利用方法

procedure TForm1.Grid1OnColWidthsChange(Sender:TObject);
var
  idx :Longint;
begin
  //条件
  //  Grid1.ColCount = Grid2.ColCountであること

  if Sender = Grid1 then begin
    for idx:=0 to Grid1.ColCount-1 do begin
      Grid2.ColWidths[idx] := Grid1.ColWidths[idx];
    end;
  end else begin
      for idx:=0 to Grid2.ColCount-1 do begin
        Grid1.ColWidths[idx] := Grid2.ColWidths[idx];
      end;
    end;
  end;
end;

同様に、RowHeighsChangedもoverrideできます。
参照: [Delphi-ML:31921] <コンポーネント >

0134  D1   D2   D3   D4   D5   D6   D7   3.1   95   98    作成: 1999/02/11 osamu rev 1.1
   B1   B3   B4   B5   B6   B7   NT3   NT4   2K   XP  更新: 1999/02/11 osamu 編集
TSplitter をドラッグ中にヒント文字列が表示された時の不具合

TSplitter をドラッグ中に他のコントロール上にてヒントボックスを出現させると、TSpliter の境界線がその場に残ってしまいます。

TSplitter のドラッグ中は Application.ShowHint を False にしてしまうという回避方が [Delphi-ML:24019] にて紹介されています。
参照: [Delphi-ML:24019] <バグ> <その他コンポーネント関連> <コンポーネント >

0082  D1   D2   D3   D4   D5   D6   D7   3.1   95   98    作成: 1999/02/08 osamu rev 1.1
   B1   B3   B4   B5   B6   B7   NT3   NT4   2K   XP  更新: 1999/02/08 osamu 編集
StringGrid で マウスのある Cell 内容に応じた Hint を出したい

{Application.OnShowHint イベントハンドラの設定}

procedure TForm1.FormCreate(Sender: TObject);
begin
    Application.OnShowHint := DoShowHint;
end;


{ヒントの表示ルーチン}
{TForm1 の Private で宣言してあります。}

procedure TForm1.DoShowHint( var HintStr: string; var CanShow: Boolean;
                             var HintInfo: THintInfo);
var
  ACol,ARow: Integer;
  ARect: TRect;
begin
  {ストリンググリッドならば}
  if HintInfo.HintControl = StringGrid1 then begin
     with HintInfo do begin
       {ヒントの色の指定}
       HintColor := clAqua;
       {セルの位置を取得}
       StringGrid1.MouseToCell( CursorPos.x, CursorPos.Y, ACol, ARow );
       {セルの範囲の取得}
       ARect := StringGrid1.CellRect( ACol, ARow );
       {ヒントの表示位置}
       HintPos := StringGrid1.ClientToScreen( Point(ARect.Left,ARect.Bottom));
       {ヒントの内容}
       HintStr := 'このセルは('+IntToStr(ACol)+','+IntToStr(ARow)+')';
       {ヒントの有効範囲の設定}
       CursorRect := ARect;
     end;
  end;
end;
参照: [Delphi-ML:19686] <その他コンポーネント関連> <コンポーネント >

0029  D1   D2   D3   D4   D5   D6   D7   3.1   95   98    作成: 1999/02/08 osamu rev 1.1
   B1   B3   B4   B5   B6   B7   NT3   NT4   2K   XP  更新: 1999/02/08 osamu 編集
StringGridで選択セルのハイライト表示を無くしたい

 >> TStringGridについてなんですが、それにFocusが当たってない場合に一つのセルの色
 >> が青で塗りつぶされています。これを塗りつぶされていなくしたいのですが、やり方
 >> をご存知の方、ご教授願います。

以下のような方法でできます。

// TForm1のメンバ

    StringGrid1Selection:TGridRect;

// implementation

procedure TForm1.FormCreate(Sender: TObject);
begin
    StringGrid1Selection:=TGridRect(Rect(-1,-1,-1,-1));
end;

procedure TForm1.StringGrid1Exit(Sender: TObject);
begin
    StringGrid1Selection:=StringGrid1.Selection;
    StringGrid1.Selection:=TGridRect(Rect(-1,-1,-1,-1));
end;

procedure TForm1.StringGrid1Enter(Sender: TObject);
begin
    StringGrid1.Selection:=StringGrid1Selection;
end;

ただし、
TGridRect(Rect(-1,-1,-1,-1))は、ちょっと無理をしているので、
Delphi1.0や、将来のバージョンでは動かないことも考えられます。

 >     function GridRect(l,t,r,b:LongInt):TGridRect;
 >     begin
 >         with Result do begin
 >             Left:=l;
 >             Top:=t;
 >             Right:=r;
 >             Bottom:=b;
 >         end;
 >     end;

を作っておいて

    StringGrid1.Selection:=GridRect(-1,-1,-1,-1);

とするのが正攻法です。
参照: [Delphi-ML:17189] <コンポーネント >

0023  D1   D2   D3   D4   D5   D6   D7   3.1   95   98    作成: 1999/02/08 osamu rev 1.1
   B1   B3   B4   B5   B6   B7   NT3   NT4   2K   XP  更新: 1999/02/08 osamu 編集
TStringGridのソート

 > StringGrid上に表示したデータを、ある列(Col)をKEYとしてソートし、
 > 再表示させたいのですが、どのようにしたらよいのでしょうか?

エレガントさを求めず、力わざで良ければ、

procedure TForm1.FormCreate(Sender: TObject);
var i,j:Integer;
begin
    for i:=0 to StringGrid1.ColCount-1 do
        for j:=0 to StringGrid1.RowCount-1 do
            StringGrid1.Cells[i,j]:=IntToStr(Random(100));
end;

procedure TForm1.StringGrid1DblClick(Sender: TObject);
var c,min,i,j:Integer;
    s:string;
begin
    c:=StringGrid1.Col;
    for i:=0 to StringGrid1.RowCount-2 do begin
        min:=i;
        for j:=i+1 to StringGrid1.RowCount-1 do
            if StrToInt(StringGrid1.Cells[c, j ]) <
               StrToInt(StringGrid1.Cells[c,min]) then
                    min:=j;
        if min<>i then
            for j:=0 to StringGrid1.ColCount-1 do begin
                s:=StringGrid1.Cells[j,i];
                StringGrid1.Cells[j,i]:=StringGrid1.Cells[j,min];
                StringGrid1.Cells[j,min]:=s;
            end;
    end;
end;

としてできますが、セル数が1万を超えると速度的にチョットきつくなってきます。大きなグリッドに対してこのようなことをやりたければTListViewを使うことをお勧めします。
参照: [Delphi-ML:12259] <コンポーネント >

0009  D1   D2   D3   D4   D5   D6   D7   3.1   95   98    作成: 1999/02/08 osamu rev 1.1
   B1   B3   B4   B5   B6   B7   NT3   NT4   2K   XP  更新: 1999/02/08 osamu 編集
StringGrid/DBGrid でのセル編集の動作を細かく指定する

下のような関数を使って、TCustomGridで定義されているインプレースエディタを取得すれば、

    GetGridEditor(StringGrid1).SelStart:=0;

などとできます。

function GetGridEditor(Grid: TCustomGrid): TCustomEdit;
var i: Integer;
begin
    Result:=nil;
    for i:=0 to Grid.ControlCount-1 do
        if Grid.Controls[i] is TCustomEdit then begin
            Result:=TCustomEdit(Grid.Controls[i]);
            Exit;
        end;
end;
参照: [Delphi-ML:18726] <データベース> <コンポーネント > <DataControls>

[新規作成] [最新の情報に更新]

How To
Lounge
KeyWords

Tips
Delphi
Home
Osamu Takeuchi osamu@big.or.jp