2013-02-22 95 views
0

我有一个运行无UI的FTP进程。并有一个使用这个ftp控件的winform。在那个窗口中我有一个显示ftp上传进度的进度条。进度通过interfase进入窗口,在underliying主持人更新(我使用MVP模式)。使用线程刷新进度条UI

我的问题是当试图更新进度时,它总是抛出我这个异常。

通过线程非法操作:控制'prgProgresoSubido'是从一个线程访问,而不是您创建它的线程。

即使我在表单中使用BackGroundWorker,该问题仍然存在。

// This is a delegated on presenter when a File finish to upload 
    void client_FileUploadCompletedHandler(object sender, FileUploadCompletedEventArgs e) 
    { 
     string log = string.Format("{0} Upload from {1} to {2} is completed. Length: {3}. ", 
      DateTime.Now, e.LocalFile.FullName, e.ServerPath, e.LocalFile.Length); 

     archivosSubidos += 1; 
     _Publicacion.ProgresoSubida = (int)((archivosSubidos/archivosXSubir) * 100); 
     //this.lstLog.Items.Add(log); 
     //this.lstLog.SelectedIndex = this.lstLog.Items.Count - 1; 
    } 


    // This is My interfase 

public interface IPublicacion 
{ 
    ... 
    int ProgresoSubida { set; } 
} 

/// And Here is the implementartion of the interfase on the form 

public partial class PublicarForm : Form ,IPublicacion 
{ 
    //Credenciales para conectarse al servicio FTP 
    public FTPClientManager client = null; 
    public XmlDocument conf = new XmlDocument(); 
    public string workingDir = null; 
    public webTalk wt = new webTalk(); 
    private readonly PublicacionesWebBL _Publicador; 

    public PublicarForm() 
    { 
     InitializeComponent(); 

     String[] laPath = { System.AppDomain.CurrentDomain.BaseDirectory}; 
     String lcPath = System.IO.Path.Combine(laPath); 

     _Publicador = new PublicacionesWebBL(this, lcPath); 
    } 

    public int ProgresoSubida 
    { 
     set 
     { 
      // This is my prograss bar, here it throw the exception. 
      prgProgresoSubido.Value = value; 
     } 
    } 
} 

我该怎么办才能避免这个问题?

+0

*“这个问题仍然存在,即使我用一个BackgroundWorker” * - 你可能不正确地使用BackgroudWorker – GolfWolf 2013-02-22 15:33:49

+0

也许....这是我的第一次用它。也许你有一个简单的?问题是ftp在它自己的Thread中工作,然后我需要克隆另一个trhead来更新进度条? – 2013-02-22 15:34:47

+0

'BackgroudWorker'应该很简单:使用ReportProgress方法显示当前的进度并处理ProgressChanged事件中的UI部分(将在正确的线程中调用)。请参阅http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.progresschanged.aspx – GolfWolf 2013-02-22 15:36:34

回答

1

正如Alan刚刚指出的那样,您必须在UI线程中使用UI控件执行所有操作。

只需修改你的财产是这样的:

public int ProgresoSubida 
{ 
    set 
    { 
     MethodInvoker invoker = delegate 
           { 
            prgProgresoSubido.Value = value; 
           } 
     if (this.InvokeRequired) 
     { 
      Invoke(invoker); 
     } 
     else 
     { 
      invoker(); 
     } 

    } 
} 
+0

Tks。那适合像手套一样。 – 2013-02-22 15:59:56

2

通常,对用户界面和控件的所有更新都必须从主线程(事件分派器)完成。如果您尝试从其他线程修改控件的属性,您将会遇到异常。

必须调用Control.Invoke对事件调度调用,更新你的UI

Control.Invoke

这里的方法中,放置一个按钮和一个窗体上的标签,那就试试这个

public partial class Form1 : Form 
{ 
    public Form1() 
    { 
     InitializeComponent(); 
    } 

    private void button1_Click(object sender, EventArgs e) 
    { 
     Thread t = new Thread(new ThreadStart(TestThread)); 
     t.Start(); 
    } 

    private void TestThread() 
    { 
     for (int i = 0; i < 10; i++) 
     { 
      UpdateCounter(i); 
      Thread.Sleep(1000); 
     } 
    } 

    private void UpdateCounter(int i) 
    { 


     if (label1.InvokeRequired) 
     { 
      label1.Invoke(new ThreadStart(delegate { UpdateCounter(i); })); 
     } 
     else 
     { 
      label1.Text = i.ToString(); 
     } 
    } 
} 

认识到,如果你是从一个线程发起一个事件,该事件将在同一个线程上。因此,如果该线程不是事件分派器,则需要调用。

此外,BackgroundWorker可能会为您提供一些机制(如评论员所说)为您简化了这一操作,但是我之前从未使用过,因此我会将其留给您进行调查。

+0

对不起,如果不明白,但是我第一次使用线程...也许你有样品吗? – 2013-02-22 15:33:52

+0

@JuanPabloGomez在MSDN上的链接中有一个例子,但我会尽量把东西扔在一起 – Alan 2013-02-22 15:34:48

+0

很多,它将会非常赞赏。 – 2013-02-22 15:40:20