Chinaunix首页 | 论坛 | 博客
  • 博客访问: 455351
  • 博文数量: 724
  • 博客积分: 40000
  • 博客等级: 大将
  • 技术积分: 5010
  • 用 户 组: 普通用户
  • 注册时间: 2008-10-13 14:47
文章分类

全部博文(724)

文章存档

2011年(1)

2008年(723)

我的朋友

分类:

2008-10-13 16:56:58

/*
 * MSDN Magazine May 2004中Basic Instincts:Updating the UI from a Secondary Thread一文的VC#实现
 *
 * 原文链接:

 * 译文链接:

 * 原作者:Ted Pattison
 * VC#实现者:Abbey
 * 
 * 原文的程序是用VB.NET写成的,有感于部分朋友对VB.NET并不熟悉(其实我也不懂VB,呵呵),而我也用VC#.NET
 * 于是便有了用VC#.NET重写这个程序的想法。在重写的过程中,我加入了一些重要的注释和自己的理解
 * 希望对各位的理解能有所帮助
 *
 * 这个程序的执行流程:
 * @ 点击窗体上的“获取客户名单”按钮buttonGetCustomer,触发其单击消息处理函数buttonExit_Click()
 * @ 在buttonExit_Click()中调用UpdateUI()更新UI,然后用准备好的GetCustomers()的委托进行异步调用,
 *   并挂上回调方法MyCallback()
 * @ 然后GetCustomers()开始与主线程一起执行,当它执行完之后调用MyCallback()
 * @ 在MyCallback()内部,又用EndInvoke()取回结果,之后调用UpdateUI()实现UI更新
 *
 *
 * 版权声明:未经作者同意,禁止以任何形式进行转载
 */

using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.Threading;  //需要添加对这个命名空间的引用,才能使用Thread.Sleep()

namespace UpdateUI
{
 public class MainForm : System.Windows.Forms.Form
 {
  private System.Windows.Forms.Button buttonGetCustomer;
  private System.Windows.Forms.ListBox listBoxCustomer;
  private System.Windows.Forms.Button buttonExit;
  private System.Windows.Forms.StatusBar statusBar;
 
  private System.ComponentModel.Container components = null;

  //为了能使用BeginInvoke(),需要声明一个GetCustomers对应的委托类型GetListMethod
  
  public delegate string[] GetListMethod();
  private GetListMethod GetCustomersHandler;

  //为了能在BeginInvoke()完成后自动调用一个回调方法,需要声明一个该方法的委托变量
  
  private AsyncCallback CallbackHandler;

  public MainForm()
  {
   InitializeComponent();

   //把之后要用的两个委托准备好

   GetCustomersHandler = new GetListMethod(GetCustomers);
   CallbackHandler = new AsyncCallback(MyCallback);
   
   //注意,如果我们要在StatusBar上使用Panel就还需要下面这样的步骤
   //设置StatusBar的ShowPanels为true
   //添加一个StatusBarPanel后才能真正使用
   //statusBar.ShowPanels = true;
   //StatusBarPanel Panel = new StatusBarPanel();
   //statusBar.Panels.Add(Panel);
   
  }

  protected override void Dispose( bool disposing )
  {
   if( disposing )
   {
    if (components != null)
    {
     components.Dispose();
    }
   }
   base.Dispose( disposing );
  }

  #region Windows 窗体设计器生成的代码
  private void InitializeComponent()
  {
   this.buttonGetCustomer = new System.Windows.Forms.Button();
   this.listBoxCustomer = new System.Windows.Forms.ListBox();
   this.buttonExit = new System.Windows.Forms.Button();
   this.statusBar = new System.Windows.Forms.StatusBar();
   this.SuspendLayout();
   //
   // buttonGetCustomer
   //
   this.buttonGetCustomer.Location = new System.Drawing.Point(84, 24);
   this.buttonGetCustomer.Name = "buttonGetCustomer";
   this.buttonGetCustomer.Size = new System.Drawing.Size(120, 23);
   this.buttonGetCustomer.TabIndex = 0;
   this.buttonGetCustomer.Text = "获取客户名单(&G)";
   this.buttonGetCustomer.Click += new System.EventHandler(this.buttonGetCustomer_Click);
   //
   // listBoxCustomer
   //
   this.listBoxCustomer.ItemHeight = 12;
   this.listBoxCustomer.Location = new System.Drawing.Point(28, 71);
   this.listBoxCustomer.Name = "listBoxCustomer";
   this.listBoxCustomer.Size = new System.Drawing.Size(233, 88);
   this.listBoxCustomer.TabIndex = 1;
   //
   // buttonExit
   //
   this.buttonExit.Location = new System.Drawing.Point(107, 192);
   this.buttonExit.Name = "buttonExit";
   this.buttonExit.TabIndex = 2;
   this.buttonExit.Text = "退出(&E)";
   this.buttonExit.Click += new System.EventHandler(this.buttonExit_Click);
   //
   // statusBar
   //
   this.statusBar.Location = new System.Drawing.Point(0, 240);
   this.statusBar.Name = "statusBar";
   this.statusBar.Size = new System.Drawing.Size(288, 22);
   this.statusBar.TabIndex = 3;
   this.statusBar.Text = "准备";
   //
   // MainForm
   //
   this.AutoScaleBaseSize = new System.Drawing.Size(6, 14);
   this.ClientSize = new System.Drawing.Size(288, 262);
   this.Controls.Add(this.statusBar);
   this.Controls.Add(this.buttonExit);
   this.Controls.Add(this.listBoxCustomer);
   this.Controls.Add(this.buttonGetCustomer);
   this.Name = "MainForm";
   this.Text = "从辅助线程更新UI的C#实现";
   this.ResumeLayout(false);

  }
  #endregion

  
  [STAThread]
  static void Main()
  {
   Application.Run(new MainForm());
  }
   
  private void buttonExit_Click(object sender, System.EventArgs e)
  {
   Close();
  }

  //关于Delegate.BeginInvoke()方法的说明:
  //返回值:IAsyncResult类型,表明异步操作的状态
  //参数表第一部分是该委托类型的参数表
  //第二部分包括一个委托方法执行完毕后自动调用的回调方法,一个object类型的数组为该回调方法提供参数.
 
  private void buttonGetCustomer_Click(object sender, System.EventArgs e)
  {
   UpdateUI("开始检索客户名单...", null);

   //下面我将讲述异步调用的三种方式,注意!!!!

   //开始利用委托进行异步的方法调用,并在被调用方法异步执行完毕后自动调用MyCallback()
   //瞧,我们还没办法以其他的方式挂上我们的回调方法,所以之前的两个委托类型声明的变量在这里就被用上了

   GetCustomersHandler.BeginInvoke(CallbackHandler, null);

   //我们也可以通过主线程中的一个while语句轮询BeginInvoke()返回的IAsyncResult.IsCompleted属性
   //来查询异步调用的方法是否已经执行完毕

   //也可以在主线程中使用BeginInvoke返回的IAsyncResult.AsyncWaitHandle属性来获取WaitHandle
   //这个返回的WaitHandle允许客户端等待异步操作完成,而不是轮询IsCompleted。
   //当异步调用完成时会发出WaitHandle信号
   //我们可以通过调用WaitHandle.WaitOne()在EndInvoke()前等待它完成,当然这期间你可以干任何事情
   //WaitHandle的WaitAny()、WaitAll()则用于作为参数的WaitHandle数组元素全部/任一收到完成信号
   
  }

  //关于Delegate.EndInvoke()方法的说明:
  //EndInvoke()是阻塞方法,所以当异步调用的方法尚未执行完毕前它是不会返回的
  //返回值就是调用BeginInvoke()的委托对应的方法返回值
  //参数包括您需要异步执行的方法的 out 和 ref 参数以及由 BeginInvoke 返回的 IAsyncResult

  private void MyCallback(IAsyncResult Iar)
  {
   try
   {
    string[] retList;

    retList = GetCustomersHandler.EndInvoke(Iar);
    UpdateUI("完成检索",retList);
   }
   catch (Exception e)
   {
    string Error;

    Error = "错误:" + e.Message;
    UpdateUI(Error, null);
   }   
  }

  //为了能以委托方式调用UpdateUI_Impl()方法,只得为其声明一个委托类型
  
  public delegate void UpdateUIHandler(string statusMessage, string[] Customers);

  private void UpdateUI(string statusMessage, string[] Customers)
  {
   //UpdateUI()的作用其实很简单,就是切换线程、给UpdateUI_Impl()传递参数而已

   if (this.InvokeRequired)  //还在辅助线程里呢
   {
    //瞧,这里不就用上了刚才说的那个委托类型吗?还得为其准备参数
  
    UpdateUIHandler handler = new UpdateUIHandler(UpdateUI_Impl);
    object[] args = new object[] {statusMessage, Customers};

    //Control.BeginInvoke()的参数就不一样,是一个Delegate与该Delegate方法的参数表
    //也是通过下面这个调用,UpdateUI()实现了线程的切换!

    this.BeginInvoke(handler, args);
   }
   else //已经在主线程里了
   {
    UpdateUI_Impl(statusMessage, Customers);
   }
  }

  private void UpdateUI_Impl(string statusMessage, string[] Customers)
  {
   //设置状态条的显示文本,将客户名单绑定为listBox的数据源进行显示
   //如果前面使用了Panel,那么这里就该改为statusBar.Panels[0].Text = statusMessage;

   statusBar.Text = statusMessage;
      
   listBoxCustomer.DataSource = Customers;   
  }

  private string[] GetCustomers()
  {
   //让这个被异步调用的方法休眠5000毫秒之后才返回值

   Thread.Sleep(5000);

   return new string[] {"张三","李四","王五","赵二","周六","钱七","孙八"};
  }
 }
}

posted on 2004-05-16 10:32 Abbey的网络日志 阅读(2278)   

 re: MSDN Magazine May 2004中Basic Instincts:Updating the UI from a Secondary Thread一文的VC#实现代码 2004-06-22 01:34

在声明变量的时候便初始化
private AsyncCallback CallbackHandler = new AsyncCallback(MyCallback);
提示 初始值设定项无法引用非静态字段、方法或属性“UpdateUI.MainForm.MyCallback(System.IAsyncResult)”

请问这与在构造函数里边创建实例有什么区别?

 我理解的... 2004-06-22 11:14

你是说在Constructor中对对象进行实例化与在声明式中直接实例化的区别?或者说产生这个编译错误的原因?

我的理解:MyCallback是一个非static的对象成员,这样的成员是不能直接再赋值给类中的其他成员的。也就是说在类的内部,非static成员间不能直接在定义式中直接相互地赋值(方法与Delegate除外)。

MSDN中的例子:
class MyClass
{
int i = 5;
int j = i; //这里i是非static成员,对内部成员j的赋值就会导致同样的编译错误。
}

我想这也是基于编码安全的考虑,上例中万一i忘记了初始赋值,即使i可以自动地用缺省的初始值赋值,但j的初始化在语义上就讲不通了,而且没有其他方式可以弥补。而static成员就不同了,它在被使用前一定会被初始化,所以可以被用来赋值。不信你可以试试这样的组合:
static int i = 5;
int j = i;


--------------------next---------------------

阅读(217) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~