2016-01-07 2 views
-1

Известно, что метод Invoke используется, когда вам необходимо обновить gui из другого потока. Но как я могу реализовать это без привязки к коду?Обновление GUI из потока C#, без привязки к элементам управления пользовательского интерфейса

Вот мой тестовый класс:

class test 
{ 
    public List<Thread> threads = new List<Thread>(); 
    public int nThreads = 0; 
    public int maxThreads = 5; 

    public void DoWork(object data) 
    { 
     string message = (string)data; 
     //MessageBox.Show(message);   


    } 

    public void CreateThread(object data) 
    { 
     if (nThreads >= maxThreads) 
      return; 
     Thread newThread = new Thread(DoWork); 
     threads.Add(newThread); 
     newThread.IsBackground = true; 
     newThread.Start(data); 
     nThreads++; 


    } 

    public void WindUpThreads() 
    { 
     //MessageBox.Show("count: " + nThreads.ToString()); 
     for(int i = 0; i < threads.Count; i++) 
     { 
      if (threads[i].IsAlive == false) 
      { 
       threads[i].Abort(); 
       threads.RemoveAt(i); 
       //MessageBox.Show("removing at " + i.ToString()); 
      } 

     } 

     nThreads = threads.Count; 
    } 

} 

Вопрос заключается в том, что = tecnique я должен использовать для того, чтобы обновить графический интерфейс, но не жёстко контроль в классе? Я попытался передать делегата методу DoWork, но это не работает (http://pastebin.com/VaSYFxPw). Благодаря!

Я использую WinForms, .NET 3.5

Вот обработчик button_click:

private void button1_Click(object sender, EventArgs e) 
    { 
     button1.Enabled = false; 
     test thTest = new test(); 
     string[] strings; 
     try 
     { 

      strings = File.ReadAllLines("C:\\users\\alex\\desktop\\test.txt"); 
     } 

     catch (Exception ex) 
     { 
      MessageBox.Show(ex.Message); 
      return; 
     } 

     bool flag = true; 
     int counter = 0; 
     int dataCount = strings.Length; 

     while (flag == true) 
     { 
      if (counter >= dataCount) 
      { 
       flag = false; 
      } 

      while (thTest.nThreads < thTest.maxThreads) 
      { 
       if (flag == false) 
        break; 

       thTest.CreateThread(strings[counter]); 
       //Data d = new Data(); 
       //d.deleg = AddItem; 
       //d.mess = strings[counter]; 
       //thTest.CreateThread((object)d); 
       //MessageBox.Show(counter.ToString()); 
       counter++; 
      } 

      thTest.WindUpThreads(); 

      if (flag == false) 
      { 
       do 
       { 
        thTest.WindUpThreads(); 

       } while (thTest.nThreads != 0); 
      } 



     } 

     listBox1.Items.Add("Done"); 

    } 

Идея заключается в том, что I'am запуск потоков для каждой задачи, я хочу, чтобы обработать. После того, как я проверю, есть ли завершенные задачи, затем они завершаются, и новые запускаются, пока не осталось больше задач.

+0

Каждый список <> имеет свойство .Count, вы можете использовать его вместо nThreads. В цикле for вы удаляете элементы из списка. Это может быть проблематично. Представьте список из 2 предметов. Цикл начинается с i = 0, а затем удаляет 0-й элемент. Теперь список имеет только 1 элемент, и когда он пытается получить доступ к списку [1], он не сработает. –

+0

Какую структуру вы используете? WinForms? WPF? ...? –

+0

@DennisKuypers, но я уже использую .Count. Или ты, что ты имеешь в виду? –

ответ

1

Вместо того, чтобы DoWork ответственного за обновление пользовательского интерфейса с результатами операции он выполняет, просто иметь его возвращают значение:

//TODO change the type of the result as appropriate 
public string DoWork(string message) 
{ 
    string output = "output"; 
    //TODO do some work to come up with the result; 
    return output; 
} 

Затем использовать Task.Run создать Task, который представляет, что работа делается в поток потока потока. Затем вы можете ждать этой задачи из вашего обработчика щелчка.

private async void button1_Click(object sender, EventArgs e) 
{ 
    button1.Enabled = false; 
    test thTest = new test(); 
    //I'd note that you really should pull out reading in this file from your UI code; 
    //it should be in a separate method, and it should also be reading 
    //the file asynchronously. 
    string[] strings; 
    try 
    { 
     strings = System.IO.File.ReadAllLines("C:\\users\\alex\\desktop\\test.txt"); 
    } 
    catch (Exception ex) 
    { 
     MessageBox.Show(ex.Message); 
     return; 
    } 
    foreach (var line in strings) 
    { 
     var result = await thTest.DoWork(line); 
     listBox1.Items.Add(result); 
    } 
    listBox1.Items.Add("Done"); 
} 

Если вы действительно хотите быть старой школы об этом, вы можете использовать вместо BackgroundWorker. Просто выполните свою работу в обработчике DoWork, установив результат (через аргумент), когда вы его вычислили, и обновите пользовательский интерфейс с результатом в обработчике событий RunWorkerCompleted. Это позволяет вам работать отдельно от пользовательского интерфейса и не-пользовательского интерфейса, хотя он гораздо менее мощный, универсальный и расширяемый, как и более новые функции.

+2

Его Dot.Net 3.5 есть, как я знаю, нет функции async/await –

+0

@Servy, Спасибо за хороший пример, но мне нужно сделать это, используя только Threading. –

+0

@MaximFleitling Это особенность C#, а не рамки. Так что да, вы действительно можете использовать 'async/await' с .NET 3.5. – Servy

0

Вопрос = какой tecnique я должен использовать для обновления gui, но не управления жестким кодом в классе? Я попытался передать делегата методу DoWork, но это не работает

Это действительно одна из возможных техник. Это не работает, потому что у вас есть блокирующий цикл в потоке пользовательского интерфейса - большая часть кода внутри обработчика button1_Click. Не имеет значения, что вы создаете дополнительные рабочие потоки - этот код поддерживает занятость потока пользовательского интерфейса, поэтому Control.Invoke/Control.BeginInvoke не работает, потому что они обрабатываются контуром потока сообщений пользовательского интерфейса, который в этом случае не имеет никакого шанса сделать это. Конечным результатом является классический тупик.

Итак, вы можете использовать подход делегата, но чтобы заставить его работать, вам нужно переместить этот код в отдельный поток. Что-то вроде этого

private void button1_Click(object sender, EventArgs e) 
{ 
    button1.Enabled = false; 
    var worker = new Thread(DoWork); 
    worker.IsBackground = true; 
    worker.Start(); 
} 

private void OnWorkComplete(Exception error) 
{ 
    if (error != null) 
     MessageBox.Show(error.Message); 
    button1.Enabled = true; 
} 

private void DoWork() 
{ 
    Exception error = null; 
    try { DoWorkCore(); } 
    catch (Exception ex) { error = ex; } 
    Invoke(new Action(OnWorkComplete), error); 
} 

private void DoWorkCore() 
{ 
    test thTest = new test(); 
    // NOTE: No try/catch for showing message boxes, this is running on a non UI thread 
    string[] strings = File.ReadAllLines("C:\\users\\alex\\desktop\\test.txt"); 

    bool flag = true; 
    int counter = 0; 
    int dataCount = strings.Length; 
    // The rest of the code... 
    // Pass a delegate to the other threads. 
    // Make sure using Invoke when you need to access/update UI elements 
} 
Смежные вопросы

 Смежные вопросы