1niez
(1niez)
17 Kwiecień 2019 18:59
#1
Cześć wszystkim!
Wiem, ostatnio wrzucam tutaj sporo wątków, ale mam w sumie jeden ostatni problem. Otóż napisałem skrypcik, którego fragment wrzucam poniżej i problem polega na tym, że skrypt jest wykonywany poprawnie, ale wartości labeli itd. aktualizują się dopiero po skończeniu pętli, a chciałbym aby aktualizowały się w trakcie jej wykonywania. Jak to zrobić?
Pętla, której to dotyczy:
foreach (var file in listBox3.Items)
{
string obj = Convert.ToString(file);
try
{
if (File.Exists(obj))
{
label4.Text = obj;
try
{
using (FileStream scan_obj = new FileStream(obj, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
System.Threading.Thread.Sleep(123);
if (listBox2.Items.Contains(CheckObject(obj)))
{
listBox4.Items.Add(obj);
string foundcount = Convert.ToString(listBox4.Items.Count);
textBox3.Text = foundcount;
label3.Text = "Liczba znalezionych: " + foundcount;
}
}
}
catch
{
}
}
}
catch
{
}
int scnnr = Convert.ToInt32(textBox2.Text);
scnnr = scnnr * 1 + 1 * 1;
textBox2.Text = Convert.ToString(scnnr);
label2.Text = "Przeskanowano: " + scnnr + " z " + filecount;
int step = Convert.ToInt32(textBox2.Text);
int final = Convert.ToInt32(textBox1.Text);
int precent = 0;
precent = (step * 100) / (final * 1);
textBox4.Text = Convert.ToString(precent);
progressBar1.Value = precent;
if (CheckShieldLauncher.Properties.Settings.Default.ScanType == "Szukanie standardowe")
{
label7.Text = (" Szukanie standardowe w toku... (" + textBox4.Text + "%)");
}
else
{
label7.Text = (" Szukanie niestandardowe w toku... (" + textBox4.Text + "%)");
}
}
Pozdrawiam!
kowgli
(kowgli)
17 Kwiecień 2019 20:19
#2
W skrócie - musisz to zrobić wielowątkowo - w innym “thread’zie”. Teraz twój kod działa w tym samym wątku co UI, więc nie ma się ono kiedy odświeżyć.
Jest to większy temat, ale tu jest fajnie wytłumaczony w kontekście Windows Forms - https://www.syncfusion.com/ebooks/asynchronous_programming_succinctly
A tak bardziej klasycznie:
private void OryginalnaMetoda()
{
saveThread = new Thread(() => KodWInnymWatku());
saveThread.Start();
}
private void KodWInnymWatku()
{
/* tu coś liczysz */
this.Invoke((MethodInvoker)delegate
{
/* Tu aktualizujesz wartości kontrolek */
});
/* tu coś liczysz */
this.Invoke((MethodInvoker)delegate
{
/* Tu aktualizujesz wartości kontrolek */
});
/* tu coś liczysz */
this.Invoke((MethodInvoker)delegate
{
/* Tu aktualizujesz wartości kontrolek */
});
// itd.
}
1niez
(1niez)
17 Kwiecień 2019 20:57
#3
Zastosowałem się do Twojej wskazówki i kod wygląda tak:
private void Timer1_Tick(object sender, EventArgs e)
{
progressBar1.Increment(20);
if (progressBar1.Value == 100)
{
timer1.Stop();
System.Threading.Thread.Sleep(123);
foreach (var obj in listBox1.Items)
{
string dir = Convert.ToString(obj);
listBox3.DataSource = GetFileList("*.*", dir).ToArray();
}
string filecount = Convert.ToString(listBox3.Items.Count);
textBox1.Text = filecount;
label2.Text = "Przeszukano: 0 z " + filecount;
label5.Visible = false;
timer4.Start();
progressBar1.Value = 0;
foreach (var file in listBox3.Items)
{
string obj = Convert.ToString(file);
try
{
if (File.Exists(obj))
{
label4.Text = obj;
try
{
using (FileStream scan_obj = new FileStream(obj, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
System.Threading.Thread.Sleep(123);
if (listBox2.Items.Contains(CheckObject(obj)))
{
listBox4.Items.Add(obj);
update = new Thread(() => StatusUpdate());
update.Start();
}
}
}
catch
{
}
}
}
catch
{
}
updateprogress = new Thread(() => ProgressUpdate());
updateprogress.Start();
}
}
}
public void StatusUpdate()
{
string foundcount = Convert.ToString(listBox4.Items.Count);
textBox3.Text = foundcount;
label3.Text = "Liczba znalezionych: " + threatcount;
}
public void ProgressUpdate()
{
string filecount = Convert.ToString(listBox3.Items.Count);
int scnnr = Convert.ToInt32(textBox2.Text);
scnnr = scnnr * 1 + 1 * 1;
textBox2.Text = Convert.ToString(scnnr);
label2.Text = "Przeszukano: " + scnnr + " z " + filecount; //Tutaj leży błąd
int step = Convert.ToInt32(textBox2.Text);
int final = Convert.ToInt32(textBox1.Text);
int precent = 0;
precent = (step * 100) / (final * 1);
textBox4.Text = Convert.ToString(precent);
progressBar1.Value = precent;
if (CheckShieldLauncher.Properties.Settings.Default.ScanType == "Szukanie standardowe")
{
label7.Text = (" Szukanie standardowe w toku... (" + textBox4.Text + "%)");
}
else
{
label7.Text = (" Szukanie niestandardowe w toku... (" + textBox4.Text + "%)");
}
}
Jednak teraz mam problemy z błędem: “Nieprawidłowa operacja między wątkami: do formantu ‘label2’ uzyskiwany jest dostęp z wątku innego niż wątek, w którym został utworzony.”. Zaznaczyłem na której linijce mi go wskazuje. Dodatkowo pojawił się drobny problem, że po zliczeniu obiektów do przeszukania program nie wskazuje liczby obiektów do przeszukania na starcie tylko zostaje z domyślną wartością “label2” a wcześniej to robił. Próbowałem zamieniać te label2 w normalnej klasie w komentarz, ale nie pomaga.
kowgli
(kowgli)
17 Kwiecień 2019 21:12
#4
Tak jak pisałem każde odwołanie do kontrolek musisz robić wewnątrz
this.Invoke((MethodInvoker)delegate
{
/* Tu aktualizujesz wartości kontrolek */
});
np.
this.Invoke((MethodInvoker)delegate
{
tbTekst.Text = “aaa”;
});
Błąd mówi jasno w czym jest problem. Powyższy kod powoduje chwilowy “powrót” do wątku na którym działa UI.
Jak już skończysz zabawę z tym programem polecam zapoznać się z dwoma książkami:
https://www.amazon.de/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882/ref=sr_1_1?__mk_pl_PL=ÅMÅŽÕÑ&keywords=clean+code+robert+martin&qid=1555535784&s=gateway&sr=8-1
https://www.amazon.de/Adaptive-Code-patterns-principles-Practices/dp/1509302581/ref=sr_1_1?__mk_pl_PL=ÅMÅŽÕÑ&keywords=adaptive+code&qid=1555535812&s=gateway&sr=8-1
To jak teraz piszesz jest … takie sobie. Wszystko jednak przyjdzie z czasem.
1niez
(1niez)
17 Kwiecień 2019 22:52
#5
Dobra! Dzięki! To sporo pomogło i chętnie przeczytam te książki. Mam jeszcze pytanie, ponieważ progressBar owszem się aktualizuje z textBoxami w trakcie pracy, ale same labele już nie i dopiero robią to pod koniec. Mimo tego, że są objęte w tej klasie. Jak to zrobić aby aktualizowały się w trakcie?
xBotekx
(xBotekx)
17 Kwiecień 2019 23:01
#6
Task.Factory.StartNew(() =>
{
for (int i = 0; i < 10; i++)
{
// Any GUI control which you want to use within thread,
// you need Invoke using GUI thread. I have declare a method below for this
//Now use this method as
ExecuteSecure(() => label9.Text = "Processing " + i);
ExecuteSecure(() => progressBar1.Value = i * 10);
//... other code etc.
Thread.Sleep(1000);
}
});
//---
private void ExecuteSecure(Action action)
{
if (InvokeRequired)
{
Invoke(new MethodInvoker(() => action()));
}
else
{
action();
}
}
Kod nie jest mój ale wygląda całkiem dobrze.
1niez
(1niez)
17 Kwiecień 2019 23:17
#7
No niestety nie pomaga ten kodzik.
xBotekx
(xBotekx)
17 Kwiecień 2019 23:25
#8
W sensie nie aktualizuje się czy w ogóle nie działa?
1niez
(1niez)
17 Kwiecień 2019 23:26
#9
Nie aktualizuje mi cały czas labeli. I tylko labeli to dotyczy.
xBotekx
(xBotekx)
17 Kwiecień 2019 23:29
#10
Mogę zobaczyć jak kod teraz wygląda?
1niez
(1niez)
17 Kwiecień 2019 23:33
#11
Task.Factory.StartNew(() =>
{
string filecount = Convert.ToString(listBox3.Items.Count);
int scnnr = Convert.ToInt32(textBox2.Text);
scnnr = scnnr * 1 + 1 * 1;
textBox2.Text = Convert.ToString(scnnr);
ExecuteSecure(() => label2.Text = "Przeszukano: " + scnnr + " z " + filecount);
int step = Convert.ToInt32(textBox2.Text);
int final = Convert.ToInt32(textBox1.Text);
int precent = 0;
precent = (step * 100) / (final * 1);
textBox4.Text = Convert.ToString(precent);
ExecuteSecure(() => progressBar1.Value = precent);
ExecuteSecure(() => CheckShieldLauncher.Properties.Settings.Default.ScanProgress = Convert.ToString(precent) + "%");
});
ProgressBar bez zarzutu, inty też, problem leży z labelami.
xBotekx
(xBotekx)
17 Kwiecień 2019 23:35
#12
Albo oślepłem albo ci pętle ukradli
1niez
(1niez)
17 Kwiecień 2019 23:40
#13
Ops, sorki! Moja gafa
private void Timer1_Tick(object sender, EventArgs e)
{
progressBar1.Increment(20);
if (progressBar1.Value == 100)
{
timer1.Stop();
System.Threading.Thread.Sleep(386);
foreach (var obj in listBox1.Items)
{
string dir = Convert.ToString(obj);
listBox3.DataSource = GetFileList("*.*", dir).ToArray();
}
Task.Factory.StartNew(() =>
{
string filecount = Convert.ToString(listBox3.Items.Count);
ExecuteSecure(() => textBox1.Text = filecount;
ExecuteSecure(() => label2.Text = "Przeszukano: 0 z " + filecount);
});
label5.Visible = false;
timer4.Start();
progressBar1.Value = 0;
foreach (var file in listBox3.Items)
{
string obj = Convert.ToString(file);
try
{
if (File.Exists(obj))
{
label4.Text = obj;
try
{
using (FileStream scan_obj = new FileStream(obj, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
System.Threading.Thread.Sleep(123);
if (listBox2.Items.Contains(CheckObject(obj)))
{
Task.Factory.StartNew(() =>
{
listBox4.Items.Add(obj);
string foundcount = Convert.ToString(listBox4.Items.Count);
ExecuteSecure(() => textBox3.Text = foundcount;
ExecuteSecure(() => label3.Text = "Liczba znalezionych: " + foundcount);
});
}
}
}
catch
{
}
}
}
catch
{
}
Task.Factory.StartNew(() =>
{
string filecount = Convert.ToString(listBox3.Items.Count);
int scnnr = Convert.ToInt32(textBox2.Text);
scnnr = scnnr * 1 + 1 * 1;
ExecuteSecure(() => textBox2.Text = Convert.ToString(scnnr);
ExecuteSecure(() => label2.Text = "Przeszukano: " + scnnr + " z " + filecount);
int step = Convert.ToInt32(textBox2.Text);
int final = Convert.ToInt32(textBox1.Text);
int precent = 0;
precent = (step * 100) / (final * 1);
ExecuteSecure(() => textBox4.Text = Convert.ToString(precent);
ExecuteSecure(() => progressBar1.Value = precent);
ExecuteSecure(() => CheckShieldLauncher.Properties.Settings.Default.ScanProgress = Convert.ToString(precent) + "%");
});
}
}
}
xBotekx
(xBotekx)
17 Kwiecień 2019 23:43
#14
Task.Factory.StartNew(() =>
{
listBox4.Items.Add(obj);
string foundcount = Convert.ToString(listBox4.Items.Count);
ExecuteSecure(() => textBox3.Text = foundcount;
ExecuteSecure(() => label3.Text = "Liczba znalezionych: " + foundcount);
});
Task.Factory.StartNew(() =>
{
string filecount = Convert.ToString(listBox3.Items.Count);
int scnnr = Convert.ToInt32(textBox2.Text);
scnnr = scnnr * 1 + 1 * 1;
ExecuteSecure(() => textBox2.Text = Convert.ToString(scnnr);
ExecuteSecure(() => label2.Text = "Przeszukano: " + scnnr + " z " + filecount);
int step = Convert.ToInt32(textBox2.Text);
int final = Convert.ToInt32(textBox1.Text);
int precent = 0;
precent = (step * 100) / (final * 1);
ExecuteSecure(() => textBox4.Text = Convert.ToString(precent);
ExecuteSecure(() => progressBar1.Value = precent);
ExecuteSecure(() => CheckShieldLauncher.Properties.Settings.Default.ScanProgress = Convert.ToString(precent) + "%");
});
Gdzie są te pętle? Po co uruchamiasz coś do zmiany wątku by użyć to tylko raz?
1niez
(1niez)
17 Kwiecień 2019 23:59
#15
Sorki, to już ze zmęczenia tak. Położenie klas jest okej wg. mnie już. Mimo wszystko nadal labele się nie aktualizują.
private void Timer1_Tick(object sender, EventArgs e)
{
progressBar1.Increment(20);
if (progressBar1.Value == 100)
{
timer1.Stop();
System.Threading.Thread.Sleep(586);
foreach (var obj in listBox1.Items)
{
string dir = Convert.ToString(obj);
listBox3.DataSource = GetFileList("*.*", dir).ToArray();
}
Task.Factory.StartNew(() =>
{
string filecount = Convert.ToString(listBox3.Items.Count);
ExecuteSecure(() => textBox1.Text = filecount);
ExecuteSecure(() => label2.Text = "Przeszukano: 0 z " + filecount);
});
label5.Visible = false;
timer4.Start();
progressBar1.Value = 0;
foreach (var file in listBox3.Items)
{
string obj = Convert.ToString(file);
try
{
if (File.Exists(obj))
{
label4.Text = obj;
try
{
using (FileStream scan_obj = new FileStream(obj, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
System.Threading.Thread.Sleep(123);
if (listBox2.Items.Contains(CheckObject(obj)))
{
Task.Factory.StartNew(() =>
{
ExecuteSecure(() => listBox4.Items.Add(obj);
string foundcount = Convert.ToString(listBox4.Items.Count);
ExecuteSecure(() => textBox3.Text = foundcount);
ExecuteSecure(() => label3.Text = "Liczba znalezionych: " + foundcount);
});
}
}
}
catch
{
}
}
}
catch
{
}
Task.Factory.StartNew(() =>
{
string filecount = Convert.ToString(listBox3.Items.Count);
int scnnr = Convert.ToInt32(textBox2.Text);
scnnr = scnnr * 1 + 1 * 1;
ExecuteSecure(() => textBox2.Text = Convert.ToString(scnnr);
ExecuteSecure(() => label2.Text = "Przeszukano: " + scnnr + " z " + filecount);
int step = Convert.ToInt32(textBox2.Text);
int final = Convert.ToInt32(textBox1.Text);
int precent = 0;
precent = (step * 100) / (final * 1);
textBox4.Text = Convert.ToString(precent);
ExecuteSecure(() => progressBar1.Value = precent);
ExecuteSecure(() => CheckShieldLauncher.Properties.Settings.Default.ScanProgress = Convert.ToString(precent) + "%");
});
}
}
}
xBotekx
(xBotekx)
18 Kwiecień 2019 00:11
#16
Mam wrażenie ,że nie do końca rozumiesz o co chodzi
Task.Factory.StartNew(() =>
{
Uruchamiasz pętle{
to co ma się zmieniać na bieżąco wraz z przebiegiem pętli
}
mr-owl
(mr-owl)
18 Kwiecień 2019 00:55
#17
Witam,
Wy tak na poważnie, zrób to za pomocą docs.microsoft.com/en-us/dotnet/api/system.componentmodel.backgroundworker , praca będzie ładnie wykonana w tle a aktualizacja https://docs.microsoft.com/en-us/dotnet/api/system.windows.forms.progressbar będzie bezproblemowo się zmieniała w wątku UI.
Pozdrawiam,
mr-owl
Edgarus
(Edgarus)
18 Kwiecień 2019 05:41
#18
Jeśli można coś zasugerować, to proponuję przejść na async await. Unika się sporo problemów z wątkowym dostępem do kontrolek i działa to zawsze tak samo na pc/xbox.
1niez
(1niez)
18 Kwiecień 2019 10:23
#19
Dzięki! Poprawiłem to według Twojej wskazówki i wszystko działa świetnie. Przydałaby mi się teraz jedna rzecz jeszcze. Chodzi o to, że jak mam odpaloną tą pętle w tej klasie i próbuję skorzystać z przycisku anulowania i otwierania okna z wynikami to wszystko pięknie się robi tylko za chwilę dostaję komunikat Visual Studio, który wskazuje na to, że okno się zamknęło tylko thread nadal pracuje. Jak zatrzymać ten thread z Task.Factory.StartNew?
xBotekx
(xBotekx)
18 Kwiecień 2019 11:24
#20
Masz przycisk za pomocą którego anulujesz tak?
Spójrz tu https://docs.microsoft.com/en-us/dotnet/standard/parallel-programming/task-cancellation
Ale ogólnie jeśli możesz to pokaż nowy kod może po prostu źle podchodzisz do przerwania operacji?