Give RadioButton focus without selecting?

M

Matt B

I know this is a bit of nonstandard behavior, but I would like the
Enter key to move focus through a group of radiobuttons without
selecting them. The user would then have to use Space or actually
click to select one of the radiobuttons to select.

The default behavior of Focus() seems to also select the radiobutton,
but I can't seem to find what is causing this to happen.

I would like very much to seperate the Focus and the selection. Any
ideas on how to accomplish this?

Thanks much,
Matt
 
P

Peter Duniho

I know this is a bit of nonstandard behavior, but I would like the
Enter key to move focus through a group of radiobuttons without
selecting them. The user would then have to use Space or actually
click to select one of the radiobuttons to select.

The default behavior of Focus() seems to also select the radiobutton,
but I can't seem to find what is causing this to happen.

I would like very much to seperate the Focus and the selection. Any
ideas on how to accomplish this?

If I understand the question correctly, you want the Enter key to behave
like the Tab or arrow keys, rather than like the Space key?

If so, you can special-case the Enter key in the appropriate control's
ProcessDialogKey() method. I haven't experimented with the radio-group
question you're specifically asking about, but it seems likely to me that
either the form, or possibly the group box containing the radio buttons,
will need to override this method. If the form, then you already have a
derived class in which you can put the override. If the group box, then
you will need to create your own derived class and then use that in your
form.

In the override, you can either handle the behavior explicitly (changing
focus rather than changing the state of the button), or perhaps better
would be to map the Enter key to the Tab key by passing Keys.Tab to the
base ProcessDialogKey() method when the Enter key is passed to your method.

As you note, this *is* non-standard behavior, and you should definitely
consider carefully whether your perceived user benefit really outweighs
the potential problems caused by changing the behavior of the keys in an
otherwise-standard UI.

Pete
 
P

Peter Duniho

[...]
I would like very much to seperate the Focus and the selection. Any
ideas on how to accomplish this?

Sorry for my previous post. I had a few moments and decided to look at
the exact behavior of the radio group, and realized (as you probably
already knew) that the Tab and arrow keys don't really do what you're
asking for.

In playing with it, I found a couple of things:

* The basic idea of overriding the ProcessDialogKey() method to handle
the Enter key is sound (though, make sure you properly distinguish between
using the Enter key and the Return key...there are two different key
codes, corresponding to two different keys on standard keyboard).

* One thing you'll run into is that RadioButton controls are added to
the container's Controls property in reverse order from their tab order.
I had to set focus to the *previous* item in the container in order for
the *next* one in tab order to be selected.

* Simply changing focus is enough to get the state of the RadioButtons
to change. Unfortunately this is contrary and key to your desired
behavior. I haven't had enough time to look into where this happens or
how one might change it, but I'm curious so who knows...maybe I'll play
around with it one day. :) Of course, since this is pretty much the crux
of your question, I probably have offered nothing useful to this thread.
Sorry. :)

Anyway, on the off-chance this is useful, here's the ProcessDialogKey()
method I wrote that at least deals with handling the focus changing (in my
case, I had a "groupBox1" that contained the radio group...you can
generalize the code further if necessary, of course):

protected override bool ProcessDialogKey(Keys keyData)
{
if (keyData == Keys.Return)
{
int ictl = groupBox1.Controls.IndexOf(ActiveControl);

if (ictl != -1)
{
groupBox1.Controls[(ictl + groupBox1.Controls.Count-
1) % groupBox1.Controls.Count].Focus();
}
}

return base.ProcessDialogKey(keyData);
}

Pete
 
P

Peter Duniho

[...]
* Simply changing focus is enough to get the state of the
RadioButtons to change. Unfortunately this is contrary and key to your
desired behavior. I haven't had enough time to look into where this
happens or how one might change it, but I'm curious so who knows...maybe
I'll play around with it one day. :) Of course, since this is pretty
much the crux of your question, I probably have offered nothing useful
to this thread. Sorry. :)

For what it's worth, I did realize that you can set the
RadioButton.AutoCheck property to false to disable the auto-checking
behavior. However, if you do this, you'll have to explicitly handle all
of the related behavior, including treating a group of radio buttons as a
group.

This shouldn't be *too* hard, but there will be lots of little details to
make sure you've covered. IMHO, maybe a good enough reason to be happy
with the built-in, default behavior. :)

Pete
 
P

Peter Duniho

For what it's worth, I did realize that you can set the
RadioButton.AutoCheck property to false to disable the auto-checking
behavior. However, if you do this, you'll have to explicitly handle all
of the related behavior, including treating a group of radio buttons as
a group.

I've got to say, I cannot explain why this question intrigues me so much..
I really ought to be doing other things, but I can't stop playing with it.

Anyway...see below for some code that does what I think you're asking
for. Some notes:

0) The code assumes that you have only radio buttons within the
containing parent. In this case a GroupBox named "groupBox1". If you
wanted to generalize the code, you would need to change the code that
figures out the next control in the tab order to deal with the possibility
of non-RadioButton controls in the group, and/or groups of controls for
which you don't want the Return key to behave this way.

1) The Click handler is required because for this to work you need to
disable the default functionality by setting the AutoCheck property of
each RadioButton instance to false. With that disabled, you need to
handle the checking behavior yourself.

2) I find the need to use a flag to disable behavior in the Click
handler to be a bit kludgy. But since it appears to me that the Click
event is auto-generated in response to a Control getting the focus, I
don't see any immediate way around that.

3) For some reason, the focus rectangle is not displayed until you
first use the Tab key to move focus to the group. However, this is the
default behavior even when AutoCheck is true, so at least my code isn't at
fault for that. But it does produce a kind of strange situation in which
you use the Return key to change the focus, but there's no UI feedback
indicating that. Normally it wouldn't be a problem because the checked
state of the buttons would change, providing that feedback. You will
probably want to figure out if there's a way to make the focus rectangle
show up. I presume the basic mechanism to do that is simple, though I
don't know what the magic incantation is off the top of my head. :)

On this point #3, I found a thread on MSDN that suggests that this is not
the only place one runs into this strange behavior. It appears that the
window controls (either in .NET or, more likely, Windows itself) have a
general problem that until you switch focus using the Tab key, the focus
rectangle is not shown. I think there may be a way to change the behavior
if you make a new radio button class that derives from RadioButton, and
then either set the ShowFocusCues property to "true" or provide a way to
call the OnChangeUICues() method to explicitly have the focus rectangle
shown. But in the interest of not investing even more time in someone
else's attempt to bypass the standard UI behavior :), I've decided to not
bother trying that. :)

4) I make no claims that I have in this code handled all of the subtle
issues that may exist with respect to the selection and focus-changing.
It *seems* to work fine for me, but I easily could have missed something..
The fact that I could have is one of the reasons I try to avoid doing this
sort of "change the standard UI to be non-standard" thing.

Here's the code I came up with (I promise I'm done fiddling with this
now...sorry for all the different replies :) ):

private bool _fEnterKey;

protected override bool ProcessDialogKey(Keys keyData)
{
if (keyData == Keys.Return)
{
int ictl = groupBox1.Controls.IndexOf(ActiveControl);

if (ictl != -1)
{
Control ctlFocus = groupBox1.Controls[(ictl
+ groupBox1.Controls.Count - 1) % groupBox1.Controls.Count];

_fEnterKey = true;
ActiveControl.TabStop = false;
ctlFocus.TabStop = true;
ctlFocus.Focus();
_fEnterKey = false;
}
}

return base.ProcessDialogKey(keyData);
}

private void radioButton1_Click(object sender, EventArgs e)
{
GroupBox grp = (GroupBox)((Control)sender).Parent;

if (!_fEnterKey && grp != null)
{
foreach (RadioButton radio in grp.Controls)
{
if (radio != sender)
{
radio.TabStop = false;
radio.Checked = false;
}
}

((Control)sender).TabStop = true;
((RadioButton)sender).Checked = true;
}
}
 
M

Matt B

For what it's worth, I did realize that you can set the
RadioButton.AutoCheck property to false to disable the auto-checking
behavior. However, if you do this, you'll have to explicitly handle all
of the related behavior, including treating a group of radio buttons as
a group.

I've got to say, I cannot explain why this question intrigues me so much.
I really ought to be doing other things, but I can't stop playing with it.

Anyway...see below for some code that does what I think you're asking
for. Some notes:

0) The code assumes that you have only radio buttons within the
containing parent. In this case a GroupBox named "groupBox1". If you
wanted to generalize the code, you would need to change the code that
figures out the next control in the tab order to deal with the possibility
of non-RadioButton controls in the group, and/or groups of controls for
which you don't want the Return key to behave this way.

1) The Click handler is required because for this to work you need to
disable the default functionality by setting the AutoCheck property of
each RadioButton instance to false. With that disabled, you need to
handle the checking behavior yourself.

2) I find the need to use a flag to disable behavior in the Click
handler to be a bit kludgy. But since it appears to me that the Click
event is auto-generated in response to a Control getting the focus, I
don't see any immediate way around that.

3) For some reason, the focus rectangle is not displayed until you
first use the Tab key to move focus to the group. However, this is the
default behavior even when AutoCheck is true, so at least my code isn't at
fault for that. But it does produce a kind of strange situation in which
you use the Return key to change the focus, but there's no UI feedback
indicating that. Normally it wouldn't be a problem because the checked
state of the buttons would change, providing that feedback. You will
probably want to figure out if there's a way to make the focus rectangle
show up. I presume the basic mechanism to do that is simple, though I
don't know what the magic incantation is off the top of my head. :)

On this point #3, I found a thread on MSDN that suggests that this is not
the only place one runs into this strange behavior. It appears that the
window controls (either in .NET or, more likely, Windows itself) have a
general problem that until you switch focus using the Tab key, the focus
rectangle is not shown. I think there may be a way to change the behavior
if you make a new radio button class that derives from RadioButton, and
then either set the ShowFocusCues property to "true" or provide a way to
call the OnChangeUICues() method to explicitly have the focus rectangle
shown. But in the interest of not investing even more time in someone
else's attempt to bypass the standard UI behavior :), I've decided to not
bother trying that. :)

4) I make no claims that I have in this code handled all of the subtle
issues that may exist with respect to the selection and focus-changing.
It *seems* to work fine for me, but I easily could have missed something.
The fact that I could have is one of the reasons I try to avoid doing this
sort of "change the standard UI to be non-standard" thing.

Here's the code I came up with (I promise I'm done fiddling with this
now...sorry for all the different replies :) ):

private bool _fEnterKey;

protected override bool ProcessDialogKey(Keys keyData)
{
if (keyData == Keys.Return)
{
int ictl = groupBox1.Controls.IndexOf(ActiveControl);

if (ictl != -1)
{
Control ctlFocus = groupBox1.Controls[(ictl
+ groupBox1.Controls.Count - 1) % groupBox1.Controls.Count];

_fEnterKey = true;
ActiveControl.TabStop = false;
ctlFocus.TabStop = true;
ctlFocus.Focus();
_fEnterKey = false;
}
}

return base.ProcessDialogKey(keyData);
}

private void radioButton1_Click(object sender, EventArgs e)
{
GroupBox grp = (GroupBox)((Control)sender).Parent;

if (!_fEnterKey && grp != null)
{
foreach (RadioButton radio in grp.Controls)
{
if (radio != sender)
{
radio.TabStop = false;
radio.Checked = false;
}
}

((Control)sender).TabStop = true;
((RadioButton)sender).Checked = true;
}
}


Thanks a lot for the responses Peter! It seems like we both had very
similar ideas on how to get around this. What I've ended up doing is
setting AutoCheck to false and then handling all of the behavior of
the group myself. This seems to work just fine.

FYI, the users are used to this behavior already, this is just a re-
write. The questions they are answering are very sensitive and we
don't want the program answering these questions by default with an
accidental keystroke.
 

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