关于WPF控件行为和使用Invoke的一般问题

时间:2010-05-07 14:26:08

标签: wpf wpf-controls

我一直在推迟SO的活动,因为我目前的名声是“1337”。 :)

这是“为什么”而不是“如何”的问题。默认情况下,似乎WPF在窗口打开时没有将焦点设置到窗口中的第一个控件。此外,当文本框获得焦点时,默认情况下它不会选择现有文本。所以基本上当我打开一个窗口时,我想要专注于窗口的第一个控件,如果该控件是一个文本框,我想要它的现有文本(如果有的话)被选中。

我在网上找到了一些完成这些行为的技巧,并将它们结合起来。我在窗口的构造函数中放置的代码就是我想出的代码:

Loaded += (sender, e) =>
          {
              MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
              var textBox = FocusManager.GetFocusedElement(this) as TextBox;
              if (textBox != null)
              {
                  Action select = textBox.SelectAll;
                  //for some reason this doesn't work without using invoke.
                  Dispatcher.Invoke(DispatcherPriority.Loaded, select);
              }
          };

所以,我的问题。为什么上面不使用Dispatcher.Invoke不起作用?是否内置于窗口(或文本框)的行为中导致所选文本在加载后取消选择?

也许相关,也许不是 - 我必须使用Dispatcher.Invoke来控制表单行为的另一个例子:

WPF Focus In Tab Control Content When New Tab is Created

2 个答案:

答案 0 :(得分:1)

所有WPF控件都具有线程关联性。 Dispatcher管理创建每个控件的线程(通常这是应用程序中每个控件的单个线程,但不一定)。工作在此线程上排队并按优先级顺序执行。

任何UI操作代码必须在与Dispatcher线程上创建控件的同一线程上执行 - 因此任何方法都必须先调用回该线程,然后才能执行任何可能影响的操作用户界面(例如在TextBox中选择文字)。

那就是说,我的理解是默认情况下Loaded事件处理程序会在Dispatcher线程上触发,所以我不完全确定你为什么会在你的具体例子中看到这种行为!

答案 1 :(得分:1)

我首先要提到的是,我没有任何问题可以在.net 4.0的调度员调用中完成这项工作(它可能已在框架更新中得到修复) - 但是,之前的海报所提到的是准确的并且一直是自winforms曙光(.DoActions()和.Invoke())以来的pardigm。但是,在3.5中,如果你使用codebehind中定义的方法作为lambda中的目标调用,那么上面的工作将完成w / out调度程序:

  Loaded += (sender, e) =>
  {
   this.SelectText();
  };  

  void SelectText()
  {
   MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
   var textBox = FocusManager.GetFocusedElement(this) as TextBox;

   if (textBox != null)
   {
    textBox.SelectAll();
   }
  }

至于为什么,我真的不能给你具体细节,但我遇到了类似的问题w /使用lambdas在演示者上路由事件。我想说一下w / reference或编译表达式的上下文要做的事情 - 在这种情况下,它需要对包含对象的引用,以便知道如何委托操作(在右侧线程上选择文本框文本)。我也相信GC偶尔可以清理资源,因此延迟执行会变得很拙劣(在F#中看到它......认为这也是我在C#中出现问题的原因)。