【问题标题】:How do I prevent WPF buttons from remaining highlighted after being clicked?如何防止 WPF 按钮在单击后保持突出显示?
【发布时间】:2011-08-22 11:03:04
【问题描述】:

单击标准 WPF 按钮时,它会以蓝色突出显示(可能使用设置的任何 Windows 主题中的蓝色),并且在您与任何其他控件交互之前一直突出显示。对于我的应用程序,它会让用户感到困惑。

有没有一种简单的方法可以关闭它并使按钮恢复正常样式? 我正在使用 .NET 4。

【问题讨论】:

  • 顺便说一句,“切换”按钮的触摸屏上的问题很严重。考虑一个打开的按钮,突出显示颜色也是如此。用户现在触摸它以将其关闭:它不再突出显示但它具有焦点,所以它仍然看起来像它是突出显示颜色(来自焦点)。很混乱。 (并证明使用焦点颜色类似于高亮颜色的样式是一个根本错误。最好使用仅用轮廓指示焦点的传统解决方案。)

标签: c# .net wpf button focus


【解决方案1】:

发生的情况是按钮在被点击后接受输入焦点,就像您点击它时任何其他控件一样。

Windows 指示控件具有输入焦点(至少在 Aero 主题下)的方式是带有微妙的蓝色突出显示。

特别是对于按钮控件,当它具有输入焦点时,只需按 Enter 键即可“按下”该按钮。这就是为什么保持高亮非常重要,这样用户才能知道会发生什么。

更好的解决方案是在用户单击按钮后立即将焦点设置到窗口中的不同控件。这样,当用户按下 Enter 键时,它将不再自动突出显示并且不会自动触发任何操作。 (这是您尝试解决的真正可用性问题,即使您还不知道。没有什么比在用户实际尝试键入时无意中点击按钮更令人困惑的了东西。)

可以通过将其Focusable property 设置为false 来阻止按钮完全获得焦点,但我强烈建议不要这样做。完成此操作后,用户将无法仅使用键盘“按下”按钮。设计良好的应用程序应该始终可供不喜欢或无法使用鼠标的用户访问。

【讨论】:

  • 感谢您的详细解释。我认为将焦点设置为隐藏控制或其他方式将是可行的方法。可能是另一个高度和宽度为零的按钮,因为我不希望这个按钮占用任何空间。 Visibility=none 不应该工作,因为焦点不能设置为未绘制的东西。还有一种方法我不必为单击事件中的每个按钮设置焦点吗?我的应用程序中有太多按钮。而且其中许多只有命令绑定..没有事件.. MVVM!
  • @learner:或者用户最有可能与之交互的控件。我不确定您的窗口是如何布局的,但除非它包含的唯一内容是一个按钮,否则对于控件来说,拥有焦点而不是隐藏在屏幕外的控件可能是一种更自然的选择。
  • @learner:嗯,如果你真的很想这样做,你应该认真重新考虑按钮是否适合你的应用程序。通常,默认行为是您想要的(这也是用户最有可能期望的,因为他们所有的其他应用程序都以相同的方式工作)。仅仅为了与众不同而与众不同并不是一个好主意,即使在当时看起来它会“更好”。如果按钮的行为不应该像其他按钮,那么也许它们根本不应该是按钮!
  • 实际上是正确的。我查看了 VS2010,它也是如此。在按钮上完成的操作没有突出显示。但是,是的,似乎没有办法使用键盘选项卡来改变焦点。
  • 值得注意的是,对于只有触摸屏访问(无键盘或鼠标)的 POS 系统,这里的最佳解决方案是Focusable = false。对我来说真正的问题是,当扫描 RFID 令牌时,它会(实际上)输入一个 10 位数字,然后按 Enter;导致按钮被随机点击。
【解决方案2】:

尝试将Focusable 设置为false。该按钮将是可点击的,但不会保持焦点。

【讨论】:

  • 这会干扰用户仅使用键盘与应用程序交互的能力。设计不佳的应用程序的标志是不使用鼠标就无法访问。
  • @CodyGray 除非它显示在 - 例如 - 商店的触摸屏上。在这种情况下,绝对不需要焦点,键盘或鼠标输入根本不重要。
  • @CodyGray 或者每个操作都有明确指定的键盘快捷键 - 如果空格键是其中之一,则更多。
【解决方案3】:

这只是专注的状态。要关闭它,您必须更改聚焦状态。使用 Blend 是最简单的。

我不建议将 Focusable 设置为 false,因为它会干扰使用键盘

【讨论】:

    【解决方案4】:

    这是 Aero 按钮获得焦点时的默认外观。您可以设置Focusable="False" 或使用自定义样式,当按钮具有焦点时不会以不同方式呈现它。比如:

    xmlns:theme="clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Aero"
    
    <Style x:Key="BaseButtonStyle" TargetType="{x:Type ButtonBase}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type ButtonBase}">
                    <theme:ButtonChrome Name="Chrome" Background="{TemplateBinding Background}"
                            BorderBrush="{TemplateBinding BorderBrush}" RenderDefaulted="{TemplateBinding Button.IsDefaulted}"
                            RenderMouseOver="{TemplateBinding IsMouseOver}" RenderPressed="{TemplateBinding IsPressed}"
                            SnapsToDevicePixels="true">
                        <ContentPresenter Margin="{TemplateBinding Padding}"
                                VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
                                HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" RecognizesAccessKey="True"
                                SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
                    </theme:ButtonChrome>
                    <ControlTemplate.Triggers>
                        <!--
                        Do not show blue when focused
                        <Trigger Property="IsKeyboardFocused" Value="true">
                            <Setter TargetName="Chrome" Property="RenderDefaulted" Value="true" />
                        </Trigger>-->
                        <Trigger Property="ToggleButton.IsChecked" Value="true">
                            <Setter TargetName="Chrome" Property="RenderPressed" Value="true" />
                        </Trigger>
                        <Trigger Property="IsEnabled" Value="false">
                            <Setter Property="Foreground" Value="#ADADAD" />
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
    
    <Style x:Key="{x:Type ToggleButton}" BasedOn="{StaticResource BaseButtonStyle}" TargetType="{x:Type ToggleButton}" />
    <Style x:Key="{x:Type RepeatButton}" BasedOn="{StaticResource BaseButtonStyle}" TargetType="{x:Type RepeatButton}" />
    <Style x:Key="{x:Type Button}" BasedOn="{StaticResource BaseButtonStyle}" TargetType="{x:Type Button}" />
    

    您需要添加对 PresentationFramework.Aero.dll 的引用

    【讨论】:

    • 您应该知道,更改样式将更改按钮在获得焦点时的绘制方式。它不会改变按钮 具有 焦点的事实。这很重要,因为焦点按钮是“特殊的”。它将任何按 Enter 键解释为“单击”按钮,如果用户无法一眼看出哪个控件具有焦点,则会导致潜在的意外操作。因此不建议仅仅改变样式。
    • 谢谢伙计。样式解决方案可能非常有用,但我担心我的用户会抱怨“Cody Gray”提到的问题
    • 更好的是更改样式以便可以看到焦点,但看起来与突出显示的外观不同。例如,“轮廓”边框,但非高亮颜色。这解决了科迪提出的观点。
    【解决方案5】:
    <Style x:Key="TouchButton" TargetType="{x:Type Button}">
            <Setter Property="IsTabStop" Value="false"/>
            <Setter Property="Focusable" Value="false"/>
            <Setter Property="ClickMode" Value="Press"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type Button}">
                        <Border x:Name="Border" CornerRadius="2" BorderThickness="1" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}">
                            <ContentPresenter Margin="2" HorizontalAlignment="Center" VerticalAlignment="Center" RecognizesAccessKey="True"/>
                        </Border>
                        <ControlTemplate.Triggers>
                            <Trigger Property="Button.IsMouseOver" Value="True">
                                <Setter TargetName="Border" Property="Background" Value="Red"></Setter>
                            </Trigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    

    【讨论】:

    • 请更详细地解释您的答案,它可以帮助其他人理解您建议的解决方案是什么。
    【解决方案6】:

    我需要做类似的事情,但在运行时的代码中,它看起来像这样

    //You can get this XAML by using System.Windows.Markup.XamlWriter.Save(yourButton.Template)";
                 const string controlXaml = "<ControlTemplate TargetType=\"ButtonBase\" " +
                                        "xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\" " +
                                        "xmlns:s=\"clr-namespace:System;assembly=mscorlib\" " +
                                        "xmlns:mwt=\"clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Aero\">" +
                                        "<mwt:ButtonChrome Background=\"{TemplateBinding Panel.Background}\" " +
                                        "BorderBrush=\"{TemplateBinding Border.BorderBrush}\" " +
                                        "RenderDefaulted=\"{TemplateBinding Button.IsDefaulted}\" " +
                                        //"RenderMouseOver=\"{TemplateBinding UIElement.IsMouseOver}\" " +
                                        "RenderPressed=\"{TemplateBinding ButtonBase.IsPressed}\" Name=\"Chrome\" SnapsToDevicePixels=\"True\">" +
                                        "<ContentPresenter RecognizesAccessKey=\"True\" " +
                                        "Content=\"{TemplateBinding ContentControl.Content}\" " +
                                        "ContentTemplate=\"{TemplateBinding ContentControl.ContentTemplate}\" " +
                                        "ContentStringFormat=\"{TemplateBinding ContentControl.ContentStringFormat}\" " +
                                        "Margin=\"{TemplateBinding Control.Padding}\" " +
                                        "HorizontalAlignment=\"{TemplateBinding Control.HorizontalContentAlignment}\" " +
                                        "VerticalAlignment=\"{TemplateBinding Control.VerticalContentAlignment}\" " +
                                        "SnapsToDevicePixels=\"{TemplateBinding UIElement.SnapsToDevicePixels}\" /></mwt:ButtonChrome>" +
                                        "<ControlTemplate.Triggers>" +
                                        "<Trigger Property=\"UIElement.IsKeyboardFocused\">" +
                                        "<Setter Property=\"mwt:ButtonChrome.RenderDefaulted\" TargetName=\"Chrome\"><Setter.Value><s:Boolean>True</s:Boolean></Setter.Value></Setter>" +
                                        "<Trigger.Value><s:Boolean>True</s:Boolean></Trigger.Value></Trigger>" +
                                        "<Trigger Property=\"ToggleButton.IsChecked\">" +
                                        "<Setter Property=\"mwt:ButtonChrome.RenderPressed\" TargetName=\"Chrome\"><Setter.Value><s:Boolean>True</s:Boolean></Setter.Value></Setter>" +
                                        "<Trigger.Value><s:Boolean>True</s:Boolean></Trigger.Value></Trigger>" +
                                        "<Trigger Property=\"UIElement.IsEnabled\"><Setter Property=\"TextElement.Foreground\"><Setter.Value><SolidColorBrush>#FFADADAD</SolidColorBrush></Setter.Value></Setter>" +
                                        "<Trigger.Value><s:Boolean>False</s:Boolean></Trigger.Value></Trigger></ControlTemplate.Triggers>" +
                                        "</ControlTemplate>";
    
            var xamlStream = new MemoryStream(System.Text.Encoding.Default.GetBytes(controlXaml));
            var _buttonControlTemplate = (ControlTemplate)System.Windows.Markup.XamlReader.Load(xamlStream);
            var yourButton = new Button() { Template = _buttonControlTemplate };
    

    你可以看到我评论了“RenderMouseOver”这一行

    我的第一个希望是使用 FrameworkElementFactory,但我需要创建所有默认模板....全部手动! ;)
    使用

    System.Windows.Markup.XamlWriter.Save(myButton.Template)
    

    它给了我我想要的模板,然后删除渲染部分很容易。

    【讨论】:

      【解决方案7】:

      我找到了两步解决方案。该解决方案有点有趣但有效。 我的参考帖子是;

      How to remove global FocusVisualStyle to all the Controls?

      C# WPF application .NET 4.5 Set Mouse Position

      必须导入一个DLL,因为WPF不直接支持鼠标移动。

      1. 将 System.Runtime.InteropServices 添加到命名空间
      2. 在 MainWindow 或任何您的窗口代码中添加两行

      [DllImport("User32.dll")] private static extern bool SetCursorPos(int X, int Y);

      1. 将这两行添加到您的点击事件中。 SetCursorPos(0, 0); ButtonName.FocusVisualStyle = null;

      它对我有用。

      【讨论】:

        【解决方案8】:

        在我的情况下,我通过使用 MultiTrigger 解决了这种情况,并设置如下状态:

        <MultiTrigger.Conditions>
           <Condition Property="IsFocused" Value="True"/>
           <Condition Property="IsPressed" Value="False"/>
        </MultiTrigger.Conditions>
        

        要了解更多信息,请参阅link

        【讨论】:

          猜你喜欢
          • 2017-07-17
          • 2021-11-28
          • 2015-02-24
          • 1970-01-01
          • 1970-01-01
          • 2013-07-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多