当前位置: 动力学知识库 > 问答 > 编程问答 >

c# - How can I work around setting IsChecked for ToggleButton in GotFocus event causing both Checked and Unchecked events to be called?

问题描述:

Requirements

When a user tabs to one of the toggle buttons, I want it to be checked and uncheck any other toggle buttons. If a user clicks on a toggle button, it's state should change from either checked to unchecked or unchecked to checked.

Issue

Setting the IsChecked property in the GotFocus event for a ToggleButton causes the Checked event to be called and then mysteriously unchecks the ToggleButton and causes the Unchecked event to be called.

Code

Here is the sample code from my lab project that demonstrates this.

XAML:

<Grid>

<StackPanel x:Name="ToggleButtonContainer" Orientation="Horizontal" VerticalAlignment="Top">

<ToggleButton Content="Toggle 1" Margin="2" Padding="4" GotFocus="ToggleButton_GotFocus" Checked="ToggleButton_Checked" Unchecked="ToggleButton_Unchecked" />

<ToggleButton Content="Toggle 2" Margin="2" Padding="4" GotFocus="ToggleButton_GotFocus" Checked="ToggleButton_Checked" Unchecked="ToggleButton_Unchecked" />

<ToggleButton Content="Toggle 3" Margin="2" Padding="4" GotFocus="ToggleButton_GotFocus" Checked="ToggleButton_Checked" Unchecked="ToggleButton_Unchecked" />

<ToggleButton Content="Toggle 4" Margin="2" Padding="4" GotFocus="ToggleButton_GotFocus" Checked="ToggleButton_Checked" Unchecked="ToggleButton_Unchecked" />

<ToggleButton Content="Toggle 5" Margin="2" Padding="4" GotFocus="ToggleButton_GotFocus" Checked="ToggleButton_Checked" Unchecked="ToggleButton_Unchecked" />

</StackPanel>

</Grid>

CODE:

 private void ToggleButton_Checked(object sender, RoutedEventArgs e)

{

System.Diagnostics.Debug.WriteLine("INFO: ToggleButton_Checked called by {0}", sender);

foreach (var toggleButton in ToggleButtonContainer.Children)

{

if (toggleButton != sender &&

(toggleButton as ToggleButton).IsChecked.HasValue &&

(toggleButton as ToggleButton).IsChecked.Value)

{

(toggleButton as ToggleButton).IsChecked = false;

}

}

}

private void ToggleButton_Unchecked(object sender, RoutedEventArgs e)

{

System.Diagnostics.Debug.WriteLine("INFO: ToggleButton_Unchecked called by {0}", sender);

}

private void ToggleButton_GotFocus(object sender, RoutedEventArgs e)

{

System.Diagnostics.Debug.WriteLine("INFO: ToggleButton_GotFocus called by {0}", sender);

(sender as ToggleButton).IsChecked = true;

}

Environment Info

  • Visual Studio 2013 Update 4
  • Windows 8 / Windows 10
  • .NET Framework 4.5

Observations

I have observed that if I click the button and drag away from it before releasing the left mouse button, then I don't experience the issue. This also happens when you debug and step through the Checked event (because again, the ToggleButton never receives the MouseLeftButtonUp event since the debugger has taken focus).

Questions

  • Why does the button become unchecked?
  • Is this a bug in WPF or am I just doing something incorrectly?
  • What is a good work around to resolve this issue? (if there is a good, clean work around)

网友答案:

Let me know if this works for you.

XAML

<Grid>
        <StackPanel x:Name="ToggleButtonContainer" Orientation="Horizontal" VerticalAlignment="Top">
            <RadioButton Style="{StaticResource {x:Type ToggleButton}}" Content="Toggle 1" Margin="2" Padding="4" GotFocus="ToggleButton_GotFocus" Checked="ToggleButton_Checked" Unchecked="ToggleButton_Unchecked" />
            <RadioButton Style="{StaticResource {x:Type ToggleButton}}" Content="Toggle 2" Margin="2" Padding="4" GotFocus="ToggleButton_GotFocus" Checked="ToggleButton_Checked" Unchecked="ToggleButton_Unchecked" />
            <RadioButton Style="{StaticResource {x:Type ToggleButton}}" Content="Toggle 3" Margin="2" Padding="4" GotFocus="ToggleButton_GotFocus" Checked="ToggleButton_Checked" Unchecked="ToggleButton_Unchecked" />
            <RadioButton Style="{StaticResource {x:Type ToggleButton}}" Content="Toggle 4" Margin="2" Padding="4" GotFocus="ToggleButton_GotFocus" Checked="ToggleButton_Checked" Unchecked="ToggleButton_Unchecked" />
            <RadioButton Style="{StaticResource {x:Type ToggleButton}}" Content="Toggle 5" Margin="2" Padding="4" GotFocus="ToggleButton_GotFocus" Checked="ToggleButton_Checked" Unchecked="ToggleButton_Unchecked" />
        </StackPanel>
    </Grid>

CODE:

 private void ToggleButton_Checked(object sender, RoutedEventArgs e)
        {
            System.Diagnostics.Debug.WriteLine("INFO:  ToggleButton_Checked called by {0}", sender);
        }

        private void ToggleButton_Unchecked(object sender, RoutedEventArgs e)
        {
            System.Diagnostics.Debug.WriteLine("INFO:  ToggleButton_Unchecked called by {0}", sender);
        }

        private void ToggleButton_GotFocus(object sender, RoutedEventArgs e)
        {
            System.Diagnostics.Debug.WriteLine("INFO:  ToggleButton_GotFocus called by {0}", sender);
            (sender as ToggleButton).IsChecked = true;
        }
网友答案:

Use the GroupName attribute to achieve mutual exclusion amongst your toggle controls.

<StackPanel>
    <RadioButton GroupName="Os" Content="Windows XP" IsChecked="True"/>
    <RadioButton GroupName="Os" Content="Windows Vista" />
    <RadioButton GroupName="Os" Content="Windows 7" />
    <RadioButton GroupName="Office" Content="Microsoft Office 2007" IsChecked="True"/>
    <RadioButton GroupName="Office" Content="Microsoft Office 2003"/>
    <RadioButton GroupName="Office" Content="Open Office"/>
</StackPanel>
网友答案:

You can do it by simply a style. To test purpose, I have set the Margin to 5 if toggle button is checked; and if gets focused then set the control as checked and Foreground to Red.

<Style TargetType="ToggleButton" >
    <Setter Property="IsChecked" Value="False" />
    <Style.Triggers>
       <Trigger Property="IsChecked" Value="True">
        <Setter Property="Margin" Value="5" />
      </Trigger>
      <Trigger Property="IsFocused" Value="True">
        <Setter Property="IsChecked" Value="True" />
        <Setter Property="Foreground" Value="Red" />
       </Trigger>
   </Style.Triggers>
   </Style>
网友答案:

Why does the button become unchecked?

The button becomes unchecked because the IsChecked property is changed in the GotFocus event, but then it is changed back when the OnClick method fires.

Is this a bug in WPF or am I just doing something incorrectly?

It appears that the current ToggleButton was designed with the intention that the only control for selecting/unselecting it lies with the mouse left button click or the spacebar.

What is a good work around to resolve this issue? (if there is a good, clean work around)

Thanks to this post and the conversation with Janne Matikainen, I was able to come up with the following solution:

public class MyToggleButton : ToggleButton
{
    protected override void OnGotKeyboardFocus(KeyboardFocusChangedEventArgs e)
    {
        if (e.KeyboardDevice.IsKeyDown(Key.Tab))
        {
            IsChecked = true;
            base.OnGotKeyboardFocus(e);
        }
    }

    protected override void OnLostFocus(System.Windows.RoutedEventArgs e)
    {
        IsChecked = false;
        base.OnLostFocus(e);
    }
}

Replacing all of the instances of <ToggleButton> with <uc:MyToggleButton> produces the behavior as specified in the Requirements section of the question text.

分享给朋友:
您可能感兴趣的文章:
随机阅读: