I am fetching data from a database and binding it to 3 different combo boxes simultaneously based on the parent comboBox value. Following is the example, I have three combo boxes named
i. comboBoxBranch
ii. comboBoxClass
iii. comboBoxSection
Value of comboBoxClass is fetched from database on the basis of selected branch, Similarly the value of comboBoxSection is fetched on the basis of selected branch and selected class. So the order of binding is (ComboBoxBranch) then comboBoxClass and then comboBoxSection.
Now in order to acheive this I am using seperate thread to call GetBranches() method to bind data with comboboxBranch in following way.
private void GetBranches() (This is working perfectly fine)
{
if (comboBoxBranches.InvokeRequired)
{
comboBoxBranches.BeginInvoke(((MethodInvoker) delegate
{
comboBoxBranches.DataSource = _schoolManagementSystemServiceClient.GetBranches();
comboBoxBranches.ValueMember = "BranchId";
comboBoxBranches.DisplayMember = "BranchName";
}));
}
Now the problem occurs how should I bind data with other two comboBoxes that are comboxClass and comboBoxSection, Should I use another thread to as I am using for Getbranch Method or there is any other clean method to achieve this. Following is my GetClasses method that I am calling in comboBoxBranches_SelectedValueChanged() event method of comboBoxBranches.
private void comboBoxBranches_SelectedValueChanged(object sender, EventArgs e)
{
Thread thread=new Thread(GetClasses());
thread.start();
}
private void GetClasses()// in this method how should I achieve invoking for multiple controls? What should I do here?
{
if (InvokeRequired)
{
comboBoxBranches.BeginInvoke(((MethodInvoker) delegate
{
Branch branch = comboBoxBranches.SelectedItem as Branch;
}));
comboBoxClasses.BeginInvoke((MethodInvoker) delegate
{
comboBoxClasses.DataSource = _schoolManagementSystemServiceClient.GetClasses(branch.BranchId);
comboBoxClasses.ValueMember = "ClassId";
comboBoxClasses.DisplayMember = "ClassName";
});
}
}
Same method is for comboxBoxSections whose value is based on both ComboBoxBranches and comboBoxClasses? I am new to multi-threading.
Invoking means waiting until the UI thread is idle, then switch to the UI thread and perform some actions. Therefore, the long running task (e.g. querying data from a database) must be performed before invoking takes place.
Today the preferred way to achieve this is to use async/await.
private async void comboBoxBranches_SelectedValueChanged(object sender, EventArgs e)
{
// We are in the UI thread and can access the controls directly.
Branch branch = comboBoxBranches.SelectedItem as Branch;
var classes = await Task.Run(
// This runs in a new thread. At this point the UI is not blocked.
() => _schoolManagementSystemServiceClient.GetClasses(branch.BranchId)
);
// Here the thread joins the UI thread and returns the classes.
// We are in the UI thread again. No need for Invoke.
comboBoxClasses.DataSource = classes;
comboBoxClasses.ValueMember = "ClassId";
comboBoxClasses.DisplayMember = "ClassName";
}
Note the keyword async in the method header. It tells C# to handle this method in a special way. Behind the scenes C# rewrites this method completely to make the magic happen and hides the complexity involved.
To understand how this works, you can imagine that C# puts the lines after the awaited task (the 3 lines with comboBoxClasses) into a callback method.
As explained in Async in depth (Microsoft) you also should rewrite GetClasses to work asynchronously and to return a Task<T> object, instead of starting a new thread here.
var classes = await _schoolManagementSystemServiceClient.GetClassesAsync(branch.BranchId);
See: Asynchronous programming (Microsoft).
Related
I've got this custom Task code:
public static async Task Run(this CustomForm parent, Action action)
{
parent.Enabled = false;
using (Caricamento form = new Caricamento())
{
form.TopLevel = true;
form.TopMost = true;
form.Show();
await Task.Run(action);
}
parent.Enabled = true;
}
The gif animation and the text inside the form just won't properly load until the async task finished .
ListMessaggi listForm = new ListMessaggi(ListMessaggi.Tipo.Entrata);
listForm.FormClosing += (o, args) =>
{
if (this.Controls.Count == 2)
{
args.Cancel = true;
}
};
listForm.FormBorderStyle = FormBorderStyle.None;
listForm.Dock = DockStyle.Fill;
listForm.TopLevel = false;
panel.Controls.Add(listForm);
listForm.Show();
And then, in the form which shows up upon listForm.Show() method call I've got:
Finally, this the result showing while the async task is running:
How can I improve the code to make things work properly?
Based on the additional information you provided in the comments, I think that you should try to convert your code to entirely use async / await. This will involve converting all your ADO.NET functions to the new async methods added to ADO in .NET 4.5. This will should eliminate all the Task.Run calls, as well as the messy InvokeRequired and BeginInvoke calls that you are doing to marshal control back to the UI thread.
I think you will find that, if properly implemented, you won't even need the special Run extension method; you will be able to have all your code in-line, just as in "traditional" .net development.
For example, you could use code like this to responsively load data, without locking the UI, in a form's Load event.
public async void Form1_Load(object sender, EventArgs e)
{
var data = await _dataProvider.GetSomeDataFromTheDatabase(aTotallyMadeUpVariable);
this.MyDataGrid.DataSource = data;
}
The exact same pattern works for the event handlers on comboboxes and buttons.
As a side note, event handlers in WinForms is practically the only place that async void methods are legitimately valid. Everything else, including async methods called from within event handler, should be Task returning functions.
As a bit more of a "primer" on async / await, this is how it avoids blocking the UI thread in my example above.
The async modifier on a function acts as a marker to the compiler to convert the method in to a state-machine. The compile will segment the code in the method, breaking it up at each await call, in to separate states. When the function is called, the first state is run (up to where the await is), and then the function returns to the caller. When the function that is being awaitted returns, the code after it (in the next state) is invoked as a continuation. Any data that is shared between the states, such as local variables, is moved off to an object that is passed in to each state's continuation.
I want to bind my combobox's data source using a binding list. The binding list contains a collection of Client instances. I managed this and it works well, but because reading from the database takes long I decided to use a task. And now once the binding list is updated via the task the combobox still has no values.
I wanted to use a normal thread at first, but struggled so switched to using a task(pretty much the same thing I guess thing I guess). So a solution using threads would be just as useful.
public partial class frmJobCreation : Form
{
public frmJobCreation()
{
InitializeComponent();
}
BindingList<Client> clients = new BindingList<Client>();
private void frmJobCreation_Load(object sender, EventArgs e)
{
cbxRtojClient.DataSource = clients;
Task.Run(() =>
{
clients = new BindingList<Client>(Client.GetClients());
});
}
}
Where Client.GetClients() is a static method that returns List<Client>
In the form load event you have cbxRtojClient.DataSource = clients; and then you are updating the clients object in the Task. But this is never going to update the ComboBox.
A Task in C# (wrt winforms) should ideally perform a task and return its results so that it can be updated in the UI. The UI update should ideally be done in a ContinueWith continuation task which is run in the UI context. We should always update any control in the context of the UI thread. Check this for reference.
Task.Run<BindingList<Client>>(() =>
{
// return all the clients to 'ContinueWith' task
return new BindingList<Client>(Client.GetClients());
})
.ContinueWith(t =>
{
// Result is a dynamic property and holds the output of its previous Task. BindingList<Client> in this case.
clients = t.Result;
// Update UI
cbxRtojClient.DataSource = clients;
}, TaskScheduler.FromCurrentSynchronizationContext()); // ensures calling the ContinueWith task to be run in the UI thread.
To know more about Task Parallel Library (TPL) follow this.
I have to process some records. For reasons that are unnecessary to mention, I can not loop through these records at the UI layer. The client wants to be able to simply call the middle tier using a function call, have the middle tier loop through and process the records. The problem is they want the middle tier to report back a status after each record is processed. How would I design that. For what it's worth, this is c# in .net.
A setup similar to this should work. It's untested/uncompiled so consider it pseudo-code. Also, it should ideally be asynchronous, but this will give you a starting point as an example of how to communicate changes back to the UI through eventing without the UI being aware of any "looping".
Event plumbing:
public class MyEventArgs : EventArgs
{
//add properties you want to send to the UI here.
}
public delegate void ProcessedEventHandler(object sender, MyEventArgs e);
Middle tier raises events.
public class MiddleTier
{
public event ProcessedEventHandler RecordProcessed;
//NOTE it would be best to make a tweak to do this asynchronously
//such that all records can be processed at the same time instead
//of processing them sequentially. if the method were async, you
//could do all of this without the method itself blocking.
public void Process()
{
//this loop/processing should ideally be asynchronous
foreach(var thing in whatever)
{
//process thing
//make event args
var args = new MyEventArgs(); //fill out properties
//raise event
OnProcessed(args);
}
private void OnProcessed(MyEventArgs args)
{
//follow this pattern for thread safety
var p = RecordProcessed;
if(p != null)
p(this, args);
}
}
}
Then in your UI layer:
//in some UI function
var mt = new MiddleTier();
//handle event
mt.RecordProcessed +=
(s, args) =>
{
//update UI
};
//kick things off
mt.Process();
You don't mention what technology your UI will be but assuming it is an application, you want the processing to happen on a separate thread so as to allow your UI to update.
I would look at the backgroundworker component as a starting point. It facilitates a progresschanged event you can use to notify your UI of how it getting on. Similar can be achieved using asynchronous framework.
in my app I have two methods that load data from csv. These methods are loadData() and loadOtherData(). I want to run them in parallel so I have two threads to run them.
The loadData() method also populate one datagridview, while the data loaded by the loadOtherData() method are stored in a dictionary myDictionary.
After data loading is completed (so the methods running in the two threads have finished) I want to call another method updateGrids() that takes myDictionary as argument.
For updateGrids() to run properly, both loadData() and loadOtherData() must have run successfully cause otherwise the method doesn't have the data to work on.
How can I call method updateGrids() only when the other methods have terminated in other threads?
The structure is as follow:
private void loadData_Click_1(object sender, EventArgs e)
{
ThreadStart thread1Start = new ThreadStart(loadData); // load data and fill the first datagridview
ThreadStart thread2Start = new ThreadStart(loadOtherData); // load data and fill myDictionary
Thread t1 = new Thread(thread1Start);
Thread t2 = new Thread(thread2Start);
t1.Start();
t2.Start();
updateGrids(myDictionary); // call this method to update remaining datagridviews
}
private void updateGrids(Dictionary<string, double> myDictionary)
{
// update the remaining datagridviews by using
// information in the first datagridview and in myDictionary
}
But if I run this, I get the error when updateGrids() since it doesn't have the data to work with. How can I easily change the code to make it work?
You could stop using ThreadStart objects directly and use Tasks instead - have them returned from your loadData and loadOtherData methods.
You have either two options here:
Either Use Task.WaitAll if you're using .NET 4.0
or (more preferable)....
Make loadData and loadOtherData async in .NET 4.5 and above (or use the async BCL package in .NET 4.0). The when you call the methods, you can cache the Task objects they return and await a call to Task.WhenAll
public async Task LoadData(){
}
public async Task LoadOtherData() {
}
private async void loadData_Click_1(object sender, EventArgs e)
{
var loadDataTask = LoadData();
var loadOtherDataTask = LoadOtherData();
await Task.WhenAll(loadDataTask, loadOtherDataTask);
updateGrids(myDictionary);
}
You can use async/await to wait both tasks without blocking the UI
async private void loadData_Click_1(object sender, EventArgs e)
{
await Task.WhenAll(Task.Run(() => loadData()),
Task.Run(() => loadOtherData()));
updateGrids(myDictionary);
}
After you start your threads, you want to wait until they are done. So add these lines of code:
t1.Join();
t2.Join();
Join will cause the current thread to sleep until the thread being joined is complete.
Based on comments, I'm assuming this is WinForms. In this case Join will block the UI thread. You could create a third thread that will join the other two threads and then call Invoke to call your update method.
Since you're using .Net 4.5., you could use Async / Await; add the async modifier to your click method event handler and uses Tasks instead of Threads directly and await them. Other answers cover that, and you can see this blog for other details too.
[Windows forms application & .NET 4.0]
I need to execute database access methods that return objects (either list of classes or simple classes).
Also i need to open forms that are responsive while main thread does initialization.
I need to run these on separate threads keeping the User Interface responsive and of course to be able to pass the results back to main thread for UI updates.
I have been reading books regarding the various ways for this.
I understand that my job can be done by:
BackGroundWorker
Thread Class
Task Class
Which one i should dive into ?
Update: using the suggested Task class i am getting errot for cross thread safety using this:
private void BtnCheckClick(object sender, EventArgs e)
{
var itm = Task<JDEItemLotAvailability>.Factory.StartNew(() =>
Dal.GetLotAvailabilityF41021(
txtLot.Text,
cmbMcu.SelectedItem.ToString(),
cmbLocn.SelectedItem.ToString())
);
lblDescriptionValue.Text = itm.Result.Description;
lblItemCodeValue.Text = itm.Result.Code;
lblQuantityValue.Text = itm.Result.AvailableQuantity.ToString();
LotFocus(true);
}
On the above exmaple i am getting the exception in cmbMcu control not the txtLot.
I would use the Task class, it's really easy to synchronize it and it already provides a support for returning objects.
var task = Task.Factory.StartNew(
() => GetDatabaseData(someArguments),
TaskCreationOptions.LongRunning);
// Example method
public DataSet GetDatabaseData(object args) { ... }
this this tells a scheduler to create and begin a new task and gives it a hint that it might be a good idea not to use a thread-pool thread, if the scheduler uses a thread-pool. Anyway you can now decide how do you want to synchronize.
For example to achieve similar behaviour as in Gregor Primar's answer, you can set up a continuation using ContinueWith method as follows,
task.ContinueWith(oldTask => ProcessReturnedData(oldTask.Result));
// Example method
public IEnumerable<SomeEntity> ProcessReturnedData(DataSet data) { ... }
which will schedule calling the ProcessReturnedData method after the task object has done executing. Note that this will be called even if task fails for some reason, so it may not be always a good solution - or you would have to do some checks in the provided delegate.
If you want to do a non-blocking wait on the main thread and use the returned object there, you can simply use the Wait method.
task.Wait(); // Makes current thread wait until the task is comnpleted.
DataSet result = task.Result; // Accessing the result object.
I hade done a lot of projects using Thread, however Task should be more easy to use.
Here is demo how make async operations using Threads.
This is the class that will return data to ui:
public class MyAsyncClass
{
public delegate void NotifyComplete(DataSet data);
public event NotifyComplete NotifyCompleteEvent;
//Starts async thread...
public void Start()
{
System.Threading.Thread t = new System.Threading.Thread(new System.Threading.ThreadStart(DoSomeJob));
t.Start();
}
void DoSomeJob()
{
//just wait 5 sec for nothing special...
System.Threading.Thread.Sleep(5000);
if (NotifyCompleteEvent != null)
{
//TODO: fill your data...
DataSet ds = new System.Data.DataSet();
NotifyCompleteEvent(ds);
}
}
}
And here is ui implementation:
MyAsyncClass myClass = null;
private void button2_Click(object sender, EventArgs e)
{
myClass = new MyAsyncClass();
myClass.NotifyCompleteEvent += new MyAsyncClass.NotifyComplete(myClass_NotifyCompleteEvent);
//here I start the job inside working class...
myClass.Start();
}
//here my class is notified from working class when job is completed...
delegate void myClassDelegate(DataSet data);
void myClass_NotifyCompleteEvent(DataSet data)
{
if (this.InvokeRequired)
{
Delegate d = new myClassDelegate(myClass_NotifyCompleteEvent);
this.Invoke(d, new object[] { data });
}
else
{
//TODO: show your data
MessageBox.Show("Data retrieved!");
}
}