C# WPF文本框移动光标并更改焦点

C# WPF文本框移动光标并更改焦点,c#,.net,wpf,mvvm,textbox,C#,.net,Wpf,Mvvm,Textbox,这是一个奇怪的问题,我甚至不知道该搜索什么,但相信我,我已经找到了 我有一个文本框,绑定到它的OnTextChanged事件是下面的方法 这里的目的是为文本框提供焦点,将光标移动到文本框的末尾,并将焦点返回到实际聚焦的位置(通常是一个按钮)。问题是,在我将焦点发送回最初聚焦的元素之前,文本框似乎没有“重新绘制”(因为没有更好的词?),因此光标位置不会在屏幕上更新(尽管所有属性都认为它有) 目前,我已经粗暴地将其整合在一起,基本上将之前关注的项目的重新聚焦延迟了10毫秒,并在不同的线程中运行它,以

这是一个奇怪的问题,我甚至不知道该搜索什么,但相信我,我已经找到了

我有一个文本框,绑定到它的
OnTextChanged
事件是下面的方法

这里的目的是为文本框提供焦点,将光标移动到文本框的末尾,并将焦点返回到实际聚焦的位置(通常是一个按钮)。问题是,在我将焦点发送回最初聚焦的元素之前,文本框似乎没有“重新绘制”(因为没有更好的词?),因此光标位置不会在屏幕上更新(尽管所有属性都认为它有)

目前,我已经粗暴地将其整合在一起,基本上将之前关注的项目的重新聚焦延迟了10毫秒,并在不同的线程中运行它,以便UI有时间进行更新。现在,这显然是一个任意的时间量,在我的机器上可以正常工作,但在旧机器上运行此应用程序的人可能会有问题

有没有合适的方法?我想不出来

private void TextBoxBase_OnTextChanged(object sender, TextChangedEventArgs e)
{
    if (sender == null) return;
    var box = sender as TextBox;

    if (!box.IsFocused)
    {

        var oldFocus = FocusManager.GetFocusedElement(FocusManager.GetFocusScope(this));
        box.Select(box.Text.Length, 0);
        Keyboard.Focus(box); // or box.Focus(); both have the same results

        var thread = new Thread(new ThreadStart(delegate
                                                    {
                                                        Thread.Sleep(10);
                                                        Dispatcher.Invoke(new Action(() => oldFocus.Focus()));
                                                    }));
        thread.Start();
    }
}
编辑

我的一个新想法是在UI完成更新后运行oldFocus.Focus()方法,因此我尝试了以下方法,但得到了相同的结果:(


您的做法是正确的,问题在于,对于要坚持的
.Focus()
调用,您需要将调用延迟到Dispatcher中的稍后时间。
请尝试使用DispatcherPriority在以后的DispatcherPriority设置焦点,而不是使用Send的
DispatcherPriority
值(最高值),例如Input

Dispatcher.BeginInvoke(DispatcherPriority.Input,
new Action(delegate() { 
    oldFocus.Focus();         // Set Logical Focus
    Keyboard.Focus(oldFocus); // Set Keyboard Focus
 }));
如您所见,我还在设置键盘焦点。

WPF可以有多个焦点作用域,多个元素可以有逻辑焦点(
IsFocused=true
).但是,只有一个元素可以具有键盘焦点并将接收键盘输入。

经过许多天,我终于能够让它工作。它需要调度程序检查文本框是否同时具有焦点和键盘焦点以及大量循环

这是供参考的代码。其中有一些注释,但如果有人点击此页面寻找答案,您必须自己阅读。提醒,这是关于文本更改的

protected void TextBox_ShowEndOfLine(object sender, TextChangedEventArgs e)
    {
        if (sender == null) return;
        var box = sender as TextBox;

        if (!box.IsFocused && box.IsVisible)
        {
            IInputElement oldFocus = FocusManager.GetFocusedElement(FocusManager.GetFocusScope(this));
            box.Focus();
            box.Select(box.Text.Length, 0);
            box.Focus();

            // We wait for keyboard focus and regular focus before returning focus to the button
            var thread = new Thread((ThreadStart)delegate
                                        {
                                            // wait till focused
                                            while (true)
                                            {
                                                var focused = (bool)Dispatcher.Invoke(new Func<bool>(() => box.IsKeyboardFocusWithin && box.IsFocused && box.IsInputMethodEnabled), DispatcherPriority.Send);
                                                if (!focused)
                                                    Thread.Sleep(1);
                                                else
                                                    break;
                                            }

                                            // Focus the old element
                                            Dispatcher.Invoke(new Action(() => oldFocus.Focus()), DispatcherPriority.SystemIdle);
                                        });
            thread.Start();
        }
        else if (!box.IsVisible)
        {
            // If the textbox is not visible, the cursor will not be moved to the end. Wait till it's visible.
            var thread = new Thread((ThreadStart)delegate
                                        {
                                            while (true)
                                            {
                                                Thread.Sleep(10);
                                                if (box.IsVisible)
                                                {
                                                    Dispatcher.Invoke(new Action(delegate
                                                                                     {
                                                                                         box.Focus();
                                                                                         box.Select(box.Text.Length, 0);
                                                                                         box.Focus();

                                                                                     }), DispatcherPriority.ApplicationIdle);
                                                    return;
                                                }
                                            }
                                        });
            thread.Start();
        }
    }
protectedvoid文本框\u ShowEndOfLine(对象发送者,textchangedventargs e)
{
if(sender==null)返回;
var-box=发送方作为文本框;
如果(!box.IsFocused&&box.IsVisible)
{
IIInputElement oldFocus=FocusManager.GetFocusedElement(FocusManager.GetFocusScope(this));
box.Focus();
box.选择(box.Text.Length,0);
box.Focus();
//我们等待键盘焦点和常规焦点,然后再将焦点返回到按钮
var thread=新线程((ThreadStart)委托
{
//留心
while(true)
{
var focused=(bool)Dispatcher.Invoke(新函数(()=>box.IsKeyboardFocusWithin&&box.IsFocused&&box.IsInputMethodEnabled),DispatcherPriority.Send);
如果(!聚焦)
睡眠(1);
其他的
打破
}
//关注旧元素
Invoke(新操作(()=>oldFocus.Focus()),DispatcherPriority.SystemIdle);
});
thread.Start();
}
如果(!box.IsVisible),则为else
{
//如果文本框不可见,光标将不会移动到末尾。请等待它可见。
var thread=新线程((ThreadStart)委托
{
while(true)
{
睡眠(10);
if(box.IsVisible)
{
Dispatcher.Invoke(新操作(委托
{
box.Focus();
box.选择(box.Text.Length,0);
box.Focus();
}),DispatcherPriority.ApplicationIdle);
返回;
}
}
});
thread.Start();
}
}
最后,我找到了这个问题的“正确”解决方案(底部的完整解决方案):

实际上,你不想聚焦文本框-这是必需的,因为textbox.CaretIndex、textbox.Select()等在文本框没有聚焦的情况下不会做任何事情。使用其中一种滚动方法可以在没有聚焦的情况下工作。我不知道双偏移量到底应该是多少(使用过多的
1000.0
值对我来说很有效)。该值的行为类似于像素,因此请确保它足够大,适合您的场景

接下来,当用户使用键盘输入编辑值时,您不希望触发此行为。作为奖励,我结合了垂直和水平滚动,其中多行文本框垂直滚动,而单行文本框水平滚动。Fina
protected void TextBox_ShowEndOfLine(object sender, TextChangedEventArgs e)
    {
        if (sender == null) return;
        var box = sender as TextBox;

        if (!box.IsFocused && box.IsVisible)
        {
            IInputElement oldFocus = FocusManager.GetFocusedElement(FocusManager.GetFocusScope(this));
            box.Focus();
            box.Select(box.Text.Length, 0);
            box.Focus();

            // We wait for keyboard focus and regular focus before returning focus to the button
            var thread = new Thread((ThreadStart)delegate
                                        {
                                            // wait till focused
                                            while (true)
                                            {
                                                var focused = (bool)Dispatcher.Invoke(new Func<bool>(() => box.IsKeyboardFocusWithin && box.IsFocused && box.IsInputMethodEnabled), DispatcherPriority.Send);
                                                if (!focused)
                                                    Thread.Sleep(1);
                                                else
                                                    break;
                                            }

                                            // Focus the old element
                                            Dispatcher.Invoke(new Action(() => oldFocus.Focus()), DispatcherPriority.SystemIdle);
                                        });
            thread.Start();
        }
        else if (!box.IsVisible)
        {
            // If the textbox is not visible, the cursor will not be moved to the end. Wait till it's visible.
            var thread = new Thread((ThreadStart)delegate
                                        {
                                            while (true)
                                            {
                                                Thread.Sleep(10);
                                                if (box.IsVisible)
                                                {
                                                    Dispatcher.Invoke(new Action(delegate
                                                                                     {
                                                                                         box.Focus();
                                                                                         box.Select(box.Text.Length, 0);
                                                                                         box.Focus();

                                                                                     }), DispatcherPriority.ApplicationIdle);
                                                    return;
                                                }
                                            }
                                        });
            thread.Start();
        }
    }
if (!tb.IsFocused)
{
    tb.Dispatcher.BeginInvoke(new Action(() => 
        tb.ScrollToHorizontalOffset(1000.0)), DispatcherPriority.Input);
}
    /// <summary>The attached dependency property.</summary>
    public static readonly DependencyProperty AutoScrollToEndProperty =
        DependencyProperty.RegisterAttached("AutoScrollToEnd", typeof(bool), typeof(TextBoxBehavior),
            new UIPropertyMetadata(false, AutoScrollToEndPropertyChanged));

    /// <summary>Gets the value.</summary>
    /// <param name="obj">The object.</param>
    /// <returns>The value.</returns>
    public static bool GetAutoScrollToEnd(DependencyObject obj)
    {
        return (bool)obj.GetValue(AutoScrollToEndProperty);
    }

    /// <summary>Enables automatic scrolling behavior, unless the <c>TextBox</c> has focus.</summary>
    /// <param name="obj">The object.</param>
    /// <param name="value">The value.</param>
    public static void SetAutoScrollToEnd(DependencyObject obj, bool value)
    {
        obj.SetValue(AutoScrollToEndProperty, value);
    }

    private static void AutoScrollToEndPropertyChanged(DependencyObject dependencyObject,
        DependencyPropertyChangedEventArgs e)
    {
        var textBox = dependencyObject as TextBox;
        var newValue = (bool)e.NewValue;
        if (textBox == null || (bool)e.OldValue == newValue)
        {
            return;
        }
        if (newValue)
        {
            textBox.TextChanged += AutoScrollToEnd_TextChanged;
        }
        else
        {
            textBox.TextChanged -= AutoScrollToEnd_TextChanged;
        }
    }

    private static void AutoScrollToEnd_TextChanged(object sender, TextChangedEventArgs args)
    {
        var tb = (TextBox)sender;
        if (tb.IsFocused)
        {
            return;
        }
        if (tb.LineCount > 1) // scroll to bottom
        {
            tb.ScrollToEnd();
        }
        else // scroll horizontally (what about FlowDirection ??)
        {
            tb.Dispatcher.BeginInvoke(new Action(() => tb.ScrollToHorizontalOffset(1000.0)), DispatcherPriority.Input);
        }
    }
        <TextBox b:TextBoxBehavior.AutoScrollToEnd="True"
                 Text="{Binding Filename}"/>