WPF SelectionChanged event do the action before tab change - c#

i have SelectionChanged event in my WPF aplication. i want that when the tab is change to do some action but first i want the tab to visualy change before the action starts. i am using background worker to do the job. my code is:
private void Tab_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (tab1.IsSelected)
{
//this line is not working
tabcontrol.SelectedIndex = 1;
wNetTest = new BackgroundWorker();
wNetTest.DoWork += new DoWorkEventHandler(worker_DoWork);
wNetTest.RunWorkerCompleted += worker_RunWorkerCompleted;
wNetTest.WorkerReportsProgress = true;
wNetTest.WorkerSupportsCancellation = true;
wNetTest.RunWorkerAsync();
}
}
void worker_DoWork(object sender, DoWorkEventArgs e)
{
//do the job
}

Your problem is that your code is running synchronously. Therefore, every line of your Tab_SelectionChanged event handler will run before you will see the TabItem change. To fix this problem, you just need to run your long running process asynchronously. One of the simplest ways to do that is this:
private void Tab_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (tab1.IsSelected)
{
//this line is not working
tabcontrol.SelectedIndex = 1;
Task.Factory.StartNew(() => LongRunningMethod(parameter));
}
}
private void LongRunningMethod(object parameter)
{
// perform long running process here
}
The parameter input parameter is optional... just remove it if you don't need it.

Related

Multithreading with events in WPF

UPDATE:
As mentioned in the comments section the problem was solved, but I do not understand why my way of implementation was wrong.
I have this situation:
I have a device which can be triggered with an event in my WPF project.
This event pulls Data from my device at a polling rate of 1ms. I want to process
the data in different threads.
My approach was, to start a backgroundworker which registers the device event (I read events run on the thread they are called from). In the device event itself the data is saved to an object, which is declared in my form. After that the labels in the WPF form a are refreshed with a Invoke Method.
This happens until someone presses cancel on a button in my form, which unregisters the device event and stops the thread.
Here is some code I use:
Declaration in my Main Window:
public partial class MainWindow : Window
{
private BackgroundWorker worker = new BackgroundWorker();
private MeasureObject mObject = new MeasureObject();
... }
This is my initialization:
public MainWindow()
{
InitializeComponent();
this.worker.WorkerSupportsCancellation = true;
this.worker.DoWork += worker_DoWork;
this.worker.RunWorkerCompleted += worker_RunWorkerCompleted;
}
If this button is press i run my background worker:
private void btnStartMeasure_Click(object sender, RoutedEventArgs e)
{
this.worker.RunWorkerAsync();
}
Here I register my event for the device. It should now run on my worker thread. I tried to declare the event itself here too, but it did not work, so I placed it in my main windows.
private void worker_DoWork(object sender, DoWorkEventArgs e)
{
this.myController.ControlCenter.Diagnostics.NewDiagPacketArrived += new EventHandler<NewDiagPacketArrivedEventArgs>(Diagnostics_NewDiagPacketArrived);
// run all background tasks here
}
This is not needed and empty. The worker is only cancelled if the user sets it on cancel.
private void worker_RunWorkerCompleted(object sender,
RunWorkerCompletedEventArgs e)
{
}
This event is triggered in my Window and calls two functions, it should run on the backgroundworker if I am correct.
private void Diagnostics_NewDiagPacketArrived(object sender, NewDiagPacketArrivedEventArgs e)
{
try
{
Measure(e);
this.Dispatcher.BeginInvoke( new Action(() => { SetStates(e); }),System.Windows.Threading.DispatcherPriority.Input);
}
catch
{
}
}
Measure gets the e Object from the device and saves it to a Dataobject i created
private void Measure(NewDiagPacketArrivedEventArgs e)
{
lock(this.mObject)
{
this.mObject.ID = this.list.Count;
....
this.list.Add(this.mObject);
}
}
SetStates refreshed the GUI
private void SetStates(NewDiagPacketArrivedEventArgs e)
{
lock(this.mObject)
{
this.lblID.Content = this.mObject.ID;
}
}
The problem with my code is if I cancel the event and the thread with this code:
private void btnStopMeasure_Click(object sender, RoutedEventArgs e)
{
this.myController.ControlCenter.Diagnostics.NewDiagPacketArrived -= Diagnostics_NewDiagPacketArrived;
this.worker.CancelAsync();
}
And try to get the list where I added my objects, all objects have the same ID's and values. It seems like as soon as I unregister the event or press the stop measure button, all mObjects in my list get overwritten with the mObject at the time when I unregister the event.
so my list looks like this:
list[0].ID = 1
list[1].ID = 1
list[2].ID = 1
rather than this:
list[0].ID = 1
list[1].ID = 2
list[2].ID = 3
Maybe you can help?
Your problem is that you are not creating a new instance of the mObject - you only create one of them here:
private MeasureObject mObject = new MeasureObject();
Your code then adds the SAME object to the list, and updates that. You need to make a new object each time and put it in the list.
this.mObject.ID = this.list.Count;
....
this.list.Add(this.mObject);
Currently your list is a list of the same object.

picturebox opening too late

I want to show "Loading.., please wait" gif by getting content from web.
I have tried the following code, but Picturebox opening too late.
private void buttonStart_Click(object sender, EventArgs e)
{
pictureBox1.Visible = true;
webSatList = new WebSatelliteList(this, XMLSatList, name);
webSatList.LoadTPList();
TPListToBeAdded = webSatList.GetTPListToBeAdded();
TPListToBeRemoved = webSatList.GetTPListToBeRemoved();
drawTPListGridView(TPListToBeAdded, TPListToBeRemoved);
}
public void drawTPListGridView(List<TPInfo> TPListToBeAdded, List<TPInfo> TPListToBeRemoved)
{
pictureBox1.Visible = false;
//draw TP List ..
}
Picturebox is openning after this line:
"TPListToBeRemoved = webSatList.GetTPListToBeRemoved();"
I have tried to fix this problem by using backgroundworker (the following code) and the same problem has been seen. Also, I have used the popup form instead of PictureBox nothing has changed.
private void buttonStart_Click(object sender, EventArgs e)
{
pictureBox1.Visible = true;
backgroundWorker1.DoWork += new DoWorkEventHandler(backgroundWorker1_DoWork);
backgroundWorker1.RunWorkerCompleted += new RunWorkerCompletedEventHandler(backgroundWorker1_RunWorkerCompleted);
backgroundWorker1.RunWorkerAsync();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
webSatList = new WebSatelliteList(this, XMLSatList, name);
webSatList.LoadTPList();
TPListToBeAdded = webSatList.GetTPListToBeAdded();
TPListToBeRemoved = webSatList.GetTPListToBeRemoved();
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
drawTPListGridView(TPListToBeAdded, TPListToBeRemoved);
}
public void drawTPListGridView(List<TPInfo> TPListToBeAdded, List<TPInfo> TPListToBeRemoved)
{
pictureBox1.Visible = false;
//draw TP List ..
}
How can i fix this problem? Any help would be appreciated.
Not entirely sure what you're trying to do here, but chances are you want to implement the async / await pattern.
Mark your button press as async
private async void buttonStart_Click(object sender, EventArgs e)
For anything that you need to wait for should then be awaited and it will keep your form redrawing so it doesn't freeze up. For example, something like:
await Task.Run(() => loadPictureBox());
Or you could make your loadpicturebox method asynchronous by giving it a signature of async Task.
The problem you're likely having is that the form will not update or refresh itself until the click method has exited. This means if you first make it display a loading image, and then load the next image in the same method that the form will freeze up until both operations have finished and the method has exited.
The async await pattern means that while it's doing some processing or whatever, let windows carry on drawing the form and handling actions on it.

Event not raising properly in forms

I have a MainWindow with eventhandler which is not working properly. I have made simple model of this problem. Please see comment in code where the problem is:
public partial class MainWindow : Window
{
public event EventHandler Event1;
public MainWindow()
{
Event1 += MainWindow_Event1;
InitializeComponent();
}
void MainWindow_Event1(object sender, EventArgs e)
{
textBox1.Text = "wth!?"; //Not changing text box. Not showing message. If delete this line, it will work fine
MessageBox.Show("raised");
}
private void bw_DoWork(object sender, DoWorkEventArgs e)
{
EventHandler evt = Event1;
while (true)
{
Thread.Sleep(500);
evt(null, null);
}
}
private void Button_Click_1(object sender, RoutedEventArgs e)
{
BackgroundWorker bw = new BackgroundWorker();
bw.DoWork += bw_DoWork;
bw.RunWorkerAsync();
}
}
Please explain this behavior and how can I fix it?
The problem is that you're invoking the event from a background thread. This will not work and the program is simply hanging when trying to access the TextBox. However, if you change this code:
textBox1.Text = "wth!?"; //Not changing text box. Not showing message. If delete this line, it will work fine
MessageBox.Show("raised");
to this:
this.Dispatcher.BeginInvoke((Action)delegate()
{
textBox1.Text = "wth!?"; //Not changing text box. Not showing message. If delete this line, it will work fine
MessageBox.Show("raised");
});
it'll work for you.
You can't update the UI elements from the background thread.
The worker thread fails by exception trying to access the UI element (Text property). So messageBox isn't showing as well. Use notification mechanisms, or Dispatcher calls (there is a wast amount of information like this on the web)
Here are possible duplicates/help:
Update GUI using BackgroundWorker
Update GUI from background worker or event
This problem is because you need to use the Synchronization Context of the current Thread for comunicating between threads, some thing like this
private void Button_Click(object sender, RoutedEventArgs e)
{
var sync = SynchronizationContext.Current;
BackgroundWorker w = new BackgroundWorker();
w.DoWork+=(_, __)=>
{
//Do some delayed thing, that doesn't update the view
sync.Post(p => { /*Do things that update the view*/}, null);
};
w.RunWorkerAsync();
}
Please check this question, hope can helps...

C# Threading, 1st Task DoSomething, 2nd Task update a WinFormLabel (Realtime)?

Lets say I have Task 1:
private void Task1()
{
//Here is some Code, could be any "longer" Task -
//For Example: Grab all words from a .txt File and fill in a List<String>
}
Then I have an other Task 2:
private void Task2(string word)
{
//So lets say theres a Label on my WinForm..
//Now While Task1 is grabbing the words, Task2 should fill a Label
//with the added 'word' (parameter) - (Task2 will be called from Task1
}
Actually I don't know how to make this possible, or whats the best way. On the UI I should be able to see the Label.Text changing (every word).. So I need to make a second Thread? How could I do this? Maybe someone could help me, cheers
UPDATE:
I tried it now with the Backgroundworker, but something seems to be false.. its actually not working, nothing happens on the form
Code:
public void CreateAndSaveAMatch(DateTime date) //That method is being called several times
{
//HERE IS CODE, WHICH CREATES AND SAVES A MATCH
// Start the asynchronous operation.
backgroundWorker1.RunWorkerAsync(date);
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
backgroundWorker1.ReportProgress(0, Convert.ToDateTime(e.Argument).ToShortDateString());
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
label1.Text = (string)e.UserState; //here on the Label I would like to show the Date
}
Ok, try this. This is a simple example that will show you how to solve your problem using BackgroundWorker. Also note that there are many other solutions. To use this example create a Form in a new project that only has a button and a label. Also note that this is a supplement of the other answers which were correct.
public partial class Form1 : Form
{
BackgroundWorker createAndSaveAMatchBGW;
public Form1()
{
InitializeComponent();
createAndSaveAMatchBGW = new BackgroundWorker();
createAndSaveAMatchBGW.DoWork += new DoWorkEventHandler(createAndSaveAMatchBGW_DoWork);
createAndSaveAMatchBGW.ProgressChanged += new ProgressChangedEventHandler(createAndSaveAMatchBGW_ProgressChanged);
createAndSaveAMatchBGW.RunWorkerCompleted += new RunWorkerCompletedEventHandler(createAndSaveAMatchBGW_RunWorkerCompleted);
createAndSaveAMatchBGW.WorkerReportsProgress = true;
}
private void button1_Click(object sender, EventArgs e)
{
createAndSaveAMatchBGW.RunWorkerAsync(DateTime.Now);
}
void createAndSaveAMatchBGW_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
MessageBox.Show("BackgroundWorker finished");
}
void createAndSaveAMatchBGW_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
label1.Text = ((DateTime)e.UserState).ToString("ss");
}
void createAndSaveAMatchBGW_DoWork(object sender, DoWorkEventArgs e)
{
//BackgroundWorker does something for a 10 seconds, each second it Reports
BackgroundWorker bgw = (BackgroundWorker)sender;
DateTime dt = (DateTime) e.Argument;
for (int i = 0; i < 10; i++)
{
Thread.Sleep(1000);
dt = dt.AddSeconds(1);
bgw.ReportProgress(0, dt);
}
}
}
And if you report from CreateAndSave... method only once per its execution, then you can use this code:
BackgroundWorker createAndSaveAMatchBGW;
public Form1()
{
InitializeComponent();
createAndSaveAMatchBGW = new BackgroundWorker();
createAndSaveAMatchBGW.DoWork += new DoWorkEventHandler(createAndSaveAMatchBGW_DoWork);
createAndSaveAMatchBGW.RunWorkerCompleted += new RunWorkerCompletedEventHandler(createAndSaveAMatchBGW_RunWorkerCompleted);
}
private void button1_Click(object sender, EventArgs e)
{
createAndSaveAMatchBGW.RunWorkerAsync(DateTime.Now);
}
void createAndSaveAMatchBGW_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
label1.Text = ((DateTime)e.Result).ToString();
}
void createAndSaveAMatchBGW_DoWork(object sender, DoWorkEventArgs e)
{
DateTime dt = (DateTime) e.Argument;
//you do something with your DateTime
dt = dt.AddDays(10);
e.Result = dt;
}
Use BackgroundWorker for reporting progress from first task. Drag this component from toolbox to your form, and subscribe to DoWork and ProgressChanged events. Also set property WorkerReportsProgress to true. Then start you first task asynchronously:
// this will execute code in `DoWork` event handler
backgroundWorker1.RunWorkerAsync();
Next - use userState object to pass processed words:
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
// grab words in a loop and report progress
backgroundWorker1.ReportProgress(0, word);
}
And last step - update label in ProgressChanged event handler
void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
label1.Text += (string)e.UserState; // this is your grabbed word
}
The simplest way to achieve this kind of thing is using BackgroundWorker.
http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx
BackgroundWorker automatically handles thread marshalling and provides events that allow you to update the UI. The event handlers run on the UI thread.
The things you do in Task1 could be moved into a BackgroundWorker, and the updates to the UI that you propose to do in Task2 can actually be in response to progress events from BackgroundWorker.
ProgressChangedEventArgs provides for user-defined data that could hold the current word.
However, Winforms (and indeed pretty much any UI) will not be able to keep up with a separate CPU thread just loading words from a file if you intend to show every word you load.
Task1 could be started on a separate thread.
You wouldn't actually need a Task2 unless there was some complex logic being performed to update the TextBox. You you really need to do is use TextBox.Invoke() to invoke the update on the UI Thread from Task1.

Pause and Resume a For-Loop in C#

I'm working on a windows App in C#, I have a for-loop which update something in a loop, and I have 3 buttons on the form named "Stop,Pause,Resume". So the purpose is as same as the buttons named. Does anyone know how to do this?
Here is the Loop
private void btnCompleteAuto_Click(object sender, EventArgs e)
{
setGeneralValue();
for (int i = 1; i <= autoGridView.Rows.Count - 1; i++)
{
if (SRP == "Pause") // this is what I was thinking but it won't work
{ // it will step into end-less loop
do // how to stop this loop on "Resume" button click
{
}while(SRP!="Resume")
}
car = false;
try
{
MemberID = Convert.ToInt64(autoGridView.Rows[0].Cells["Member_ID"].Value);
DispID = Convert.ToString(autoGridView.Rows[0].Cells["Disp_Id"].Value);
Mobile = Convert.ToString(autoGridView.Rows[0].Cells["Mobile"].Value);
DueDate = Convert.ToString(autoGridView.Rows[0].Cells["Due_Date"].Value);
}
catch (Exception)
{
MessageBox.Show("Row Not Found");
}
AutoRecharge(network_name, pack_name, Mobile, Mobile, Convert.ToString(autoGridView.Rows[0].Cells["Rck_Amt"].Value), vendor_id, vendor_pwd, pack_id, oxinetwork_id);
autoGridView.Rows.RemoveAt(0);
}
}
Here are the 3 button events in which I'm setting a variable
private void btnPause_Click(object sender, EventArgs e)
{
SRP = "Pause";
}
private void btnStop_Click(object sender, EventArgs e)
{
SRP = "Stop";
autoGridView.DataSource = "";
}
private void btnResume_Click(object sender, EventArgs e)
{
SRP = "Resume";
}
The reason this doesn't work as you expect is this:
A Windows Forms application uses a single UI thread, which continually processes incoming messages from a queue. Any event handlers you attach to the events of a Windows Forms control get sent to this queue and processed by the UI thread as quickly as possible.
Your btnCompleteAuto_Click is one such handler. Once it starts, nothing else will be processed by the UI thread until it exits. Thus any other handlers you attach to other events (btnPause_Click, btnStop_Click, etc.) must wait their turn, as they will run on the same (UI) thread.
If you want pause/resume functionality, this has to be achieved on a separate thread.
A possible way to implement it might be to use a BackgroundWorker, as suggested by saurabh.
Here is a rough sketch of what your updated code might look like (I have not even attempted to compile this, let alone debug it; it's intended only as a basic outline of how you might accomplish this functionality).
You need to be aware, however, that accessing UI controls directly from a non-UI thread is a no-no. Use a mechanism such as the BackgroundWorker.ProgressChanged event to handle any UI updates that you need to happen based on activity on a non-UI thread.
ManualResetEvent _busy = new ManualResetEvent(false);
private void btnCompleteAuto_Click(object sender, EventArgs e)
{
if (!backgroundWorker.IsBusy)
{
_busy.Set();
btnAutoCompleteAuto.Text = "Pause";
backgroundWorker.RunWorkerAsync();
}
else
{
_busy.Reset();
btnAutoCompleteAuto.Text = "Resume";
}
btnStop.Enabled = true;
}
private void btnStop_Click(object sender, EventArgs e)
{
_busy.Set();
backgroundWorker.CancelAsync();
}
private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
// for (something)
// {
_busy.WaitOne();
if (backgroundWorker.CancellationPending)
{
return;
}
// Do your work here.
// }
}
private void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
_busy.Reset();
btnAutoCompleteAuto.Text = "Start";
btnStop.Enabled = false;
}
After Reading your actual requirement in our comment , i would suggest that use Background worker class which supports cancellation of running process.
See here

Categories