PDA

View Full Version : عدم مقدار دهی متغییر از نوع رشته ای در backgroundWorker1_DoWork_1



mustafa13
یک شنبه 23 اسفند 1388, 13:46 عصر
سلام دوستان چرا در این قسمت نمی توان به متغییر هایی که رشته ای هستن مقدار داد اگه ترفندی هست لطفا راهنمایی کنید با تشکر


private void backgroundWorker1_DoWork_1(object sender, DoWorkEventArgs e)
{
progressBar1.Maximum = 100;

for (int i = 0; i <= 100; i++)
{
label2.Text = i.ToString();
System.Threading.Thread.Sleep(1);
backgroundWorker1.ReportProgress(i);
}
}

mustafa13
سه شنبه 25 اسفند 1388, 11:55 صبح
کسی از دوستان با این خطا برخورد نکرد

mehdi.mousavi
سه شنبه 25 اسفند 1388, 13:18 عصر
کسی از دوستان با این خطا برخورد نکرد

سلام.
پست شماره 9 از این تاپیک رو مطالعه کنید. (http://barnamenevis.org/forum/showthread.php?p=880050)

موفق باشید.

mustafa13
سه شنبه 25 اسفند 1388, 14:22 عصر
سلام.
پست شماره 9 از این تاپیک رو مطالعه کنید. (http://barnamenevis.org/forum/showthread.php?p=880050)

موفق باشید.
ضمن تشکر از تون
اگه قبل از حلقه باشه و یا در حلقه هم اگه شرطی که برقرار شد چی باز هم نیم توان برای متغییری که رشته ای هست مقدار دهی کرد

mehdi.mousavi
سه شنبه 25 اسفند 1388, 14:46 عصر
ضمن تشکر از تون اگه قبل از حلقه باشه و یا در حلقه هم اگه شرطی که برقرار شد چی باز هم نیم توان برای متغییری که رشته ای هست مقدار دهی کرد

سلام.
متوجه سوالتون نمیشم. شما توی Worker Thread خودتون، اجازه دسترسی به UI Control ها رو ندارید... به روشی که در پست شماره 9 (http://barnamenevis.org/forum/showpost.php?p=880050&postcount=9) اون تاپیک توضیح دادم، عمل کنید. اگر مشکلی پیش اومد، اونوقت مطرح کنید تا بهش پاسخ بدم.

موفق باشید.

mahmoodramzani
سه شنبه 25 اسفند 1388, 18:16 عصر
اصولا چیزی که میگید هیچ ربطی به قبل از حلقه یا بعد از حلقه بودن و متغیر رشته ای بودن نداره.
شما میتونید هرجائی که خواستید و هر نوع داده ای رو که خواستید برگردونید به UI Thread
اینجا توضیح داده که چطور این کار رو انجام بدید.
How to: Make Thread-Safe Calls to Windows Forms Controls (http://msdn.microsoft.com/en-us/library/ms171728.aspx)

در واقع کلمه کلید و مهم در اینجا استفاده از this.Invoke هست(البته اگه از Windowm Form ) استفاده میکنید.

mustafa13
چهارشنبه 26 اسفند 1388, 08:49 صبح
سلام
ضمن تشکر از استاتید محترم
ممکن که از ویندوز من باشد چون ویندوز من ویندوز 7
حال من کل سورس را می گذارم لطفا تست کنید ببینید رو دستگاه من همون خطایی که گفتم را می هد از راهنمایی هاتون متشکرم

mahmoodramzani
پنج شنبه 27 اسفند 1388, 14:52 عصر
شما لینکی که دادم رو خوندید؟

مثال از همون لینک:

// Check if this method is running on a different thread
// than the thread that created the control.
if (this.textBox1.InvokeRequired)
{
// It's on a different thread, so use Invoke.
SetTextCallback d = new SetTextCallback(SetText);
this.Invoke
(d, new object[] { text + " (Invoke)" });
}
else
{
// It's on the same thread, no need for Invoke
this.textBox1.Text = text + " (No Invoke)";
}

mustafa13
چهارشنبه 04 فروردین 1389, 23:18 عصر
سلام
سال نو را به همه دوستان تبریک عرض می کنم انشاالله سالی خوبی باشه براتون در کنار خانواده محترم تان
کسی به این مثال من نگاه نکرده

mustafa13
یک شنبه 08 فروردین 1389, 10:37 صبح
سلام.
متوجه سوالتون نمیشم. شما توی Worker Thread خودتون، اجازه دسترسی به UI Control ها رو ندارید... به روشی که در پست شماره 9 (http://barnamenevis.org/forum/showpost.php?p=880050&postcount=9) اون تاپیک توضیح دادم، عمل کنید. اگر مشکلی پیش اومد، اونوقت مطرح کنید تا بهش پاسخ بدم.

موفق باشید.
سلام
ممکن مثالی را که ضمیمه کرده ام را تست کنی متشکرم

mehdi.mousavi
یک شنبه 08 فروردین 1389, 13:04 عصر
سلام ممکن مثالی را که ضمیمه کرده ام را تست کنی متشکرم

سلام.
من توابعی که نوشتید رو اینجا میارم تا دیگه نیازی به Download کردن و دیدن کد توسط دیگران نباشه:


private void button1_Click(object sender, EventArgs e)
{

backgroundWorker1.WorkerReportsProgress = true;
progressBar1.Maximum = 1000;
backgroundWorker1.RunWorkerAsync();

}

private void backgroundWorker1_DoWork_1(object sender, DoWorkEventArgs e)
{
textBox1.Text = "";
for (int i = 0; i <= 1000; i++)
{
System.Threading.Thread.Sleep(5);
backgroundWorker1.ReportProgress(i);
if ((i / 2) > 1)
textBox1.Text = textBox1.Text + textBox1.Text;
}
}

private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar1.Value = e.ProgressPercentage;
label1.Text = e.ProgressPercentage.ToString();
}


من برنامه شما رو اجرا نکردم چون وقتی کد DoWork رو دیدم، نا امید شدم. من بهتون گفتم، که شما نباید توی backgroundWorker1_DoWork_1 به UI Element ها کاری داشته باشید اما بازهم شما دارید textBox1.Text رو Set می کنید! اینکار اشتباهه! هر کاری که مایلید روی UI انجام بدید، باید توی متود backgroundWorker1_ProgressChanged انجام بدید.

@همه: به خاطر خدا پاسخهایی رو که میدم درست و دقیق بخونید.

mehdi.mousavi
دوشنبه 09 فروردین 1389, 15:47 عصر
سلام.
پیرو صحبتهایی که داشتیم (در پیامهای خصوصی)، من کد شما رو بدین شکل تغییر دادم: (البته اینجا میذارم تا اگر افراد دیگری هم همین سوال رو داشتن، پاسخشون رو بگیرن).


private void button1_Click(object sender, EventArgs e)
{
textBox1.Text = "";
backgroundWorker1.WorkerReportsProgress = true;
backgroundWorker1.RunWorkerAsync();
}

private void backgroundWorker1_DoWork_1(object sender, DoWorkEventArgs e)
{
for (int i = 0; i <= 1000; i++)
{
System.Threading.Thread.Sleep(5);
backgroundWorker1.ReportProgress(i * 100 / 1000, "Obj#" + i);
}
}

private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
string result = e.UserState as string;
textBox1.Text += result;

progressBar1.Value = e.ProgressPercentage;
label1.Text = e.ProgressPercentage.ToString();
label1.Refresh();
}


خوب. تغییرات چی بود؟


Range ای که شما برای ProgressBar تعیین کرده بودید رو من حذف کردم، تا همون 0 تا 100 خودش باقی بمونه. چرا اینکارو کردم؟ چون ReportProgress یک عدد بین 0 تا 100 انتظار بهش بدیم، در صورتیکه کد شما ممکن بود تا عدد 1000 رو هم به این تابع بده!
تغییر بعدی، حذف کلیه عملیاتی بود که شما روی TextBox و توی تابع DoWork انجام می دادید. شما اجازه ندارید به UI Element ها از درون این تابع دسترسی پیدا کنید. چرا؟ چون DoWork داره تو Context یک Thread دیگه اجرا میشه و به UI Element ها باید حتما از Thread ای که UI رو ایجاد کرده دسترسی پیدا کرد. این مساله در C++ و بقیه زبانها نیز صادقه و ربطی به C# نداره.
به ReportProgress یک پارامتر دوم اضافه کردم، پارامتری که نیاز دارم توی ProgressChanged بهش دسترسی پیدا کنم. دقت کنید. اینجا تمام اون چیزی هستش که شما برای Marshaling داده ها بین این دو Thread نیاز دارید. من، string ای ساختم که i رو هر سری بهش اضافه میکنم و بعد پیشرفت رو با ReportProgress گزارش میدم. بدین ترتیب، توی متود backgroundWorker1_ProgressChanged، از روی پارامتر دوم، میتونم به این string (یا هر object ای که مایل بودید) دسترسی داشته باشم. چون string تعریف کرده بودم، با کد زیر هم اونو گرفتم:

string result = e.UserState as string;

و توی همین تابع، با خیالی راحت، textBox1.Text رو تغییر میدم. فقط اینجا یک نکته دیگه میمونه! اونم اینکه وقتی با این تغییراتی که گفتم برنامه رو اجرا کنید، متوجه میشید که Label تغییری نکرده و فقط تا شماره خاصی Refresh میشه و بعد روی عدد 100 (یعنی 100%) دوباره Refresh میشه و نمایش داده میشه. این ایراد چی هستش و چطور باید رفعش کرد؟

وقتی شما توی ProgressChanged دارید Label رو تغییر میدید، منظورم با این خط از کد هستش:

label1.Text = e.ProgressPercentage.ToString();

در واقع برنامه داره دستور مزبور رو توی صف پیامهای ویندوز میذاره. تا وقتی این پیامها پردازش نشن، label جدید مشاهده نمیشه... Sleep ای که شما گذاشته اید، Thread رو برای 5 میلی ثانیه متوقف میکنه، حالا 5 میلی ثانیه فرصت داره ویندوز تا توی یک Context Swithing، بره و Label شما رو Refresh کنه. اما قبل از اینکه اینکارو انجام بده، 5 میلی ثانیه طی شده و Context Switching به Worker Thread دوباره باعث میشه تا Main Thread نرسه که کارش رو انجام بده. اگر 5 میلی ثانیه رو بیشتر کنید، مشکل حل میشه، اما اگر همون 5 میلی ثانیه بخواهید نگهدارید، باید بعد تغییر Label اونو به زور Refresh کنید. به بیان دیگه، به Windows بفهمونید که الان باید Label شما رو Invalidate کنه و ...

اون خط از کد که من نوشتم برای Refresh بخاطر این بوده. البته، توصیه میکنم اون 5 میلی ثانیه رو افزایش بدید. قاعدتا با 20 میلی ثانیه، مشکلی مزبور پیش نمیاد و نیازی به Refresh کردن Label بصورت Explicit ندارید.

موفق باشید.