WPF Backgroundwork er call method on DoWork event - wpf

I have a BackgroundWorker on my WPF UserControl.
private readonly BackgroundWorker worker = new BackgroundWorker();
public ucCustomer()
{
InitializeComponent();
worker.DoWork += worker_DoWork;
worker.RunWorkerCompleted += worker_RunWorkerCompleted;
}
private void worker_DoWork(object sender, DoWorkEventArgs e)
{
// run all background tasks here
System.Threading.Thread.Sleep(10000);
}
private void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
//update ui once worker complete his work
}
private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
worker.RunWorkerAsync();
}
Above code is work, the UI is response when the task is working, but if i change the worker_DoWork() to
private void worker_DoWork(object sender, DoWorkEventArgs e)
{
// run all background tasks here
Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Background,
new Action(() => {
gridDataBind(); //A long data-mining task,using Dispatcher.Invoke() to access UI.
}));
}
private void gridDataBind()
{
SnEntities sn = new SnEntities();
var customers = from c in sn.Customer select c;
dgCustomer.ItemsSource = customers.ToList();
}
The UI is freeze until the task is end.
Is it any solution?
Thanks you.

Try setting the ItemsSource like below code:
private void worker_DoWork(object sender, DoWorkEventArgs e)
{
// run all background tasks here
e.Result = gridDataBind(); //A long data-mining task.
}
private IList<Customer> gridDataBind()
{
SnEntities sn = new SnEntities();
var customers = from c in sn.Customer select c;
return customers.ToList();
}
private void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
var customers = e.Result as IList<Customer>;
ObservableCollection<Customer> gridItemsSource = new ObservableCollection<Customer>();
Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Background,
new Action(() =>
{
dgCustomer.ItemsSource = gridItemsSource;
}));
foreach(var customer in customers)
{
Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Background,
new Action(() =>
{
gridItemsSource.Add(customer);
}));
}
}

Store your data in e.result at worker_DoWork and update UI at the worker_RunWorkerCompleted.
in that case UI will be free when data will coming from database.

Try this, it should help you
Application.Current.Dispatcher.BeginInvoke(
DispatcherPriority.Background,
new Action(() => gridDataBind();));

Related

background operation of user controller

I have prepared more than one UserControl for a windows program in XAML. Each user control works as a separate page. But I do page transitions in navigate class. In ".xaml.cs" when calling User control
Navigate.navigate (navigate_grid, new DeviceLayout ());
I'm using the line of code. But every time I create a new user control, the background functions don't work. How do I flip one instead of invoking a new user control each time?
class Navigate
{
public static void navigate(Grid grd, UserControl uc)
{
if (grd.Children.Count > 0)
{
grd.Children.Clear();
grd.Children.Add(uc);
}
else { grd.Children.Add(uc); }
}
}
Example navigate:
public SettingsView()
{
InitializeComponent();
Navigate.navigate(navigate_grid, new SystemLayout());
}
private void system_button_click(object sender, RoutedEventArgs e)
{
Navigate.navigate(navigate_grid, new SystemLayout());
previous_page.Text = "";
current_page.Text = "SİSTEM";
next_page.Text = "UYGULAMA";
}
private void application_button_click(object sender, RoutedEventArgs e)
{
Navigate.navigate(navigate_grid, new ApplicationLayout());
previous_page.Text = "SİSTEM";
current_page.Text = "UYGULAMA";
next_page.Text = "BAĞLANTI";
}
private void connection_button_click(object sender, RoutedEventArgs e)
{
Navigate.navigate(navigate_grid, new ConnectionLayout());
previous_page.Text = "UYGULAMA";
current_page.Text = "BAĞLANTI";
next_page.Text = "ÜRÜNLER";
}
private void product_button_click(object sender, RoutedEventArgs e)
{
Navigate.navigate(navigate_grid, new ProductsLayout());
previous_page.Text = "BAĞLANTI";
current_page.Text = "ÜRÜNLER";
next_page.Text = "CİHAZLAR";
}
private void device_button_click(object sender, RoutedEventArgs e)
{
Navigate.navigate(navigate_grid, new DeviceLayout());
previous_page.Text = "ÜRÜNLER";
current_page.Text = "CİHAZLAR";
next_page.Text = "YAZICILAR";
}
private void printer_button_click(object sender, RoutedEventArgs e)
{
Navigate.navigate(navigate_grid, new PrinterLayout ());
previous_page.Text = "CİHAZLAR";
current_page.Text = "YAZICILAR";
next_page.Text = "KULLANICILAR";
}
private void users_button_click(object sender, RoutedEventArgs e)
{
Navigate.navigate(navigate_grid, new UsersLayout());
previous_page.Text = "YAZICILAR";
current_page.Text = "KULLANICILAR";
next_page.Text = "BAKIM";
}
private void maintenance_button_click(object sender, RoutedEventArgs e)
{
Navigate.navigate(navigate_grid, new MaintenanceLayout());
previous_page.Text = "KULLANICILAR";
current_page.Text = "BAKIM";
next_page.Text = "HAKKINDA";
}
private void info_button_click(object sender, RoutedEventArgs e)
{
Navigate.navigate(navigate_grid, new InfoLayout());
previous_page.Text = "BAKIM";
current_page.Text = "HAKKINDA";
next_page.Text = "";
}
}
Not sure if I get what you mean.
When you change the UserControl with the use of any of those functions eg info_button_click you can't access this funtion anymore.
That would be the case, because your XAML and .cs file are one class, containing those funcitons. If you change the UserControl (XAML) you will also change the .cs file. Therefore you can't access those functions anymore.
You could probably get the behaviour you want if you bind the commands to a viewmodel, which you could then pass through the navigation as well?
Sry, I'm still not sure what exactly it is you're doing.

WPF Check the window availability in different UI thread?

I had created a new window in a new thread as following:
private WindowNew windowNew;
private void Button_Click(object sender, RoutedEventArgs routedEventArgs)
{
Thread newWindowThread = new Thread(() =>
{
SynchronizationContext.SetSynchronizationContext(new DispatcherSynchronizationContext(Dispatcher.CurrentDispatcher));
windowNew = new WindowNew();
windowNew.Show();
windowNew.Closed += (s, e) => Dispatcher.CurrentDispatcher.BeginInvokeShutdown(DispatcherPriority.Background);
Dispatcher.Run();
});
newWindowThread.SetApartmentState(ApartmentState.STA);
newWindowThread.IsBackground = true;
newWindowThread.Start();
}
How can I check the instance availability of windowNew?
private void Button_Click(object sender, RoutedEventArgs routedEventArgs)
{
if(windowNew!=null) return;
...
windowNew.Closed += (s, e) =>
{
Dispatcher.CurrentDispatcher.BeginInvokeShutdown(DispatcherPriority.Background);
windowNew = null;
}
}
Also you have to care about the synchronization(if statement and Closed event handler)

How can I update my WinForm label on timer?

I can't update my WinForm label properties.
Details: I am trying to check my database and get some values posted, but I can't even update a mere label it seems. I'm using SharpDevelop.
The code:
//this is my form
public partial class MainForm : Form
{
//Declaring timer
public static System.Timers.Timer aTimer = new System.Timers.Timer();
public MainForm()
{
InitializeComponent();
//Timer
aTimer.Elapsed +=new ElapsedEventHandler(OnTimedEvent);
aTimer.Interval = 2000; //milisecunde
aTimer.Enabled = true;
label1.Text="some_text";
}
private static void OnTimedEvent(object source, ElapsedEventArgs e) {Check();}
public static void Check()
{
//Database checks here..
try{label1.Text="new_text";}catch(Exception e) {MessageBox.Show(e.ToString());}
MessageBox.Show("BAAAA");
}
void Button1Click(object sender, EventArgs e)
{
label1.Text = "mergeeeeee?!";
}
}
EDIT: I've removed all static modifiers. Also updated the post with the new code (try catch is added and the messagebox after it + a button that changes the label).
The try catches the following error:
. Really could use some help, been researching answers for more than 6 hours.
Try this (use a System.Windows.Forms.Timer instead of System.Timers.Timer):
//Declaring timer
public System.Windows.Forms.Timer aTimer = new System.Windows.Forms.Timer();
public Form1()
{
InitializeComponent();
//Timer
aTimer.Tick += aTimer_Tick;
aTimer.Interval = 2000; //milisecunde
aTimer.Enabled = true;
label1.Text = "some_text";
}
void aTimer_Tick(object sender, EventArgs e)
{
Check();
}
public void Check()
{
try
{
//Database checks here..
label1.Text = string.Format("new_text {0}", DateTime.Now.ToLongTimeString());
}
catch (Exception ex)
{
throw ex;
}
MessageBox.Show("BAAAA");
}
The Elapsed event of the System.Timers.Timer is fired on a non-UI thread (change your original code to not swallow exceptions and you should see the cross-thread exception).
I used the following code for my project and it worked.
It has a button to activate the timer and the timer raises an event when 500 milliseconds passed.
private void ActiveTimer_Click(object sender, EventArgs e)
{
EnableTimer();
}
private void EnableTimer()
{
System.Timers.Timer raiseTimer = new System.Timers.Timer();
raiseTimer.Interval = 500;
raiseTimer.Elapsed += RaiseTimerEvent;
raiseTimer.AutoReset = true;
raiseTimer.Enabled = true;
}
private void RaiseTimerEvent(object sender, System.Timers.ElapsedEventArgs e)
{
this.Invoke(new Action(() =>
{
label1.Text += "500 ms passed\n";
}));
}

Close form after raising an event

I have a mainform that opens a StartForm at some point:
private void TestChosen(object sender, TestChosenEventArgs e)
{
var frm = new TestStartForm(e.Test, StartTest);
frm.ShowDialog();
}
On this StartForm I have a button to actually start the test.
public TestStartForm(Test test, StartTestHandler startTestHandler)
{
InitializeComponent();
_test = test;
OnStartTest = startTestHandler;
}
public delegate void StartTestHandler(object sender, StartTestEventArgs e);
public event StartTestHandler OnStartTest;
public void InvokeOnStartTest(StartTestEventArgs e)
{
StartTestHandler handler = OnStartTest;
if (handler != null) handler(this, e);
}
private void btnStart_Click(object sender, EventArgs e)
{
InvokeOnStartTest(new StartTestEventArgs(_test));
Close();
}
The problem I'm facing is that the StartForm stays open untill the work of the StartTestHandler is completely done.
In the StartTestHandler another form is opened with the actual test.
Is there a way to force the StartForm to close without waiting for the test to be finnished?
EDIT
As #cowboydan suggested I've used BeginInvoke to show the form.However, I had to do it for the StartForm as well as the actual TestForm before it worked properly.
private void TestChosen(object sender, TestChosenEventArgs e)
{
BeginInvoke((Action)delegate
{
var frm = new TestStartForm(e.Test, StartTest);
frm.ShowDialog();
});
}
private void StartTest(object sender, StartTestEventArgs e)
{
BeginInvoke((Action)delegate
{
var frm = new TestForm(e.Test);
frm.ShowDialog();
});
}
You could use BeginInvoke which would launch StartForm in a separate thread from the ThreadPool. This is a fairly common practice.

Cannot Get Threading right on wpf UI

I am building a proof of concept application before it gets rollout to the real one.
Scenario
I should be able to stop processing in the middle of it.
Toolbar 2 buttons "Start" & "Stop"
User press start and it process a long running task.
User decides out of the blue to stop the task.
I cannot seem to get threading right!! I cannot press stop as it's waiting for the long running task as if the long running task is actually running on UI thread and not as intented on background thread.
What Am I doing wrong can you spot it? Thanks for your help
public partial class TestView : UserControl
{
private readonly BackgroundWorker _worker;
public TestView
{
InitializeComponent();
_worker = new BackgroundWorker();
_worker.RunWorkerCompleted += RunWorkerCompleted;
_worker.DoWork+=DoWork;
_worker.WorkerReportsProgress = true;
_worker.ProgressChanged+=_worker_ProgressChanged;
_worker.WorkerSupportsCancellation = true;
}
static void RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Cancelled)
{
MessageBox.Show("The task has been cancelled");
}
else if (e.Error != null)
{
MessageBox.Show("Error. Details: " + e.Error);
}
else
{
MessageBox.Show("The task has been completed. Results: " + e.Result);
}
}
private delegate void SimpleDelegate();
void DoWork(object sender, DoWorkEventArgs e)
{
for (var i = 0; i < 1000000; i++)
{
_worker.ReportProgress(i, DateTime.Now);
// SimpleDelegate simpleDelegate = () => txtResult.AppendText("Test" + System.Environment.NewLine);
//Dispatcher.BeginInvoke(DispatcherPriority.Normal, simpleDelegate);
}
MessageBox.Show("I have done it all");
}
private void _worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
DateTime time = Convert.ToDateTime(e.UserState);
txtResult.AppendText(time.ToLongTimeString());
txtResult.AppendText(Environment.NewLine);
}
private void BtnStart_Click(object sender, RoutedEventArgs e)
{
_worker.RunWorkerAsync();
}
private void BtnStop_Click(object sender, RoutedEventArgs e)
{
_worker.CancelAsync();
MessageBox.Show("Process has been stopped!");
}
}
You run a very tight loop inside of DoWork and continuously push Invoked ProgressUpdates to the Main Thread. That will make it sluggish.
But the real problem is that DoWork has to cooperate in Cancellation:
void DoWork(object sender, DoWorkEventArgs e)
{
for (var i = 0; i < 1000000; i++)
{
if (_worker.CancelationPending)
{
e.Cancel = true;
break; // or: return to skip the messagebox
}
_worker.ReportProgress(i, DateTime.Now);
}
MessageBox.Show("I have done it all"); // remove or make depend on Cancelled
}

Resources