如何解决在 GotFocus 事件中为 ToggleButton 设置 IsChecked 的 IsChecked 导致

本文关键字:IsChecked ToggleButton 设置 事件 导致 GotFocus 何解决 解决 | 更新日期: 2023-09-27 18:24:13

要求

当用户按 Tab 键转到其中一个切换按钮时,我希望选中它并取消选中任何其他切换按钮。 如果用户单击切换按钮,其状态应从选中更改为未选中或未选中更改为选中。

问题

ToggleButtonGotFocus 事件中设置 IsChecked 属性会导致调用Checked事件,然后神秘地取消选中ToggleButton并导致调用Unchecked事件。

法典

下面是我的实验室项目中演示这一点的示例代码。

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>

法典:

    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;
    }

环境信息

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

观察

我观察到,如果我在释放鼠标左键之前单击按钮并拖离它,那么我就不会遇到这个问题。 当您调试并单步执行 Checked 事件时,也会发生这种情况(因为同样,自调试器获取焦点以来,ToggleButton永远不会收到 MouseLeftButtonUp 事件(。

问题

  • 为什么按钮变为未选中状态?
  • 这是 WPF 中的错误还是我只是做错了什么?
  • 解决此问题
  • 的好方法是什么? (如果有良好、干净的解决方法(

如何解决在 GotFocus 事件中为 ToggleButton 设置 IsChecked 的 IsChecked 导致

让我知道这是否适合您。

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>

法典:

 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;
        }

为什么按钮变为未选中状态?

该按钮变为未选中状态,因为 IsChecked 属性在 GotFocus 事件中已更改,但在触发 OnClick 方法时又更改回来。

这是 WPF 中的错误还是我只是做错了什么?

似乎当前的 ToggleButton 的设计目的是选择/取消选择它的唯一控件位于鼠标左键单击或空格键上。

解决此问题

的好方法是什么?(如果有良好、干净的解决方法(

感谢这篇文章和与Janne Matikainen的对话,我能够提出以下解决方案:

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);
    }
}

的所有实例替换为 <uc:MyToggleButton> 将生成问题文本的"要求"部分中指定的行为。

使用 GroupName 属性可实现切换控件之间的互斥。

<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>

你可以通过简单的风格来做到这一点。为了测试目的,我将Margin设置为5切换按钮是否checked;如果得到focused则将控件设置为 checked 并将Foreground设置为 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>