2012-03-12 18 views
10

Bir TPL Göreviyle başka bir iş parçacığında uzun bir işlem gerçekleştiren basit bir küçük winform uygulaması var. Bu uzun çalışma sürecinde UI'yi (ilerleme çubuğu veya bir şey) güncellemek istiyorum. Gerekli olmadan bunu yapmak için bir yolu var mı? Devam()?WinForms içindeki alt görevlerden kullanıcı arabirimi nasıl güncellenir

public partial class Form1 : Form 
{ 
    private Task _childTask; 

    public Form1() 
    { 
     InitializeComponent(); 

     Task.Factory.StartNew(() => 
     { 
      // Do some work 
      Thread.Sleep(1000); 

      // Update the UI 
      _childTask.Start(); 

      // Do more work 
      Thread.Sleep(1000); 
     }); 

     _childTask = new Task((antecedent) => 
     { 
      Thread.Sleep(2000); 
      textBox1.Text = "From child task"; 
     }, TaskScheduler.FromCurrentSynchronizationContext()); 


    } 
} 

Ben her yerde istisna olsun bu kodu yürütülüyor:

Cross-thread operation not valid: Control 'textBox1' accessed from a thread other than the thread it was created on.

+0

Metin kutusu başvurusunu iş parçacığına yapıştırmanız gerektiğini düşünüyorum. – jwillmer

cevap

10

Evet, açıkça iletişim kurmak istiyoruz Pencere/Kontrolü BeginInvoke çağırabilir. Senin durumunda bu şuna benzer:

this.textBox.BeginInvoke(new Action(() => 
{ 
    this.textBox.Text = "From child task."; 
})); 
+0

Kesinlikle yolda görünüyor ama bir derleme hatası alıyorum: 'System.Delegate' türüne dönüştüremeyen lambda deyimi dönüştürülemiyor – devlife

+0

delege türü bir eylem oluşturdum ve anonim yöntem yerine eylemde geçirilen. Yukarıdakilerin neden çalışmadığından emin değilim, ancak yolun% 99'unu bana ulaştı. Teşekkürler Drew. – devlife

+1

@devlife Oops, bu sadece yeni API'lerle çalışmaya alışkın olduğum için benim hatam. Evet, eski WinForms BeginInvoke API'sinin imzasının tasarlanma şekli nedeniyle yeni bir Eylemle (...) açıkça sarmanız gerekir. Cevabımı güncelleyeceğim. –

5

Sen TaskSchedulerstate olarak geçiyoruz (veya antecedent, bunu denilen gibi). Bu pek anlamlı değil.

Tam olarak ne yapmak istediğinizden emin değilim, ancak oluştururken değil, Task başlattığınızda TaskScheduler belirtmeniz gerekir. Yapamazsınız

public Form1() 
{ 
    InitializeComponent(); 

    StartAsync(); 
} 

private async void StartAsync() 
{ 
    // do some work 
    await Task.Run(() => { Thread.Sleep(1000); }); 

    // start more work 
    var moreWork = Task.Run(() => { Thread.Sleep(1000); }); 

    // update the UI, based on data from “some work” 
    textBox1.Text = "From async method"; 

    // wait until “more work” finishes 
    await moreWork; 
} 

: Bütün bu C# 5 ve await ile çok daha kolay olacak Tabii

var scheduler = TaskScheduler.FromCurrentSynchronizationContext(); 

Task childTask = null; 

Task.Factory.StartNew(
    () => 
    { 
     Thread.Sleep(1000); 
     childTask.Start(scheduler); 
     Thread.Sleep(1000); 
    }); 

childTask = new Task(
    () => 
    { 
     Thread.Sleep(2000); 
     textBox1.Text = "From child task"; 
    }); 

: Ayrıca, childTask bir saha olmak zorunda olmadığını görünüyor async yapıcısını yapın, ancak bir async yöntemini buradan çalıştırabilirsiniz. UI iş parçacığı üzerinde "İşi yapmak" çalışmıyor, çünkü Task.Run() aracılığıyla açıkça başlatıldı. Ancak, StartAsync() doğrudan çağrıldığından, UI iş parçacığı üzerinde yürütülür.

+0

Bu sadece bir nokta. Ötekini yürütmek için bir görev tamamlanana kadar beklemek istemiyorum. – devlife

+0

Karşılaşmaya çalıştığım şeyi daha iyi göstermek için örneğimi güncelledim. – devlife

+0

Bu durumda, ilk örneğim hala sizin için çalışacaktır. C# 5 sürümünü de güncelledim. – svick

İlgili konular