如何解决在GotFocus事件中为ToggleButton设置IsChecked导致调用Checked和Unchecked事件的问题?

时间:2015-11-24 17:46:26

标签: c# wpf togglebutton

要求

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

问题

IsChecked事件的GotFocus事件中设置ToggleButton属性会导致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>

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

环境信息

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

观察

我观察到,如果我在释放鼠标左键之前单击按钮并将其拖离,则我不会遇到此问题。调试并逐步执行Checked事件时也会发生这种情况(因为调试器已经开始关注,ToggleButton永远不会收到MouseLeftButtonUp事件。

问题

  • 为什么按钮会被取消选中?
  • 这是WPF中的错误还是我做错了什么?
  • 解决此问题的好处是什么? (如果有一个好的,干净的工作)

4 个答案:

答案 0 :(得分:1)

请告诉我这是否适合您。

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

答案 1 :(得分:0)

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

答案 2 :(得分:0)

你可以通过一种风格来做到这一点。为了测试目的,如果切换按钮为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>

{{1}}

答案 3 :(得分:0)

  

为什么按钮未选中?

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

  

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

看来当前的ToggleButton的设计意图是选择/取消选择它的唯一控件位于鼠标左键单击或空格键。

  

解决此问题的好处是什么? (如果有一个好的,干净的工作)

感谢this post以及与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);
    }
}

替换&lt; ToggleButton&gt;的所有实例与&lt; uc:MyToggleButton&gt;产生问题文本的“要求”部分中指定的行为。