【问题标题】:WPF - Remove focus when clicking outside of a textboxWPF - 单击文本框外部时移除焦点
【发布时间】:2011-06-27 05:46:15
【问题描述】:

我有一些文本框,我希望它们的焦点与 WPF 应用程序的正常行为略有不同。基本上,我希望它们的行为更像是网页上的文本框。也就是说,如果我单击文本框外的任何位置,它将失去焦点。最好的方法是什么?

如果答案是以编程方式移除焦点,那么检测边界外的鼠标点击的最佳方法是什么?如果我单击的元素将成为焦点的新接收者怎么办?

【问题讨论】:

  • 如果您单击不同的 UI 元素,文本框应该会自动失去焦点。
  • @Merlyn Morgan-Graham:不,它没有。
  • @Charlie:我的评论是为了回应“如果我单击的元素将成为焦点的新接收者怎么办?”我同意,如果用户点击框外的任何位置(例如窗口),它不会引发 LostFocus 事件,但会引发另一个可聚焦元素(例如另一个文本框)。
  • @Merlyn Morgan-Graham:同意,很明显,如果您单击另一个文本框,前一个文本框将失去焦点,但这里不是这种情况,OP 希望通过单击任何地方来失去焦点侧文本框。

标签: c# wpf focus click


【解决方案1】:

我认为你应该给你的 Grid 一个名称,而不是向窗口添加新控件,并对窗口上的 MouseDown 事件做出反应,将焦点移到 Grid strong>网格本身。像这样的:

<Window x:Class="WpfApplication1.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    Title="Window1" 
    Height="412" Width="569" 
    MouseDown="Window_MouseDown" 
    Name="window1">

    <Grid ShowGridLines="False" 
          Background="#01FFFFFF"
          KeyDown="Grid_KeyDown" 
          Name="grid1" 
          Focusable="True">

          <TextBox Width="120" Margin="117,61,0,0" 
                   Name="textBox1" 
                   VerticalAlignment="Top" 
                   HorizontalAlignment="Left"/>
    </Grid>
</Window>

后面的代码:

private void window1_MouseDown(object sender, MouseButtonEventArgs e)
{
    grid1.Focus();
}

【讨论】:

  • 啊,这行得通。要扩展解决方案,也无需命名窗口 - 只需覆盖 OnMouseDown 以获得更清晰的 XAML。
  • @JacobJ:哎呀..我想写的是在你的“网格”而不是窗口中添加名称。已编辑。谢谢指出!
  • 我也试过了。不知道为什么,但这对我不起作用。 LostFocus 没有被我窗口中的文本框调用。
  • @ Merlyn Morgan-Graham:简而言之,您需要做的是,将名称属性添加到网格,使网格可聚焦,在窗口的鼠标按下事件时:将焦点设置到该网格。这是你尝试过的吗?
  • @Charlie:“使网格可聚焦”是我错过的部分。谢谢! +1
【解决方案2】:

我认为,解决此问题的更好方法是将 MouseDown 事件处理程序添加到带有代码的窗口:

private void window_MouseDown(object sender, MouseButtonEventArgs e)
{
    Keyboard.ClearFocus();
}

【讨论】:

  • 我对此了解不多,但是按下文本字段本身时,这不会也清除焦点吗?
  • 否,因为如果你点击文本框,点击事件由文本框处理,不会路由到窗口。因此,当您单击文本框时,永远不会调用 Windows.MouseDown。
  • 这样可以防止在文本框上调用LostFocus 事件。
  • 我知道我来晚了,但这(大约)是我为同样的问题所采用的解决方案,我只是想回复@skiwi 提出的问题。我可以通过在清除焦点之前抓住 Keyboard.FocusedElement 然后执行 element.RaiseEvent(new RoutedEventArgs(UIElement.LostFocusEvent)) 来解决这个问题——我必须这样做,因为我的 TextBox 的边框取决于焦点状态。
【解决方案3】:

另一种对我有用的方法是使用

Mouse.AddPreviewMouseDownOutsideCapturedElementHandler

例如,假设您有一个 TextBlock,当您单击该 TextBlock 时,它应该可以通过显示一个聚焦的 TextBox 来进行编辑。然后,当用户在 TextBox 外部单击时,它应该再次被隐藏。你可以这样做:

private void YourTextBlock_OnMouseDown(object sender, MouseButtonEventArgs e)
{
    YourTextBox.Visibility = Visibility.Visible;
    YourTextBox.Focus();
    CaptureMouse();
    Mouse.AddPreviewMouseDownOutsideCapturedElementHandler(this, OnMouseDownOutsideElement);
}

private void OnMouseDownOutsideElement(object sender, MouseButtonEventArgs e)
{
    Mouse.RemovePreviewMouseDownOutsideCapturedElementHandler(this, OnMouseDownOutsideElement);
    ReleaseMouseCapture();
    YourTextBox.Visibility = Visibility.Hidden;
}

【讨论】:

  • 除非在YourTextBlock_OnMouseDown 中有MessageBox.Show,否则无法正常工作。 CaptureMouse 返回 true 并且不会调用 OnMouseDownOutsideElement
【解决方案4】:

为了避免代码落后,您可以使用此行为 行为

 public class ClearFocusOnClickBehavior : Behavior<FrameworkElement>
 {
    protected override void OnAttached()
    {
        AssociatedObject.MouseDown += AssociatedObject_MouseDown;
        base.OnAttached();
    }

    private static void AssociatedObject_MouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
    {
        Keyboard.ClearFocus();
    }

    protected override void OnDetaching()
    {
        AssociatedObject.MouseDown -= AssociatedObject_MouseDown;
    }
}

在 XAML 中使用:

在文本框外的任何元素上你希望他清除焦点的点击添加:

    <i:Interaction.Behaviors>
        <behaviors:ClearFocusOnClickBehavior/>
    </i:Interaction.Behaviors>

【讨论】:

    【解决方案5】:

    我已经在反应原生 WPF 应用程序中尝试了选择的答案,但它没有触发文本框失去焦点,因为哪个文本框没有失去焦点。以下解决方案对我有用。

    将鼠标按下事件绑定到窗口:

    <Window x:Class="WpfApplication1.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
        Title="Window1" Height="412" Width="569" MouseDown="window_MouseDown" Name="window1" >
        <Grid ShowGridLines="False" KeyDown="Grid_KeyDown" Name="grid1" Focusable="True">
              <TextBox HorizontalAlignment="Left" Margin="117,61,0,0" Name="textBox1" VerticalAlignment="Top" Width="120" />
        </Grid>
    </Window>
    

    事件是:

    private void window_MouseDown(object sender, MouseButtonEventArgs e)
    {
        TextBox textBox = Keyboard.FocusedElement as TextBox;
        if (textBox != null)
        {
            TraversalRequest tRequest = new TraversalRequest(FocusNavigationDirection.Next);
            textBox.MoveFocus(tRequest);
        }
    }
    

    【讨论】:

    • 这实际上效果很好,只是为我省去了很多挫败感。谢谢!
    【解决方案6】:

    我不是 100% 确定,但是如果您在容器元素(Grid、StackPanel 等)上将 Focusable 设置为 true,那么它应该将焦点从文本框移开。

    【讨论】:

    • 我在问这个问题之前试过这个,但它并没有完全做到。除了在容器元素上设置 Focusable 之外,我还需要根据 Charlie Sharma 的解决方案手动将焦点设置在 mousedown 上。
    【解决方案7】:

    值得一提的是,文本框会同时捕获鼠标和键盘事件,因此要从文本框中移动或移除焦点,您必须释放这两个元素。

    让我们从键盘开始,Keyboard.ClearFocus(); 将从文本框中移除键盘焦点,它非常适合隐藏闪烁的光标,但不会将焦点从文本框中移动,换句话说,文本框将保持焦点元素,但不显示光标。 您可以通过在Keyboard.ClearFocus(); 之后打印FocusManager.GetFocusedElement(this); 来检查。

    所以,如果你有一个LostFocus 事件附加到文本框或类似事件,它不会被触发,因为元素还没有失去焦点。

    PreviewMouseDown += (s, e) => FocusManager.SetFocusedElement(this, null);
    PreviewMouseDown += (s, e) => Keyboard.ClearFocus();
    

    作为一种解决方案,您应该通过调用以下方法将聚焦于 Window 的元素设置为 null: System.Windows.Input.FocusManager.SetFocusedElement(this, null); 现在,如果您想知道我们是否可以摆脱 Keyboard.ClearFocus();,那么答案是否定的,因为我们要移除鼠标焦点,但键盘焦点仍然存在。 (您会注意到闪烁的光标仍然存在,您将能够在文本框中输入一些文本)。

    【讨论】:

    • 虽然此代码 sn-p 可能是解决方案,但 including an explanation 有助于提高您的帖子质量。请记住,您是在为将来的读者回答问题,而这些人可能不知道您提出代码建议的原因。
    【解决方案8】:

    如果你点击了可以抓住焦点的元素,你就会得到你需要的东西。例如,如果您有一些面板,您可以处理面板的 mouseClick 事件来满足您的需求,或者使用 Richard Szalay 的建议。

    【讨论】:

      【解决方案9】:

      我遇到了类似的问题,但是当我将 ScrollViewer 控件包裹在我的 textboxes 周围时,当单击 texboxes 之外的任何位置时,所有文本框都会自动失去焦点。

      【讨论】:

      • 我认为你的意思是在你的文本框周围包裹一个滚动查看器,而不是相反。建议编辑。在我的文本框周围包裹一个滚动查看器解决了这个问题
      【解决方案10】:

      你不能明确地失去控制的焦点

      您可以将焦点设置为其他控件

      **txt.Focusable=true;
      label.focus();
      Keyboard.Focus(txtPassword);**
      

      试试这个

      【讨论】:

        【解决方案11】:

        您可以使用IsKeyboardFocusedChanged 事件:

        myTextBox.IsKeyboardFocusedChanged += myTextBox_IsKeyboardFocusedChanged;
        
        private void SendFileCaptionTextBox_IsKeyboardFocusedChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            if (e.NewValue.ToString() == "True")
            {
                // it's focused
            }
            else
            {
                // it's not focused
            }
        }    
        

        【讨论】:

        • 你应该将 e.NewValue 转换为 bool
        【解决方案12】:
        public class ClearFocusOnOutsideClickBehavior : Behavior<FrameworkElement>
        {
            protected override void OnAttached()
            {
        
                AssociatedObject.GotFocus += AssociatedObjectOnGotFocus;
                AssociatedObject.LostFocus += AssociatedObjectOnLostFocus;
                base.OnAttached();
            }
        
            private void AssociatedObjectOnLostFocus(object sender, RoutedEventArgs e)
            {
                App.Current.MainWindow.MouseUp -= _paren_PreviewMouseUp;
            }
        
            private void AssociatedObjectOnGotFocus(object sender, RoutedEventArgs e)
            {
                App.Current.MainWindow.MouseUp += _paren_PreviewMouseUp;
            }
        
            private void _paren_PreviewMouseUp(object sender, MouseButtonEventArgs e)
            {
                Keyboard.ClearFocus();
            }
        
            protected override void OnDetaching()
            {
                AssociatedObject.GotFocus -= AssociatedObjectOnGotFocus;
                AssociatedObject.LostFocus -= AssociatedObjectOnLostFocus;
            }
        }
        

        在 XAML 中使用:

        <TextBox Height="30" Width="200">
                    <i:Interaction.Behaviors>
                        <behaviours:ClearFocusOnOutsideClickBehavior/>
                    </i:Interaction.Behaviors>
         </TextBox>
        

        【讨论】:

          【解决方案13】:

          您可以在应用程序启动时将PreviewMouseLeftButtonDownEvent 注册到 App.xaml.cs 中的每种元素。

          EventManager.RegisterClassHandler(typeof(UIElement), UIElement.PreviewMouseLeftButtonDownEvent,
                          new MouseButtonEventHandler(FocusElement));
          
          private void FocusElement(object sender, MouseButtonEventArgs e)
          {
               var parent = e.OriginalSource as DependencyObject;
               if (parent is UIElement element && !element.Focusable)
               {
                   element.Focusable = true;
                   element.Focus();
                   element.Focusable = false;
               }
          }
          

          【讨论】:

            【解决方案14】:

            这可以用少量的代码来完成。只需设置窗口的Focusable="True" 放置文本框或其他控件的位置,并将其添加到它的MouseDown 事件中:

            private void Window_MouseDown(object sender, MouseButtonEventArgs e)
            {
                 FocusManager.SetFocusedElement(this, this);
            }
            

            【讨论】:

              猜你喜欢
              • 2018-11-26
              • 1970-01-01
              • 1970-01-01
              • 2012-07-05
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2020-06-01
              相关资源
              最近更新 更多