ورود

View Full Version : سوال: پرکردن کنترلهابی WPF از یک ORM با استفاده از BackgroundWorker



PetekDincos
چهارشنبه 01 تیر 1390, 13:19 عصر
با سلام
من برای پر کردن کنترلهای WPF مثل DataGrid,ListView,... که تو لود فرمم می خوام پر بشند و از یک ORM مثل EF کوئری می گیرم و اون رو به این کنترلها بایند می کنم و چون ممکنه هزینه کوئری و بایند من بالا باشه و فرم به حالت فریز بره می خوام از BackgroundWorker استفاده کنم مثلا برای پر کردن یک ListView با یک جدول از دیتابیسم به صورت زیر از Backgroundworker استفاده می کنم


public partial class MainWindow : Window
{
private BackgroundWorker _bw;
public MainWindow()
{
InitializeComponent();
_bw = new BackgroundWorker()
{
WorkerReportsProgress=true,
WorkerSupportsCancellation=true
};
_bw.DoWork += bw_DoWork;
_bw.RunWorkerCompleted += bw_RunWorkerComplete;
_bw.ProgressChanged += bw_ProgressChanged;

}

private void bw_DoWork(object sender, DoWorkEventArgs e)
{
for (int i = 0; i <= 100; i ++)
{
if (_bw.CancellationPending) { e.Cancel = true; return; }
_bw.ReportProgress(i);
Thread.Sleep(10);
}
e.Result = "RunWorkerComplete";
}
private void bw_RunWorkerComplete(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Cancelled)
{
if (_bw.IsBusy)
_bw.CancelAsync();
}
else if (e.Error != null)
{
if (_bw.IsBusy)
_bw.CancelAsync();
MessageBox.Show(e.Error.ToString());
}
else
{
MainListView.ItemsSource = FillListView();
}
}
private void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
// MainListView.ItemsSource =(List<MyClass>) e.UserState;
Progress1.Value = e.ProgressPercentage;
}
private List<MyClass> FillListView()
{
List<MyClass> MyList=new List<MyClass>();
for (int i = 1; i < 10000000; i++)
MyList.Add(new MyClass(){Name="Name "+i,Family="Family "+i,Age=i});
return MyList;
}

private void Window_Loaded(object sender, RoutedEventArgs e)
{
_bw.RunWorkerAsync();
}

}
public class MyClass
{
public string Name { get; set; }
public string Family { get; set; }
public int Age { get; set; }
}

که در اینجا MyClass مثلا جدول من در دیتابیسمه و متد FillListView یک کوئری از ORM است و معلوم نیست این کوئری تعداد رکوردهای برگشتیش چندتا باشه و من در متد DoWork بگروندم در یک حلقه از 1 تا 100 و با Sleep 10 می خوام پراپرتی Value کنترل ProgressBar توی فرم رو مقداردهی کنم تا در رویداد ProgressChanged این کار انجام بشه و چون کوئری برگشتی از دیتابیسم هزینش بالاست با این نمایشی که ProgressBar داره هم خوانی نداره می خواستم بدونم وقتی از یک ORM مثل EF یک کوئری گرفته می شه رویدادهای BackgroundWorker رو چطور بنویسیم که با پر شدن ProressBar هم خوانی داشته با شه
و اگه سوالم زیاد در هم برهم بود ببخشید با تشکر

مهدی فرزاد
چهارشنبه 01 تیر 1390, 15:20 عصر
سلام
کد شما چند مشکل اساسی داره
ببینید عمل پر شدن لیست شما باید در bw_DoWork انجام بشه نه در bw_RunWorkerComplete
در کد شما انگار که بکگراند ورکر بکار نبردید!!
دوم اینکه من پیش نهاد میکنم برای این کار پر شدن Progress رو نشون ندید و فقط اونو در حالت IsIndeterminate="True f بکار ببرید
چون همون جور که در کد اصلاح شده ای که من برای شما میگذارم شما نیاز به دونستن تعداد رکورد های درون دیتابیس دارید و برای این هم باید کوئری بنویسید که بازم همون مشکل قبلی پیش میاد
من یک نمونه کد اصلاح شده شما رو میگذارم البته در این نمونه چون ما میدونستیم چند رکورد قرار در لیست قرار بگیره میتونم زمان پر شدن Progress رو محاسبه کنیم
public partial class MainWindow : Window
{
private BackgroundWorker _bw;
private ListCollectionView view;
private List<MyClass> MyList = new List<MyClass>();
public MainWindow()
{
InitializeComponent();
_bw = new BackgroundWorker()
{
WorkerReportsProgress = true,
WorkerSupportsCancellation = true
};
_bw.DoWork += bw_DoWork;
_bw.RunWorkerCompleted += bw_RunWorkerComplete;
_bw.ProgressChanged += bw_ProgressChanged;
this.view = (ListCollectionView)(CollectionViewSource.GetDefau ltView(this.MyList));

}

private void bw_DoWork(object sender, DoWorkEventArgs e)
{

if (_bw.CancellationPending) { e.Cancel = true; return; }
else
{

for (int i = 1; i < 100000; i++)
{

MyClass m = (MyClass)this.view.AddNew();
m.Name = "Name " + i;
m.Family = "Family " + i;
m.Age = i;
this.view.CommitNew();
_bw.ReportProgress(this.view.Count / 1000);
}


}
}
private void bw_RunWorkerComplete(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Cancelled)
{
if (_bw.IsBusy)
_bw.CancelAsync();
}
else if (e.Error != null)
{
if (_bw.IsBusy)
_bw.CancelAsync();
MessageBox.Show(e.Error.ToString());
}
else
{
MainListView.ItemsSource = MyList;
}
}
private void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
Progress1.Value = e.ProgressPercentage;
}


private void Window_Loaded(object sender, RoutedEventArgs e)
{
_bw.RunWorkerAsync();
}

}
public class MyClass
{
public string Name { get; set; }
public string Family { get; set; }
public int Age { get; set; }
}

استفاده از ListCollectionView فقط برای این بوده که زمان Progress رو محاسبه کنیم اگر نیازی به محاسبه نباشه و از اون راهی که گفتم برید و فقط به نوشتن یک جمله لطفا صبر کنید اکتفا کنید به ListCollectionView نیازی نیست