关于WPF线程渲染问题
今天又遇到WPF线程问题,问题是程序实现点击某OptionButton时让光标落在一个文本框上,代码如在WinForm模式下绝对没有问题,也很简单,就是在optionButton点击事件中设置文本框的焦点;但运行结果是光标已停在文本框上,但焦点不在该文本框上,而optionButton仍能看到虚框。后经修改为BeginInvoke调用解决了(见下面代码),还需要设置优先级为Input
public void ResetFocus()
{
this.Dispatcher.BeginInvoke(
System.Windows.Threading.DispatcherPriority.Input,
new NoArgDelegate(this.UpdateUI));
}
private void UpdateUI()
{
_txtPerson.Focus();
}
// Delegates to be used in placking jobs onto the Dispatcher.
private delegate void NoArgDelegate();
{
this.Dispatcher.BeginInvoke(
System.Windows.Threading.DispatcherPriority.Input,
new NoArgDelegate(this.UpdateUI));
}
private void UpdateUI()
{
_txtPerson.Focus();
}
// Delegates to be used in placking jobs onto the Dispatcher.
private delegate void NoArgDelegate();
转载网上一篇文章(对WPF有了更深的认识):
今天又看到了UI线程的问题,之所以存在UI线程问题,其实还是在线程模型上来说,直接对另一个线程的操作会带来隐患,比方说,UI线程正在渲染A,我们直接操作A的数据会导致渲染的结果和实际数据不一致,说通俗点儿,就是界面上我们看到的数据并不是实际的数据(寒~~我说得都有点儿晕了!)之前,我写过文章,讲述了几个如何修改UI数据从其他线程,但是,毕竟是从技巧层面上去迁就,始终感觉有些不妥,籍着重构代码时的思路,就在这里感慨一下。
今天又看到了UI线程的问题,之所以存在UI线程问题,其实还是在线程模型上来说,直接对另一个线程的操作会带来隐患,比方说,UI线程正在渲染A,我们直接操作A的数据会导致渲染的结果和实际数据不一致,说通俗点儿,就是界面上我们看到的数据并不是实际的数据(寒~~我说得都有点儿晕了!)之前,我写过文章,讲述了几个如何修改UI数据从其他线程,但是,毕竟是从技巧层面上去迁就,始终感觉有些不妥,籍着重构代码时的思路,就在这里感慨一下。
其实,彻底解决这个问题并不困难,首先要理清自己系统中各个层和各个模块的分工,让我们回忆一下,SOA面向服务的架构优越性不言而喻,实现的困难关键在于如何去实现一个企业总线,而这个企业总线的思路,其实就是把数据的使用和数据的操作做个分离。在传统的三层架构,我们通常分为表现层、逻辑层、数据层,表现层来存储数据,逻辑层来控制业务逻辑,数据层来存储管理数据。
如果,我们的设计能够如此清晰,我们就不会存在需要在表现层里使用的数据在逻辑层或数据层里修改,为什么呢?因为,层和层之间不能存在耦合,否则就失去了分层的意义和价值,例如:表现层需要展示产品A的信息,向逻辑层要产品A的数据,产品A的数据被逻辑层过滤选择之后传递出来,传递的是值而不是一个引用,这一点非常重要!!如果传递的是引用会怎么样呢?传递引用会造成数据只能由生成该实例的线程进行操作,否则,存在跨线程操作的安全问题,例如UI线程生成实例,无法在逻辑层线程中修改,而逻辑层线程生成的数据实例也同样无法在UI线程中进行修改,回归了我们的题目:”彻底解决UI跨线程操作问题!“
我们之所以会从逻辑层参会数据引用而不返回一个数据的拷贝(值类型拷贝),最主要的原因是我们被数据绑定迷惑了,我们太依赖于数据绑定带来的好处,但是,使用的思路差异会带来本质的不同。如果,我们把数据绑定只是在表现层中使用,而不是为了方便,把来自于逻辑层的数据结构引用绑定到表现层,在表现层绑定显示之后,如果不是Once绑定,数据在任何时候发生的改变都会自动呈现在界面上。
事实并非如此!!!我们只是一厢情愿而已,上述的情况只是一种理想的假设,而这个理想的假设带来了UI跨线程操作的问题。回想一下,我们当初开发Win32应用程序时,我们会给界面中一个控件的Text属性赋值,但是我们会在后台线程中操作这个Text属性,我们希望数据的改变被呈现在界面中,但却因为跨线程操作而出错,这时候,正确的操作应该是,UI线程在轮询或监听一个后台线程的时间,一旦数据改变,由UI线程来进行数据的修改,这样就不会出错了。同理,在我们开发Silverlight的时候,我们也带着这样的思路,无论是绑定还是任何形式的数据呈现,呈现所在的线程生成数据实例同时管理数据内容,这样就永远不会出现跨线程操作的问题,只要在逻辑层稍作修改即可,通过监听底层数据改变,并把自己当作一个Proxy透传这些数据改变给UI线程,UI线程来决定什么时候修改数据,问题就彻底解决了。
我们之所以会从逻辑层参会数据引用而不返回一个数据的拷贝(值类型拷贝),最主要的原因是我们被数据绑定迷惑了,我们太依赖于数据绑定带来的好处,但是,使用的思路差异会带来本质的不同。如果,我们把数据绑定只是在表现层中使用,而不是为了方便,把来自于逻辑层的数据结构引用绑定到表现层,在表现层绑定显示之后,如果不是Once绑定,数据在任何时候发生的改变都会自动呈现在界面上。
事实并非如此!!!我们只是一厢情愿而已,上述的情况只是一种理想的假设,而这个理想的假设带来了UI跨线程操作的问题。回想一下,我们当初开发Win32应用程序时,我们会给界面中一个控件的Text属性赋值,但是我们会在后台线程中操作这个Text属性,我们希望数据的改变被呈现在界面中,但却因为跨线程操作而出错,这时候,正确的操作应该是,UI线程在轮询或监听一个后台线程的时间,一旦数据改变,由UI线程来进行数据的修改,这样就不会出错了。同理,在我们开发Silverlight的时候,我们也带着这样的思路,无论是绑定还是任何形式的数据呈现,呈现所在的线程生成数据实例同时管理数据内容,这样就永远不会出现跨线程操作的问题,只要在逻辑层稍作修改即可,通过监听底层数据改变,并把自己当作一个Proxy透传这些数据改变给UI线程,UI线程来决定什么时候修改数据,问题就彻底解决了。