Hello CubicDesign. I solved your problem :-) And it's not a bug in 
Delphi, it's just something you misunderstood.

Next time, please be more reserved about announcing a bug. It only masks 
the problem and scares away people that might be able to help. If your 
subject line would have looked something like this: "TEdit.OnChange not 
triggering in custom component" you would have attracted allot more 
people. And it would have shown that you care and you try to fix your 
problem. I took the time to debug your problem out of anger. If  Horváth 
Márton didn't post a message that could be misinterpreted as a 
bug-confirmation, I wouldn't have bothered with your question.

So here are the steps I took to fix your problem:

(1) Create the code, hit F9, run the code, notice it's OnChange doesn't 
work.

(2) First test: Did I really assign OnChange? Is there some strange code 
changing my OnChange event handler? Put a button on the form hosting 
your grid and add this code to it:
<code>
if Assigned(MyGrid.Editor.OnChange) then ShowMessage('it''s assigned!')
else ShowMessage('NOT assigned! Got you!');
</code>
Well... that's not the issue. OnChange is in fact assigned, so the code 
is not in the fact that OnChange is not assigned.

(3) Ask this question: If OnChange IS assigned, why doesn't it trigger? 
The answer is probably in the code that makes the actual call to 
OnChange. Open StdCtrls.pas (the unit where TEdit is defined) and do a 
search for OnChange. You'll find the definition you're after at line 232 
(property OnChange of object TCustomEdit). You'll then find it's 
internal var name (FOnChange). Now do a search for FOnChange and see how 
it's used.

(4) Searching for FOnChange in that file you'll notice a call at line 
1991, in TCustomEdit.Change; That's very "standard looking" code for the 
Delphi VCL. It provides a very simple way for calling the OnChange event 
handler from other places in the code while providing the "Assigned" 
test. So now you'll have to look for Change, to see how that's called.

(5) The first call to the Change routine is found at line 2032 in the 
CNCommand event handler. So it's something about a command handler, that 
calls for a MSDN search (yes, it's MSDN search, Microsoft, not Borland). 
Do a MSDN search for EN_CHANGE (or, if you're like me and reaaaaaly hate 
MSDN search, do a google search and click on the first MSDN link :-) )

(6) It will get you here:
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc/platform/commctls/editcontrols/editcontrolreference/editcontrolmessages/en_change.asp
Read the document. You'll notice something really funny: The message is 
sent to the control THROUGH THE PARENT WINDOW!!! Boy that's big news. So 
it's not actually the TEdit control that is to blame for not triggering 
this message but it's parent window. But what makes the parent window 
NOT pass the notification back to TEdit, as it's expected of it? Well... 
TStringGrid is not a typical control because it's really a "final" 
control, it's not like TPanel. It's something you drop on a form and use 
directly. So take a look at how TPanel handles WM_COMMAND, just to get 
an idea of how it SHOULD be done.

(7) Open ExtCtrls (the "home" of TPanel) and do a search for WM_COMMAND. 
What? It's not there? That's strage... so the message is not handled in 
TPanel, but in TWinControl, TPanel's parent. That's bad news since 
TStringGrid also inherits from TWinControl so it SHOULD inherit this 
behavior! Take a look into Controls.pas (the of TWinControl) to further 
understand this problem.

(8) In Controls.pas at line 1114 you'll find the definition for the 
WM_COMMAND handler in TWinControl. Hit ctrl+shift+down to get to the 
implementation and you'll find the implementation for this handle. VERY 
simple code. Why isn't it working? We'll have to put a brakepoint on 
this line to see what happens. Make sure you tick "use debug DCU's" in 
project options a do a "build". Hit run. Click on your miss-behaved edit 
and type some text. You'll notice something: your brakepoint does NOT 
trigger! But that's not possible, because, according to MSDN 
documentation a change in an edit control would automatically send an 
WM_COMMAND to it's parent window. Well... maybe there's something in the 
way, like an other WM_COMMAND handler that does not call inherited?

(9) Open grids.pas, home of TStringGrid and do a search for WM_COMMAND. 
And at line 315 you'll find the definition for an WM_COMMAND handler! 
Again, hit ctrl+shift+down to get to the implementation (at line 3901). 
Notice something? It's not the same code, and it does not call 
inherited! Since it doesn't call inherited the code in TWinControl 
doesn't get invoked and, in turn, it doesn't send the CN_COMMAND message 
to TCustomEdit so the EN_CHANGE never gets handled by TCustomEdit so the 
OnChange event handler is not triggered!

(10) well... that's it. You've cracked it! You found the cause of your 
problem. Now, is it a bug? I don't think so. TStringGrid is free to 
handle WM_COMMAND any way it wants. After all, that's what the message 
is for... Should TStringGrid have looked at the Handle for the control 
and call "inherited" if it doesn't know the handle? TStringGrid is an 
"final" component, just like TCustomEdit. It's not supposed to get 
controls on top of it, so why bother with the extra 3 lines of code?

The fix: Add this code to YOUR grid:
<def>
procedure WMCommand(var Message: TWMCommand); message WM_COMMAND;
</def>
<implementation
procedure TBGrid.WMCommand(var Message: TWMCommand);

  function DoControlMsg(ControlHandle: HWnd; var Message): Boolean;
  var
    Control: TWinControl;
  begin
    DoControlMsg := False;
    Control := FindControl(ControlHandle);
    if Control <> nil then
      with TMessage(Message) do
      begin
        Result := Control.Perform(Msg + CN_BASE, WParam, LParam);
        DoControlMsg := True;
      end;
  end;

begin
  if Message.Ctl = Editor.Handle then
    DoControlMsg(Message.Ctl, Message) // Restore "normal" behaviour
                                       // for Editor
  else
    inherited; // do the TStringGrid thing
end;
</implementation>

My code only does this: it restores TWinControl behaviour for WM_COMMAND 
if the control is your Editor. Plane and simple. DoControlMsg is 
copy-pasted from the Controls unit so it wasn't difficult.
--
Cosmin Prund,
Happy Coding everyone!

CubicDesign wrote:
> I use small cells (13x13 pixels) in my TStringGrid, and the 
> InpalceEditor (which I use it now) is also small, so I need to replace 
> it with something bigger.
>
> Please tell me if the OnClick worked in your Delphi because in mine is 
> working.
> It is so strange that OnClick is working and OnChange is not.
>
> Somebody with a version of Delphi higher than 7 can confirm if the bug 
> was fixed?
> Maybe is finally the time to buy Delphi 2005.
> Anyway, Delphi 7 started to behave very strange in the last month and 
> the IDE editor crashed more often than usual.
>
> They say that Delphi is the most RAD tool. Maybe it will be true if its 
> IDE will be not so damn buggy.
> (This does not mean that I will not buy/upgrade Delphi anymore :)
>
>
>
>
> Horváth Márton wrote:
>   
>> Hi,
>>
>>   I have tried your sample, and I found the same (in D7). I think the guilty 
>> is the StringGrid descendant, who is the parent of the TEdit. It steal the 
>> Change message from the Edit. You can use the OnKey.... events (it is so 
>> ugly), or (more confortable) the TInPlaceEdit descendant instead the TEdit.
>>   In the TInPlaceEdit descendant you can override the protected methods: 
>> UpdateContents, Click, DblClick as you wish.
>>
>> regards.: m.
>>
>> ----- Original Message ----- 
>> From: "CubicDesign" <[EMAIL PROTECTED]>
>> To: <delphi-talk@elists.org>
>> Sent: Thursday, November 16, 2006 1:13 PM
>> Subject: Bug in Delphi?
>>
>>
>>   
>>     
>>> *Dear List   * :)
>>>
>>> I created my own custom TStringList VCL and now I want to add a better
>>> editor to it. I want to let the user to enter the text in a TEdit
>>> control, positioned right above the cell.
>>> For this, I want to create at run time a TEdit control. All is nice and
>>> beautiful until I want to assign an event to TEdit.OnChange. It does not
>>> assign:
>>>
>>>
>>> TYPE
>>>  TBGrid= class(TStringgrid)
>>>   Private
>>>     ...
>>>   protected
>>>     Editor: TEdit;     { Custom InPlace Editor in StringGrid }
>>>   End;
>>>
>>>
>>> constructor TBGrid.Create;
>>> begin
>>>  inherited Create(AOwner);
>>>  Editor:= TEdit.Create(Self);
>>>  Editor.Parent   := Self;
>>>  Editor.OnChange := Edit2Change;       <- this is working
>>>  Editor.OnClick     := Edit2Change;       <- doesn't assign
>>> bla bla bla
>>> End;
>>>
>>>
>>> procedure TBGrid.Edit2Change(Sender: TObject);
>>> begin
>>>  Bip(5000, 200);
>>> end;
>>>
>>>
>>>
>>> I put the StringGrid on a form, I run the program. The Editor shows.
>>> If I click on it, it beeps. If I type a char in it, it does not beep.
>>>
>>>
>>>
>>> __________________________________________________
>>> Delphi-Talk mailing list -> Delphi-Talk@elists.org
>>> http://www.elists.org/mailman/listinfo/delphi-talk
>>>
>>>
>>>     
>>>       
>> __________________________________________________
>> Delphi-Talk mailing list -> Delphi-Talk@elists.org
>> http://www.elists.org/mailman/listinfo/delphi-talk
>>
>>   
>>     
> __________________________________________________
> Delphi-Talk mailing list -> Delphi-Talk@elists.org
> http://www.elists.org/mailman/listinfo/delphi-talk
>
>   

__________________________________________________
Delphi-Talk mailing list -> Delphi-Talk@elists.org
http://www.elists.org/mailman/listinfo/delphi-talk

Reply via email to