[Delphi] OnKeyDown overge-erft van DBGrid

Pagina: 1
Acties:

  • jelmervos
  • Registratie: Oktober 2000
  • Niet online

jelmervos

Simple user

Topicstarter
Ik heb een component die van TDBGrid is afgeleid, en ik wil de OnKeyDown overschrijven met ene eigen event. Na mijn eigen code in de OnKeyDown doe ik uiteraard een inherited. Werkt allemaal perfect.

Maar het probleem is dat als de Grid in editor mode zit (dus men kan typen in een cel) dat hij dan die event in mijn component niet uitvoert. De OnKeyDown wordt dan wel uitgevoerd in het uiteindelijk component op de form, hoe kan dit?

Code:

Overschrijven van de procedure in TMyDBGrid:
code:
1
    procedure KeyDown(var Key: Word; Shift: TShiftState); override;

Nieuwe procedure in TMyDBGrid:
code:
1
2
3
4
5
6
procedure TMyDBGrid.KeyDown(var Key: Word; Shift: TShiftState);
begin
  //hier wat code die moet worden uitgevoerd bij een aantal toetsen. 
  //Deze wordt uitgevoerd zolang de Grid NIET in editor mode zit.
  inherited;
end;

En op het formulier:
code:
1
2
3
4
5
6
procedure TForm1.MyDBGridKeyDown(Sender: TObject; var Key: Word;
  Shift: TShiftState);
begin
  //Deze code wordt ALTIJD uitgevoerd,
  //ook al zit de Grid in editor mode. :?
end;

Iemand weet hoe dit kan en op te lossen is?

"The shell stopped unexpectedly and Explorer.exe was restarted."


Verwijderd

inherited voor je code plaatsen dacht ik zo.

  • jelmervos
  • Registratie: Oktober 2000
  • Niet online

jelmervos

Simple user

Topicstarter
Nee, maakt niet uit. Zodra de Grid in editor mode is komt er geen toets aanslag meer bij de OnKeyDown binnen TMyDBGrid uit.

"The shell stopped unexpectedly and Explorer.exe was restarted."


Verwijderd

De editor heeft een eigen window, en windowproc; Als die focus heeft zal die op toetsen reageren.

Hint: CreateEditor is virtual;

  • jelmervos
  • Registratie: Oktober 2000
  • Niet online

jelmervos

Simple user

Topicstarter
Uuhmm, snap er niet veel van wat je nu bedoeld met die CreateEditor.

"The shell stopped unexpectedly and Explorer.exe was restarted."


Verwijderd

Maak een nieuwe class met als ancestor TDBGridInplaceEdit.

Hierin reageren op een OnKeyDown ofzo, en dan doen wat je wilt doen.

CreateEditor overriden in jouw grid-type en dan die nieuwe editor aanmaken.

  • jelmervos
  • Registratie: Oktober 2000
  • Niet online

jelmervos

Simple user

Topicstarter
Op 1 of andere manier mag ik hem niet aflijden van TDBGridInplaceEdit:

type
TMyDBGridInplaceEdit = class(TDBGridInplaceEdit)

Mag niet omdat hij TDBGridInplaceEdit niet kent, maar die zit wel gewoon in DBGrids.pas

"The shell stopped unexpectedly and Explorer.exe was restarted."


Verwijderd

Oeps, da's klote, die staat in de implementation section |:(

Gebruik eventueel TInplaceEdit uit Grids, maar dan mis je nogal wat, en dat kun je waarschijnlijk niet allemaal kopieeren.

Effe kijken...

[edit]

Simpelste is misschien om de windowproc te kapen van die editor. Dus override CreateEditor, doe een inherited, zodat die editor gemaakt wordt en dan zoiets als in [topic=449290] staat beschreven.

  • jelmervos
  • Registratie: Oktober 2000
  • Niet online

jelmervos

Simple user

Topicstarter
Overnemen mag niet zomaar:

Werkt niet:
code:
1
2
3
4
function TMyDBGrid.CreateEditor: TInplaceEdit;
begin
  inherited;
end;

Geeft AV:
code:
1
2
3
4
function TMyDBGrid.CreateEditor: TInplaceEdit;
begin
  inherited CreateEditor;
end;

Hoe los ik dit allemaal op?

"The shell stopped unexpectedly and Explorer.exe was restarted."


Verwijderd

code:
1
Result := inherited;

:?

  • jelmervos
  • Registratie: Oktober 2000
  • Niet online

jelmervos

Simple user

Topicstarter
Incompatible types.

Omdat hij die TDBGridInplaceEdit niet ken, in de DBGrids.pas ziet het er zo uit:
code:
1
2
3
4
function TCustomDBGrid.CreateEditor: TInplaceEdit;
begin
  Result := TDBGridInplaceEdit.Create(Self);
end;

"The shell stopped unexpectedly and Explorer.exe was restarted."


Verwijderd

Ik bedoel eigenlijk:
code:
1
2
3
4
function TMyDBGrid.CreateEditor: TInplaceEdit;
begin
  Result := inherited;
end;

  • jelmervos
  • Registratie: Oktober 2000
  • Niet online

jelmervos

Simple user

Topicstarter
Precies hetzelfde had ik, dat mag dus niet. Types zijn niet gelijk.

"The shell stopped unexpectedly and Explorer.exe was restarted."


  • jelmervos
  • Registratie: Oktober 2000
  • Niet online

jelmervos

Simple user

Topicstarter
Wat ik ook niet snap is waarom die toesten niet bij mijn component uitkomen en wel bij het uiteindelijk component op de form.

"The shell stopped unexpectedly and Explorer.exe was restarted."


Verwijderd

Dit moet werken:
code:
1
2
3
4
function TMyDBGrid.CreateEditor: TInplaceEdit;
begin
  Result := inherited CreateEditor;
end;

Wazig, effe de help nalezen

  • jelmervos
  • Registratie: Oktober 2000
  • Niet online

jelmervos

Simple user

Topicstarter
Dat werkt wel, nu nog een hook zetten om de berichten van toets af te vangen.

"The shell stopped unexpectedly and Explorer.exe was restarted."


Verwijderd

Welke toets wil je trouwens afvangen; als je in TInplaceEdit.KeyDown kijkt kun je zien dat TInplaceEdit sommige toetsen doorstuurt naar de grid en andere niet.

  • jelmervos
  • Registratie: Oktober 2000
  • Niet online

jelmervos

Simple user

Topicstarter
Ik moet gewoon F9 iig hebben, dit is gegevens doorvoeren, en moet dus zeker weten kunnen in Editor mode.

"The shell stopped unexpectedly and Explorer.exe was restarted."


Verwijderd

F9 wordt alleen naar de OnKeyDown event van de grid gestuurd en dus niet naar de grid zelf [lees: niet naar de KeyDown method]

  • jelmervos
  • Registratie: Oktober 2000
  • Niet online

jelmervos

Simple user

Topicstarter
Ik zie het, moet dus toch een Hook zetten op die handle.

"The shell stopped unexpectedly and Explorer.exe was restarted."


  • jelmervos
  • Registratie: Oktober 2000
  • Niet online

jelmervos

Simple user

Topicstarter
Waarom kan ik dit niet doen:
code:
1
2
3
4
5
6
7
8
function TMyDBGrid.CreateEditor: TInplaceEdit;
begin
  Result := inherited CreateEditor;
  InpEditHNWD := Result.Handle;
  WndPtrProc := MakeObjectInstance(WindowProc);
  OldPtrWndProc := Pointer(SetWindowLong(InpEditHNWD,
               GWL_WNDPROC, Longint(WndPtrProc)));
end;

"The shell stopped unexpectedly and Explorer.exe was restarted."


Verwijderd

Zoiets:
code:
1
2
3
4
5
6
7
8
function TMyDBGrid.CreateEditor: TInplaceEdit;
begin
  Result := inherited CreateEditor;

  FOldEditorProc := Pointer(GetWindowLong(Result.Handle, GWL_WNDPROC));
  FNewEditorProc := MakeObjectInstance(HookEditorProc);
  SetWindowLong(Result.Handle, GWL_WNDPROC, LongInt(FNewEditorProc));
end;

  • jelmervos
  • Registratie: Oktober 2000
  • Niet online

jelmervos

Simple user

Topicstarter
Maar ik heb die handle later toch weer nodig voor deze:
code:
1
2
3
4
5
6
procedure TMyDBGrid.WindowProc(var msg: TMessage);
begin
  with Msg do begin
    Result := CallWindowProc(OldPtrWndProc, InpEditHNWD, Msg, wParam, lParam);
  end;
end;

"The shell stopped unexpectedly and Explorer.exe was restarted."


Verwijderd

Oeps, sorry beetje onzinnige reactie :o

Maar wat lukt er nu dan niet?

[edit]

WindowProc is trouwens wel een linke naam; zit ook in TControl..

  • jelmervos
  • Registratie: Oktober 2000
  • Niet online

jelmervos

Simple user

Topicstarter
Zodra hij die Result.Handle wil opvragen krijg ik deze exception:

Control '' has no parent window

"The shell stopped unexpectedly and Explorer.exe was restarted."


  • jelmervos
  • Registratie: Oktober 2000
  • Niet online

jelmervos

Simple user

Topicstarter
Op dinsdag 26 maart 2002 17:45 schreef DiFool het volgende:
[..]

WindowProc is trouwens wel een linke naam; zit ook in TControl..
Doen we toch een andere. :)

"The shell stopped unexpectedly and Explorer.exe was restarted."


Verwijderd

Dan heeft ie een parent nodig; dus doe
code:
1
Result.Parent := Self;

Na de Result := inherited CreateEditor;

  • jelmervos
  • Registratie: Oktober 2000
  • Niet online

jelmervos

Simple user

Topicstarter
Cool, het werkt!

Moet ik niet ergens nog wat destroyen? Zoiets als dit:
code:
1
2
3
4
5
6
7
8
9
10
11
destructor TMyComp.Destroy;
begin
  if not (csDesigning in ComponentState) then
  begin
    if Assigned(OldFormProc) then
    SetWindowLong(TForm(Owner).Handle, GWL_WNDPROC, LongInt(OldFormProc));
    if Assigned(NewFormProc) then
    FreeObjectInstance(NewFormProc);
    NewFormProc := nil;
    OldFormProc := nil;
  end;

Maar waar doe ik dat dan?

"The shell stopped unexpectedly and Explorer.exe was restarted."


Verwijderd

Een TCustomGrid maakt maar 1 instantie van TInplaceEdit; deze wordt gecreeerd als het nodig is en gedestroyed als de TCustomGrid zelf wordt gedestroyed.
code:
1
2
3
4
5
6
7
8
destructor TCustomGrid.Destroy;
begin
  FInplaceEdit.Free;
  inherited Destroy;
  FreeMem(FColWidths);
  FreeMem(FRowHeights);
  FreeMem(FTabStops);
end;

Voordat die FInplaceEdit gedestroyed wordt moet je dus FreeObjectInstance aanroepen; anders zouder er nog messages naar een niet meer bestaand object kunnen worden verstuurd.

Dus doe zoiets:
code:
1
2
3
4
5
6
destructor TMyDbGrid.Destroy;
begin
  if Assigned(FNewEditorProc) then
    FreeObjectInstance(FNewEditorProc);
  inherited;
end;

Maarre, ik zat wat in de VCL te kijken; en zag dat je ook gewoon de WindowProc property van een control kan veranderen; volgens mij moet dat hetzelfde effect hebben.

Dan hoef je dus niet MakeObjectInstance of FreeObjectInstance aan te roepen.

Beetje raar, want je komt veel implementaties met MakeObjectInstance etc. tegen, maar niet met WindowProc :?

  • jelmervos
  • Registratie: Oktober 2000
  • Niet online

jelmervos

Simple user

Topicstarter
Thanks, zal er zo ff mee bezig. :)

"The shell stopped unexpectedly and Explorer.exe was restarted."


  • jelmervos
  • Registratie: Oktober 2000
  • Niet online

jelmervos

Simple user

Topicstarter
Maar als ik zelf die WindowProc property zet, waar komen de toets aanslagen dan binnen?

Edit: heb het al :)

"The shell stopped unexpectedly and Explorer.exe was restarted."


Verwijderd

Als ik kijk naar de code die jij gaf:
code:
1
2
3
4
5
6
7
8
function TMyDBGrid.CreateEditor: TInplaceEdit;
begin
  Result := inherited CreateEditor;
  InpEditHNWD := Result.Handle;
  WndPtrProc := MakeObjectInstance(WindowProc);
  OldPtrWndProc := Pointer(SetWindowLong(InpEditHNWD,
               GWL_WNDPROC, Longint(WndPtrProc)));
end;

Zou je zoiets kunnen doen:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function TMyDBGrid.CreateEditor: TInplaceEdit;
begin
  Result := inherited CreateEditor;
  FOldEditorWndProc := Result.WindowProc;
  Result.WindowProc := EditorWndProc;
end;

procedure TMyDBGrid.EditorWndProc(var Message: TMessage);
begin
  with Message do
    case Msg of
    ..
    else
    FOldEditorWndProc(Message);
    end;
end;

Met FOldEditorWndProc van het type TWndMethod uit Controls.pas

  • jelmervos
  • Registratie: Oktober 2000
  • Niet online

jelmervos

Simple user

Topicstarter
Idd, had ook al zoiets.

He, mag ik jou hartelijk bedanken voor je hulp. Je hebt me vaker met dingen geholpen, leer er heel veel van. Bedankt!

"The shell stopped unexpectedly and Explorer.exe was restarted."


Verwijderd

Geen dank, ik leer er zelf ook veel van :)
Pagina: 1