I have this situation: a class that contains a background worker that do some thing in a while cycle:
public class CControls
{
public delegate void ControlChangedEventHandler();
public event ControlChangedEventHandler ControlChangedEvent;
private readonly BackgroundWorker worker;
bool bClose = false;
public CControls(IntPtr hwnd)
{
worker = new BackgroundWorker();
worker.DoWork += worker_DoWork;
worker.RunWorkerCompleted += worker_RunWorkerCompleted;
}
void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
bClose = true;
}
public void enable(bool bEnable)
{
if (bEnable && !worker.IsBusy)
{
worker.RunWorkerAsync();
}
else
{
bClose = true;
}
}
void worker_DoWork(object sender, DoWorkEventArgs e)
{
while (!bClose)
{
// my job
// ..............................................
//
if (ControlChangedEvent != null)
{
ControlChangedEvent();
}
}
}
}
I have my form that create an instance of this class and set the listener of ControlChangedEvent:
CControls ct = new CControls();
ct.ControlChangedEvent += ct_ControlChangedEvent;
int changes = 0;
void ct_ControlChangedEvent()
{
Dispatcher.Invoke(DispatcherPriority.Background, (Action)delegate
{
changes ++;
infoLabel.Content = string.Format("changes: {0}", changes);
});
}
but the infoLabel changes only if my program have the focus, otherwise is not fired...
any ideas?
thanks ;)
Related
In an Office Add-In I need to call a WPF which executes a function which may timeout but I want the UI to be responsive to allow the user to click the cancel/close button.
So far my code is the following:
// From the Ribbon
var f = new Forms.CheckConnectivityPopup();
f.doneEvent.WaitOne();
// Get the status from the popup or null if the operation was cancelled
var status = f.status;
if(status != null)
// Continue the execution
--------------------------------
public partial class CheckConnectivityPopup : MetroWindow
{
public readonly BackgroundWorker worker = new BackgroundWorker();
public AutoResetEvent doneEvent = new AutoResetEvent(false);
public Status status = null;
public CheckConnectivityPopup()
{
InitializeComponent();
this.Show();
worker.DoWork += worker_DoWork;
worker.RunWorkerCompleted += worker_RunWorkerCompleted;
worker.RunWorkerAsync();
}
private void worker_DoWork(object sender, DoWorkEventArgs e)
{
// displayAndCheck();
status = CheckStatus();
Thread.Sleep(10000); // to simulate the time
}
private void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
doneEvent.Set();
this.Close();
}
}
So far the popup is frozen until sleep period is completed.
WaitOne() blocks the current thread. You could replace the AutoResetEvent with a SemaphoreSlim:
public partial class CheckConnectivityPopup : MetroWindow
{
public readonly BackgroundWorker worker = new BackgroundWorker();
public SemaphoreSlim doneEvent = new SemaphoreSlim(0, 1);
public Status status = null;
public CheckConnectivityPopup()
{
InitializeComponent();
this.Show();
worker.DoWork += worker_DoWork;
worker.RunWorkerCompleted += worker_RunWorkerCompleted;
worker.RunWorkerAsync();
}
private void worker_DoWork(object sender, DoWorkEventArgs e)
{
//displayAndCheck();
status = CheckStatus();
Thread.Sleep(10000); // to simulate the time
}
private void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
doneEvent.Release();
this.Close();
}
}
...that you can await asynchronously:
var f = new Forms.CheckConnectivityPopup();
await f.doneEvent.WaitAsync();
For you to be able to await the WaitAsync() method, you must mark the method where you create the instance of the CheckConnectivityPopup as async:
void async YourMethod() { ... }
If this is not an option for some reason, you could use the ContinueWith method instead:
var f = new Forms.CheckConnectivityPopup();
f.doneEvent.WaitAsync().ContinueWith(_ =>
{
var status = f.status;
if (status != null)
{
//...
}
});
I need to update the program i did when i was still 3 months in the office. And i found out that the code is not clean and not easy to modify.
What i did is every method is called in background worker. And i created multiply Background worker for each task that possibly be executed at once.
So i am planning in making the code cleaner and easier to maintain.
I created a class and inherit the BackgroundWorker as shown below in my code
public class Bgw : BackgroundWorker
{
BackgroundWorker worker;
public int ProcessId { get; set; }
public object[] ProcessData { get; set; }
public Bgw(int i, object[] obj)
{
ProcessId = i;
ProcessData = obj;
worker = new BackgroundWorker();
worker.DoWork += Worker_DoWork;
worker.WorkerSupportsCancellation = true;
worker.WorkerReportsProgress = true;
worker.RunWorkerCompleted += Worker_RunWorkerCompleted;
worker.ProgressChanged += Worker_ProgressChanged;
worker.RunWorkerAsync();
}
public void Worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
}
public void Worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
}
public void Worker_DoWork(object sender, DoWorkEventArgs e)
{
if (ProcessId == 1)
{
//executed my code here
}
}
}
But with the code above. How can i notify the changes in my controls if i call the class like this
var obj = new object[3];
obj[0] = 1;
obj[1] = PdfPath;
obj[2] = EpubPath;
var work = new Bgw(1, obj);
Or is there a better way on doing this? I know that i need to use the ProgressChanged. But how can i Get that changes from where i called that class?
Thank you
Edit: If im going to use async and await like this
class Program
{
public async static void TestAsyncAwaitMethods()
{
await Task.Run(() =>
{
for (var i = 0; i <= 5000000; i++)
{
UpdateUI(i);
}
});
}
private static void UpdateUI(int i)
{
Console.WriteLine("Update the UI: " + i);
}
}
ANd call the method TestAsyncAwaitMethods()
How can i update my GUI. When the method reside from a different class and the property i want to update is in another class(I'm using mvvm pattern).
My question is similar to this one, I have pretty much the same code setup except I'm using BackgroundWorker instead of WorkflowRuntime. (And the answer doesn't appear to work for me)
In the past I have used Application.Current.Shutdown(); in the closing event of MainWindow, however I was hoping that by properly disposing of this window which I've made a static resource I could perhaps not need that.
The problem is that if I exit via closing MainWindow after all the background tasks terminate an empty BackgroundDialog remains open.
public partial class BackgroundDialog : Window
{
private static BackgroundDialog _Dialog = new BackgroundDialog();
private static UIElementCollection TasksView { get { return _Dialog.BackgroundList.Children; } }
public static void Add(BackgroundItem item)
{
if (TasksView.Count == 0)
{
_Dialog.Show();
}
TasksView.Add(item);
}
public static void Remove(BackgroundItem item)
{
TasksView.Remove(item);
if (TasksView.Count == 0)
{
if (_Release)
{
FinishRelease();
}
else
{
_Dialog.Hide();
}
}
}
private static bool _Release = false;
private static void FinishRelease()
{
// FinishRelease may be called from a BackgroundWorker thread finishing
// This results in _Dialog.Close() not behaving as expected
// For more details: https://stackoverflow.com/questions/5659930/wpf-window-not-closing
_Dialog.Dispatcher.Invoke(DispatcherPriority.Normal, new Action(() =>
{
_Dialog.Close();
_Dialog = null;
}));
}
public static void Release(EventArgs e)
{
_Release = true;
if (TasksView.Count == 0)
{
FinishRelease();
}
else foreach (BackgroundItem Task in TasksView)
{
Task.Abort();
}
}
}
public partial class BackgroundItem : UserControl
{
public delegate void TaskHandler(BackgroundWorker Worker);
public interface IBackgroundTask
{
bool IsIndeterminate { get; }
int MaxProgress { get; }
string Title { get; }
string Description(int progress);
TaskHandler Exec { get; }
}
private BackgroundWorker Worker;
public BackgroundItem(IBackgroundTask task)
{
InitializeComponent();
Title.Text = task.Title;
Description.Text = task.Description(0);
Progress.Value = 0;
Progress.Minimum = 0;
Progress.Maximum = task.MaxProgress;
Progress.IsIndeterminate = task.IsIndeterminate;
BackgroundDialog.Add(this);
Worker = new BackgroundWorker()
{
WorkerReportsProgress = true,
WorkerSupportsCancellation = true,
};
Worker.DoWork += (object sender, DoWorkEventArgs e) =>
{
task.Exec?.Invoke(Worker);
};
Worker.RunWorkerCompleted += (object sender, RunWorkerCompletedEventArgs e) =>
{
BackgroundDialog.Remove(this);
};
Worker.ProgressChanged += (object sender, ProgressChangedEventArgs e) =>
{
Progress.Value = e.ProgressPercentage;
Description.Text = task.Description(e.ProgressPercentage);
};
Worker.RunWorkerAsync();
Stop.Click += (object sender, RoutedEventArgs e) =>
{
Abort();
};
}
public void Abort()
{
Worker.CancelAsync();
Stop.IsEnabled = false;
StopText.Text = "Stopping";
}
}
public partial class MainWindow : Window
{
private class MyTask : BackgroundItem.IBackgroundTask
{
public bool IsIndeterminate => true;
public int MaxProgress => 100;
public string Title => "I'm Counting";
public BackgroundItem.TaskHandler Exec => (BackgroundWorker Worker) =>
{
for (int i = 0; i < 100; ++i)
{
if (Worker.CancellationPending)
{
break;
}
Worker.ReportProgress(i);
Thread.Sleep(500);
}
};
public string Description(int progress)
{
return progress.ToString();
}
}
public MainWindow()
{
InitializeComponent();
Loaded += (object sender, RoutedEventArgs e) => {
new BackgroundItem(new MyTask());
new BackgroundItem(new MyTask());
new BackgroundItem(new MyTask());
};
}
protected override void OnClosed(System.EventArgs e)
{
base.OnClosed(e);
BackgroundDialog.Release(e);
}
}
Try looking into Application.ShutdownMode. You'll want to set ShutdownMode to be OnMainWindowClose.
I feel silly, must have been the end of the day on Friday....here was the problem
in BackgroundDialog:
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
e.Cancel = true;
}
Must have been a relic from before I found this solution. However, some cancellation is needed to prevent the user from closing the dialog from the taskbar. So I wrapped the cancel with the statement if (!_Release)
I run user defined scripts in my WPF application using CS-Script library. How can I cancel a script if it runs endless? As my users write the script I can't rely on a cancel flag that is checked inside the script.
Here is a simplified code snippet showing the problem:
public partial class MainWindow : Window
{
public string MessageFromScript
{
get { return (string)GetValue(MessageFromScriptProperty); }
set { SetValue(MessageFromScriptProperty, value); }
}
public static readonly DependencyProperty MessageFromScriptProperty =
DependencyProperty.Register("MessageFromScript", typeof(string), typeof(MainWindow), new PropertyMetadata(null));
public MainWindow()
{
InitializeComponent();
DataContext = this;
}
BackgroundWorker worker = null;
private void OnStart(object sender, RoutedEventArgs e)
{
if(worker != null)
{
return;
}
worker = new BackgroundWorker();
worker.DoWork += RunScript;
worker.RunWorkerCompleted += ScriptCompleted;
worker.WorkerSupportsCancellation = true;
worker.RunWorkerAsync();
}
private void ScriptCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if(e.Cancelled)
MessageFromScript = "Script cancelled";
else
MessageFromScript = e.Result.ToString();
}
private void RunScript(object sender, DoWorkEventArgs e)
{
dynamic script = CSScript.Evaluator.LoadCode(#"using System;
using System.Threading;
public class Script
{
public string Test()
{
{int count=0; while(true) { count++; Console.WriteLine(count.ToString()); Thread.Sleep(200); }}
return ""Message from script"";
}
}");
e.Result = script.Test();
}
private void OnStop(object sender, RoutedEventArgs e)
{
if(worker == null)
{
return;
}
//TODO: How do I stop the script here?
worker = null;
}
}
In your Test() method add a parameter where you pass a CancellationToken to the script. Then design the loops in your script to check the canellation token if abort has been requested and break out. To stop the script just call the Cancel() method of your CancellationTokenSource which token you passed to the script on invocation.
I am trying to run a function in a different class than the dispatcher through a backgroundworker and have it update the progress on every iteration. I am getting no errors and the backgroundworker is functioning properly, but my textbox never updates...
public partial class MainWindow : Window
{
public BackgroundWorker worker = new BackgroundWorker();
public MainWindow()
{
InitializeComponent();
worker.WorkerReportsProgress = true;
worker.DoWork += new DoWorkEventHandler(workerDoWork);
worker.ProgressChanged += new ProgressChangedEventHandler(workerProgressChanged);
}
private void myButtonClick(object sender, RoutedEventArgs e)
{
worker.RunWorkerAsync();
}
void workerDoWork(object sender, DoWorkEventArgs e)
{
yv_usfm.convert(worker);
}
void workerProgressChanged(object sender, ProgressChangedEventArgs e)
{
myTextBox.Text = "some text";
}
}
public class yv_usfm
{
public static void convert(BackgroundWorker worker)
{
int i = 1;
while (i < 100)
{
worker.ReportProgress(i);
i++;
}
}
}
What makes you say the BackgroundWorker is functioning properly? I see no call to worker.RunWorkerAsync(), and without that it will never start.
You're not starting the worker!
worker.RunWorkerAsync();
Try This:
void DoWork(...)
{
YourMethod();
}
void YourMethod()
{
if(yourControl.InvokeRequired)
yourControl.Invoke((Action)(() => YourMethod()));
else
{
//Access controls
}
}
Hope This help.