我有一个WPF UserControl,其中包含一个由于状态更改而可见的StackPanel。当StackPanel变得可见时,我想将键盘焦点设置为特定的子TextBox。我发现(在经过大量试验和错误之后)对TextBox.Focus()的调用将无法设置焦点(并返回false),除非我在BeginInvoke调用中包装该调用,如下所示:
private void CtlClientLookupPanel_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e) {
LogThreadMsg(string.Format("CtlClientLookupPanel_IsVisibleChanged to {0}", CtlClientLookupPanel.Visibility));
if (CtlClientLookupPanel.Visibility == Visibility.Visible) {
Dispatcher.BeginInvoke((ThreadStart)delegate {
bool gotFocus = CtlClientSearchText.Focus();
LogThreadMsg(string.Format("CtlClientSearchText.Focus() returned {0}", gotFocus));
});
}
}
private void LogThreadMsg(string msg) {
string fullMsg = string.Format("Thread: {0} - {1}", Thread.CurrentThread.ManagedThreadId, msg);
System.Diagnostics.Trace.WriteLine(msg);
}
两个LogThreadMsg调用都表明它们位于同一个(UI)线程中,如下所示:
[5232] Thread: 1 - CtlClientLookupPanel_IsVisibleChanged to Visible
[5232] Thread: 1 - CtlClientSearchText.Focus() returned True
那为什么需要这种“黑客”呢?这似乎是某种时间问题,我正在寻找一个下游事件,这可能是一个更好的地方调用Focus()而不诉诸于此,但还没有找到它。谁能解释一下这里发生了什么?
答案 0 :(得分:3)
这确实是一个时间问题。当CtlClientLookupPanel
变得可见时,我认为您的TextBox
尚未显示,无法集中注意力。您可以尝试处理IsVisibleChanged
上的TextBox
事件而不是
答案 1 :(得分:1)
实际上可以更准确地说,当您尝试对焦时,文本框尚未呈现。 WPF的核心是一种消息泵,就像WinForms一样,但是更为先进的一种 - Dispatcher。 Dispatcher用于排队工作 - 当队列中的消息根据其优先级处理时,您触发的某些操作将在稍后的同一线程上执行。 BeginInvoke在Dispatcher队列上对另一个工作项进行排队,并在首先需要的其他项之后执行。
这是一个非常黑客的解释,我鼓励你阅读更多内容 - 只需谷歌WPF调度员阅读任何大量的文章,大多数是非常好的。
编辑:另外,在你的情况下要处理的好事件是文本框的Loaded事件;通常只有在完成所有其他布局工作并且控件实际可见后才会触发Loaded事件。但是,在Dispatcher上排队项目也是处理事情的好方法。