26. januar 2009 - 16:22Der er
9 kommentarer og 2 løsninger
Opbygning af GUI programmer og tråde
Hej eksperten.
Jeg er ved at lave en mindre applikation og vil gerne binde noget GUI på min applikation. Pt. fungerer det som et kommandolinje baseret program, som kører i uendelig løkke. I dens løkke ændrer den data. Før løkken starter sættes der nogle værdier som er statiske for løkken.
Jeg tænkte at lave et program der vha. WinForms satte de første statiske variabler og satte løkken igang. Men de data som løkken ændrer skal jo vises i min GUI, og måske også ændres fra min GUI. Og de kan ikke køre i samme tråd, men de skal begge kunne dele information.
Jeg har søgt nettet tyndt, nogle mener delegates er løsningen, andre noget andet. Men synes ikke man nogensinde får indblikket i hvordan man designer noget så man ikke låser sin GUI-tråd.
Kan nogle pege mig i en retning? Evt. komme med noget simpelt kode så jeg selv kan udbygge, eller bare komme med en klog kommentar?
Det er som hovedregel kun GUI/Main-tråden, der må røre ved GUI-dele, f.eks. sætte text i en tekstboks. MS har dog lavet en hjælpefunktion på Control: Invoke, som gør dette meget nemmere end i "de gode, gamle dage".
1. sørg for din løkke og dens logik ligger i en metode, som kan kaldes et andet sted fra i dit applikation.
2. opret en tråd som kalder denne metode.. kan gøres nemt med f.eks Thread thread = new Thread(new ThreadStart(dinMetodesNavnUdenParanteser)); thread.Start();
3. når din metode skal opdatere et element i din GUI skal du have GUI tråden til og gøre arbejdet.. kode kunne se ud som:
if(this.InvokeRequired){ ParameterizedThreadStart doUpdate = new ParameterizedThreadStart(UpdateText); this.Invoke(doUpdate, "Et eller andet"); } else { updateText("Et eller andet"); }
Brug en BackgroundWorker! Den er lavet til formål der minder om dit. At lave flertrådet applikationer giver ofte flere problemer end det løser. BackgroundWorker klassen gør mange ting for dig, og den gør det rigtigt.
Et problem du vil støde på med det samme, hvis du bruger Thread klassen, som kalp foreslår, er at dit program kaster en fejlmeddelelse, hver gang du lukker programmet. Dette opstår, da formen bliver lukket/disposed før tråden, og når tråden derefter kalder Invoke, kastes der en fejl. BackgroundWorker håndterer dette for dig, så du slipper at tænke på, plus at den håndtere flere andre ting.
Når man skal kommunikere med BackgroundWorker tråden, går det via events, og man skal ikke kalde Invoke (Det gør BackgroundWorker klassen for dig).
Jeg har oprettet en Form i visual Studio. Fra toolbox'en har jeg trukket ind en Button, en ProgressBar og en BackgroundWorker. På BackgroundWorkeren har jeg sat WorkerReportsProgress propertien til true, og jeg abonnerer på DoWork eventet og ProgressChanged eventet.
Min kode ser således ud.
using System; using System.ComponentModel; using System.Windows.Forms; using System.Threading;
namespace WindowsFormsApplication2 { public partial class BackgroundWorkerTestForm : Form { public BackgroundWorkerTestForm() { InitializeComponent(); }
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) { //Dette er BackgroundWorker tråden. Jeg kører rundt i en løkke, og rapporterer progress. for (int i = 0; i < 100; i++) { backgroundWorker1.ReportProgress(i); Thread.Sleep(50); } }
Aha lækkert kode eksempel! Fik også en BackgroundWorker anbefalet andetstedsfra. Den ser imidlertid ret nem ud at implementere, men er det korrekt at den kun kan fortælle den progress? For imidlertid virker det som om jeg vil kommunikere mere data ud til mine forms.
Du kan godt fortælle mere end bare et Progress tal. ProgressChangedEventArgs har i tillæg til ProgressPercentage propertien en property der hedder UserState, som du kan putte hvad som helst i.
Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.