这是一个奇怪的,我甚至不知道要搜索什么,但相信我,我有。
我有一个文本框,并且绑定到OnTextChanged
事件是以下方法。
这里的目的是给文本框焦点,将光标移动到TextBox的末尾,并将焦点返回到实际聚焦的任何内容(通常是按钮)。问题是,在将焦点发送回最初聚焦的元素之前,似乎TextBox没有“重绘”(因为缺少更好的单词?),因此光标位置不会在屏幕上更新(尽管所有属性都认为它有) 。
目前,我已经粗暴地将这一点一起攻击,这基本上将前一个焦点项目的重新聚焦延迟了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()方法,所以我尝试了以下但是得到了相同的结果:(
var oldFocus = FocusManager.GetFocusedElement(FocusManager.GetFocusScope(this));
Dispatcher.Invoke(DispatcherPriority.Send, new Action(delegate
{
box.Select(box.Text.Length, 0);
box.Focus();
}));
Dispatcher.Invoke(DispatcherPriority.SystemIdle, new Action(() => oldFocus.Focus()));
答案 0 :(得分:1)
你走在正确的轨道上,问题是,对于你的.Focus()
电话,你需要将电话延迟到Dispatcher的稍后时间。
不要使用发送的DispatcherPriority
值(这是最高的),请尝试使用Dispatcher将焦点设置为稍后的DispatcherPriority,例如输入。
Dispatcher.BeginInvoke(DispatcherPriority.Input,
new Action(delegate() {
oldFocus.Focus(); // Set Logical Focus
Keyboard.Focus(oldFocus); // Set Keyboard Focus
}));
如您所见,我也在设置键盘焦点
WPF可以有多个焦点范围,并且多个元素可以具有逻辑焦点(IsFocused = true
)。但是,只有一个元素可以使用键盘焦点并接收键盘输入。
答案 1 :(得分:0)
经过许多天,我终于能够开始工作了。它需要Dispatcher检查文本框是否同时具有焦点和键盘焦点以及大量循环。
以下是供参考的代码。有一些评论,但如果有人点击此页面寻找答案,你将不得不自己阅读。提醒一下,这是关于文本更改的。
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();
}
}
答案 2 :(得分:0)
最后,我发现&#34;对&#34;这个问题的解决方案(底部的完整解决方案):
if (!tb.IsFocused)
{
tb.Dispatcher.BeginInvoke(new Action(() =>
tb.ScrollToHorizontalOffset(1000.0)), DispatcherPriority.Input);
}
实际上,你并不想要关注文本框 - 这个hack是必需的,因为如果TextBox没有焦点,TextBox.CaretIndex,TextBox.Select()等不会做任何事情。使用其中一种Scroll方法可以无需关注。我不知道double offset
到底应该是什么(使用过量的1000.0
为我工作)。该值的行为类似于像素,因此请确保它足够适合您的场景。
接下来,当用户使用键盘输入编辑值时,您不想触发此行为。作为奖励,我结合了垂直和水平滚动,其中多行TextBox垂直滚动,而单行TextBox水平滚动。最后,您可能希望将此事件重用为附加属性/行为。希望您喜欢这个解决方案:
/// <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);
}
}
XAML用法:
<TextBox b:TextBoxBehavior.AutoScrollToEnd="True"
Text="{Binding Filename}"/>
其中xmlns:b
是相应的clr-namespace。快乐的编码!