How do you directly send messages / events to controls?

T

TheSilverHammer

I have two examples where I need to do this:

Example 1:
Lets say that I have a bunch of TextBox's on a form. All these textbox
objects share a common KeyPress event which validates the input to make sure
that it is a number, control character (IE: Backspace, enter key) or special
symbol such as '.' or '-' so you can have numbers like 1.2 or -12.

Each of these textboxes also have an event for "Leave" which is unique to
each textbox, and these leave events update a data structure which is
relevant to the particular textbox.

What I would like to do is is have the generic "KeyPress" event handler look
for the enter key and then fire off the leave event for the particular
textbox. The code will look something like this:

// Handle an Enter key
if ( 13 == e.KeyChar )
{
TextBox TB = sender as TextBox;
// Call Event Here
}

The "Call Event Here" might be TB.OnLeave(), but these methods are protected
so I can't call them. There doesn't appear to be any kind of SendMessage
API or any way to tell TextBox TB to run its Leave event. In the MFC days,
you would just send a message like SendMessage(WM_LEAVE) or something like
that.

The only solution I can see here is do to something clunky like:

if ( ReferenceEquals(TB, textBoxAlpha) ) { // Call textBoxAlpha leave event
if ( ReferenceEquals(TB, textBoxBeta)) { // Call textBoxBeta leave event
etc...

It seems that there must be a better way. If you have abstract control X
and you know it has a handler for Event Leave(), why can't you invoke the
event?

The windows form does it, but it is all hidden and there is no source code
to step through to see how it is all done.


Example 2:

I have a user control, which has a FlowLayout Panel which has other user
controls in them. The user controls in the panel are mostly just visual
meaning that you do not interact with them directly, however I want to know
if anyone clicks on one of the controls. I want the higher level user
control that owns the panel, that owns the collection of 'visual' user
controls, to know if anyone clicks on one of these visual user controls.
To make matters more complicated is the fact that the visual controls have
other controls on them like static text boxes. If a user clicks on one of
those, even the 'visual' control doesn't see the event. This is kind of a
Parent Notify thing that seems to be missing.

The way I manged to do this was to override the WinProc for the base class
of my Visual controls to sniff for the click message and then issue its own
click event to itself (which is just about the only thing you can do). The
User control that has the panel of these visual controls just adds a
ClickEvent to the visual user control which lets it know whenever someone
clicked on it.

It works, but seems like a round about way of doing this and it requires you
to get into the guts of the WinProc and know what messages to look for. I
could not have done this unless I found some that gave this kind of example.
Here is the function that does the trick:
protected override void WndProc(ref Message m)
{
//0x210 is WM_PARENTNOTIFY

if (m.MSG == 0x210 && m.WParam.ToInt32() == 513) //513 is
WM_LBUTTONCLICK
{
this.OnClick(new EventArgs());
}

base.WndProc(ref m);
}

There are some nice magic numbers there which I would not have found in any
C# documentation.

So, is there a way to do this without all this low-level stuff?

I do not understand why the OnEvent() methods are protected instead of
public or why there isn't a SendMessage() function of some kind. It seems
that the GUI api classes are locked up tight and only Microsoft is allowed to
send anyone message or events.
 
S

Scott Roberts

What I would like to do is is have the generic "KeyPress" event handler
look
for the enter key and then fire off the leave event for the particular
textbox. The code will look something like this:

// Handle an Enter key
if ( 13 == e.KeyChar )
{
TextBox TB = sender as TextBox;
// Call Event Here
}


I don't really see why you want to fire the Leave event. Why don't you just
call the "UpdateDataStructure()" method that the Leave event (presumably)
calls?
 
P

Peter Duniho

I have two examples where I need to do this:

I'm not sure your examples are actually situations where you "need to do
this". For certain, the first is not.
[...]
What I would like to do is is have the generic "KeyPress" event handler
look
for the enter key and then fire off the leave event for the particular
textbox.

You're describing the implementation you desire, not the actual high-level
description of the problem. In particular, it seems to me that what you
really want is simply to perform the same validation that occurs when the
Leave event is raised, any time that the Enter key is pressed.

You can do this at least two different ways, both without actually having
the control get the unmanaged windows message associated with actually
losing focus:

* Call your event handler directly, passing the control reference as
the sender, and creating a new EventArgs() instance
* Create a helper function that's called from the event handler, and
call that instead from both the Leave event handler and the KeyPress event
handler.

I haven't had much time to look at the mouse click question, but I suspect
there is similarly a different approach that would accomplish what you
want. I seem to recall some sort of setting or mechanism to cause the
parent control to get and respond to clicks before children see them.
This would be what you want.

If no one else has posted the answer to the second scenario and I figure
it out later, I'll post what I come up with.

There are cases where customizing the behavior of the UI is difficult in
managed code, but I don't think that these are examples of that.

Pete
 
N

Nicholas Paldino [.NET/C# MVP]

Example 1:

Actually, the windows form doesn't invoke the event. It simply passes
the window message along to the control, and the control invokes the event.
Only the instance can invoke the event on itself.

The solution here is simple. Just call the method directly that
contains the event logic. Of course, you will have to have some way of
mapping the textbox to the appropriate event handler, but it's not hard
(just have a structure which exposes delegate instances for the appropriate
events, then have a dictionary keyed on the textbox).

Then, in your code, you can cycle through the textboxes, find the
associated event handlers, and then invoke it yourself.

Example 2:

You will want to look at the IMessageFilter interface here. It will
allow you to process messages before they are dispatched to the appropriate
controls. You can intercept the click windows message, see if it is for the
control (or a child control) of the control you want to monitor, and proceed
from there.

Exposing events in that manner (a method to fire the event) is really a
security risk, in that you are able to manipulate the control to do
something and misrepresent itself (if you are telling the object to fire the
event, the object isn't really secure). Also, there is the case of firing
an event which isn't appropriate. For example, if you fire the Leave event,
but you still have focus, what is supposed to happen? It just doesn't make
sense to do so, just to access logic which should be separated out correctly
in the first place.
 
T

TheSilverHammer

There are some good points here, but let me make this more abstract as to
avoid getting hung up on "why do you want to do X?".

From the vibe I am getting, the ultimate answer is this:

this.OnClick(); // Possible
that.OnClick(); // Impossible

Right?

The follow up this, if is the above is correct.

Without sniffing windows message, the follow can not be done (example 2
quickly restated):

You have a form that has a flow layout panel.

The flow layout panel is populated with user controls, each of which may
have other controls on them, but for the sake of argument are
non-interactive. IE: They are static labels or icons, but no buttons or
check boxes.

There is no way, without windows message sniffing, to have the form, be
notified when a user control inside the panel has been clicked.

This is particularly true because the user control has a static label, which
when clicked on, does not notify the user control it has been clicked, and
therefor the the user control can't notify the panel or parent form that it
has been clicked.

Is this correct? This can not be done with out message sniffing? The C#
control / library has no provision beyond a message sniffing hack to send or
bounce messages to the parent control.










Nicholas Paldino said:
Example 1:

Actually, the windows form doesn't invoke the event. It simply passes
the window message along to the control, and the control invokes the event.
Only the instance can invoke the event on itself.

The solution here is simple. Just call the method directly that
contains the event logic. Of course, you will have to have some way of
mapping the text box to the appropriate event handler, but it's not hard
(just have a structure which exposes delegate instances for the appropriate
events, then have a dictionary keyed on the text box).

Then, in your code, you can cycle through the text boxes, find the
associated event handlers, and then invoke it yourself.

Example 2:

You will want to look at the IMessageFilter interface here. It will
allow you to process messages before they are dispatched to the appropriate
controls. You can intercept the click windows message, see if it is for the
control (or a child control) of the control you want to monitor, and proceed
from there.

Exposing events in that manner (a method to fire the event) is really a
security risk, in that you are able to manipulate the control to do
something and misrepresent itself (if you are telling the object to fire the
event, the object isn't really secure). Also, there is the case of firing
an event which isn't appropriate. For example, if you fire the Leave event,
but you still have focus, what is supposed to happen? It just doesn't make
sense to do so, just to access logic which should be separated out correctly
in the first place.

--
- Nicholas Paldino [.NET/C# MVP]
- (e-mail address removed)

TheSilverHammer said:
I have two examples where I need to do this:

Example 1:
Lets say that I have a bunch of TextBox's on a form. All these textbox
objects share a common KeyPress event which validates the input to make
sure
that it is a number, control character (IE: Backspace, enter key) or
special
symbol such as '.' or '-' so you can have numbers like 1.2 or -12.

Each of these textboxes also have an event for "Leave" which is unique to
each textbox, and these leave events update a data structure which is
relevant to the particular textbox.

What I would like to do is is have the generic "KeyPress" event handler
look
for the enter key and then fire off the leave event for the particular
textbox. The code will look something like this:

// Handle an Enter key
if ( 13 == e.KeyChar )
{
TextBox TB = sender as TextBox;
// Call Event Here
}

The "Call Event Here" might be TB.OnLeave(), but these methods are
protected
so I can't call them. There doesn't appear to be any kind of SendMessage
API or any way to tell TextBox TB to run its Leave event. In the MFC
days,
you would just send a message like SendMessage(WM_LEAVE) or something like
that.

The only solution I can see here is do to something clunky like:

if ( ReferenceEquals(TB, textBoxAlpha) ) { // Call textBoxAlpha leave
event
if ( ReferenceEquals(TB, textBoxBeta)) { // Call textBoxBeta leave event
etc...

It seems that there must be a better way. If you have abstract control X
and you know it has a handler for Event Leave(), why can't you invoke the
event?

The windows form does it, but it is all hidden and there is no source code
to step through to see how it is all done.


Example 2:

I have a user control, which has a FlowLayout Panel which has other user
controls in them. The user controls in the panel are mostly just visual
meaning that you do not interact with them directly, however I want to
know
if anyone clicks on one of the controls. I want the higher level user
control that owns the panel, that owns the collection of 'visual' user
controls, to know if anyone clicks on one of these visual user controls.
To make matters more complicated is the fact that the visual controls have
other controls on them like static text boxes. If a user clicks on one of
those, even the 'visual' control doesn't see the event. This is kind of a
Parent Notify thing that seems to be missing.

The way I manged to do this was to override the WinProc for the base class
of my Visual controls to sniff for the click message and then issue its
own
click event to itself (which is just about the only thing you can do).
The
User control that has the panel of these visual controls just adds a
ClickEvent to the visual user control which lets it know whenever someone
clicked on it.

It works, but seems like a round about way of doing this and it requires
you
to get into the guts of the WinProc and know what messages to look for. I
could not have done this unless I found some that gave this kind of
example.
Here is the function that does the trick:
protected override void WndProc(ref Message m)
{
//0x210 is WM_PARENTNOTIFY

if (m.MSG == 0x210 && m.WParam.ToInt32() == 513) //513 is
WM_LBUTTONCLICK
{
this.OnClick(new EventArgs());
}

base.WndProc(ref m);
}

There are some nice magic numbers there which I would not have found in
any
C# documentation.

So, is there a way to do this without all this low-level stuff?

I do not understand why the OnEvent() methods are protected instead of
public or why there isn't a SendMessage() function of some kind. It seems
that the GUI api classes are locked up tight and only Microsoft is allowed
to
send anyone message or events.
 
F

Fredo

TheSilverHammer said:
There are some good points here, but let me make this more abstract as to
avoid getting hung up on "why do you want to do X?".

From the vibe I am getting, the ultimate answer is this:

this.OnClick(); // Possible
that.OnClick(); // Impossible

Right?

Without reflection, yes. Using reflection, you can call protected and
private members of any class. It's not recommended, but if you absolutely
must, it's there. There have been times when I've had to resort to
reflection.
The follow up this, if is the above is correct.

Without sniffing windows message, the follow can not be done (example 2
quickly restated):

You have a form that has a flow layout panel.

The flow layout panel is populated with user controls, each of which may
have other controls on them, but for the sake of argument are
non-interactive. IE: They are static labels or icons, but no buttons or
check boxes.

There is no way, without windows message sniffing, to have the form, be
notified when a user control inside the panel has been clicked.

This is particularly true because the user control has a static label,
which
when clicked on, does not notify the user control it has been clicked, and
therefor the the user control can't notify the panel or parent form that
it
has been clicked.

Is this correct? This can not be done with out message sniffing? The
C#
control / library has no provision beyond a message sniffing hack to send
or
bounce messages to the parent control.

I'm not sure what you mean? Why can't you just iterate through the controls
and subscribe to the events you're interested in? Something like this:

public MyForm()
{
InitializeComponent();
SubscribeClick();
}

private void SubscribeClick(Control parentControl)
{
foreach(Control control in parentControl.Controls)
{
control.Click += new EventHandler(MyClickHandler);
if (control.Controls.Count > 0)
{
SubscribeClick(control);
}
}
}

The code above will subscribe to the click event for every control on the
form, including contained controls. You can obviously make it picky about
which controls to subscribe to by checking their types or whatever and it
might make sense to keep a list somewhere of the controls you're subscribing
to in order to unsubscribe easily and reliably.

There's a great deal you can do without having to resort to messaging...
 
T

TheSilverHammer

public MyForm()
{
InitializeComponent();
SubscribeClick();
}

private void SubscribeClick(Control parentControl)
{
foreach(Control control in parentControl.Controls)
{
control.Click += new EventHandler(MyClickHandler);
if (control.Controls.Count > 0)
{
SubscribeClick(control);
}
}
}

The code above will subscribe to the click event for every control on the
form, including contained controls. You can obviously make it picky about
which controls to subscribe to by checking their types or whatever and it
might make sense to keep a list somewhere of the controls you're subscribing
to in order to unsubscribe easily and reliably.

There's a great deal you can do without having to resort to messaging...

I suppose you could do that. I didn't think about iterating via a
collection like that. I didn't think of basic user controls or
non-container controls as being containers, whiz they are. So the parent
notify problem is solved.

Still I do feel that I might have the need to send a control a message, but
that may be just MFC thinking. One basic example which is somewhat
contrived, would be UI automation. If I wanted to automate a few UI tasks,
maybe like a Macro a user could make or maybe for a training mode where the
program 'clicks' the buttons and fills in the fields for the user as part of
a tutorial, it would seem sending messages would be needed.

I totally understand and absolutely agree with an out-side program NOT being
able to send windows messages to your app. What business does any program
have to send a WM_GET_TEXT to an edit control? Furthermore, even a click
event would be insecure because if that pop-up on vista for Admin privileges
came up and a program could just click yes, it would defeat the purpose.

However, a program that can 'click' its own controls I can see some use for,
but I suppose there is always a work-around and there isn't enough demand to
create an API change to allow the app that owns the control to send messages
to its own controls.

Currently in a utility I am writing a user needs to click through several
panels to get to some area of functionality. I have been asked to be able
to launch the program in this mode being given some command line parameters.
Can I rework my code to do this? Yes, but it would be much easier to
simply say if I have a command line param X, then Click this control, click
that control, select this item and then click that button.

There seem to be a preponderance of MS guru's on this forum, I have another
big question I may ask which has never been answered. Maybe Ill post it
later today. Anyway, thanks to everyone who has responded to this question.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top