Refresh UI from a method running asyncronously on another project - c#

I had a winform using a method on another project thought a DLL, test, count and returns 2 values (good files and bad files) and show up on the winforms those 2 results once done.
Ive been asked to improve that winform to show up results in real time, since the work and the test can take up to 30mins, but ive been struggling since i'm beginning in async programmation.
Ive tried to call function with out or ref, without success. As far i tried, i can refresh in real time a local variable, but not one running in the method out of the winform project.
Winform :
public static int goodfiles { get; set; }
public static int badfiles { get; set; }
Task workControl;
Task refreshControl;
private async void Winform_Load(object sender, EventArgs e)
{
myprogressBar.Style = ProgressBarStyle.Marquee;
workControl = Task.Run(() => WorkMethod());
refreshControl = Task.Run(() => RefreshMethod());
await executerControl.ConfigureAwait(true);
}
private void RefreshMethod()
{
while (!workControl.IsCompleted)
{
label1.Invoke(new MethodInvoker(delegate
{
label1.Text = goodfiles.ToString();
label2.Text = badfiles.ToString();
}
}
}
private void WorkMethod()
{
goodfiles = 0;
badfiles = 0;
var Work = new WorkClass();
Work.ControlFiles(goodfiles, badfiles);
}
Class library project
public class WorkClass
{
public void ControlFiles(int goodfiles, int badfiles)
{
//Do stuff
var Test = new TestClass();
Test.TestFiles(goodfiles, badfiles);
}
}
public class TestClass
{
public void TestFiles(int goodfiles, int badfiles)
{
//Test files
if(stuff) goodfiles++;
else badfiles++;
}
}
I know it's maybe far from being the prefect architecture, but I have to deal with it.
Is it technically possible, difficult or just impossible to do? Or am I missing something obvious ?

You need to use the same fields from the worker thread and UI thread. The best way is to put them in a shared object. This might be the work-class, but you could also create a separate object that is given as a parameter to the actual work-method. I recommend against using any mutable static fields.
public class WorkClass
{
public volatile int GoodFiles;
public volatile int BadFiles;
public void ControlFiles()
{
//Test files
if (stuff) GoodFiles++;
else BadFiles++;
}
}
and call it like
WorkClass myWork;
private async void Winform_Load(object sender, EventArgs e)
{
myWork = new WorkClass();
workControl = Task.Run(() => myWork.ControlFiles());
}
To check the progress I would recommend a timer. Set it to run however often you want, and update the labels from the myWork-object when event handler for the Tick-event. You can await the workControl-task and stop the timer when the task is done.

It depends on how coupled or uncoupled you want your code to be.
In most cases, the Progress class is a good choice.
Here's an article from Stephen Cleary on the subject: Reporting Progress from Async Tasks

Related

Progress Bar in DLL

I am creating DLL which contains loop of some data
how can i display progress bar
I tried to create new windows Form and displayed the same in for loop
but it ask me to close the form every time
People on SO are not here to write code for you - they are here to solve problems. Anyway, I am going to show you how you could do it, and then you can write your code based on what I provide.
First of all, a "DLL" is a Dynamic-link library. Therefore, you can attach it to any project you have (winform or unity 3d game and no, you will not do it but let's just say it could be used in both cases) so if you are already writing DLL's, make it usable in a lot of scenarios and provide the programmer with a lot possibilities for manipulation.
So, your task is divided in 2 parts here:
Calculate data
Inform the user of what stage of the calculation they are at now
For this task, we will use events and a simple for loop to show you how it works.
First of all, let's create an EventArgs class that will store all the data we want to pass when the programmer from the other code catches the event:
public class CustomEventArgs
{
public int OldResult { get; set; }
public int NewResult { get; set; }
}
Now, when we have an event class let's implement it in our code.
public class YourDllCalculation
{
// In the .NET Framework class library, events are based on the EventHandler delegate and the EventArgs base class.
// So we create delegate using our newly created class to represents it like EventHandler
public delegate void ResultChangeEventHandler(object sender, CustomEventArgs e);
// Now we create our event
public event IzborRobeEventHandler ResultChanged;
// Local storing variable
private int Result = 0;
// This is method from which you inform event something changed and user listening to event catch EventArgs passed (in our case our CustomEventArgs)
protected virtual void OnResultChange(CustomEventArgs e)
{
ResultChangeEventHandler h = ResultChanged;
if (h != null)
h(this, e);
}
// We will use this method from new code to start calculation;
public void StartCalculation()
{
// Calculation will be done in separate thread so your code could proceed further if needed
Thread t1 = new Thread(Calculate);
t1.Start();
}
private void Calculate()
{
for(int i = 0; i < 100; i++)
{
OnResultChange(new CustomEventArgs() { OldResult = i, NewResult = i + 1 });
Result = i;
Thread.Sleep(1000); // Pause thread from running for 1 sec
}
}
}
Now that we have our code we can use it like this in our Winform:
// Add at top
using YourDllNamespace;
public YourForm()
{
// Creating our class for calculation
YourDllCalculation calc = new YourDllCalculation();
calc += CalculationResultChanged;
calc.Calculate();
}
private void CalculationResultChanged(object sender, CustomEventArgs e)
{
// Here do whatever you want with passed values
// e.OldResult;
// e.NewResult;
// it will fire each second
}

Unable to populate listbox

Im having a bit of trouble with displaying a list in my listbox.
When I had everything in one class, things seemed to work fine but I cant figure out why it doesnt work now. My app, when clicked on the scan button, goes to a different class where there is a new thread created to scan for available bluetooth devices and the a list with those devices is created. Once the list is passed back to a method in Form1 class, it doesnt update the listbox. In debugging mode I could see that there are items on the list but nothing appears in the listbox. The listbox displays items if I did listBox1.Items.Add("Hello World") from the scan button click method. Im sort of stuck here. Ive just started learning C# and if anyone could help me that would be greatly appreciated.
public partial class Form1 : Form
{
int PanelWidth;
bool PanelCalShow;
public Form1()
{
InitializeComponent();
PanelWidth = PanelCal.Width;
PanelCalShow = false;
}
private void button2_Click(object sender, EventArgs e)
{
timer1.Start();
}
private void timer1_Tick(object sender, EventArgs e)
{
if (PanelCalShow)
{
PanelCal.Width = PanelCal.Width + 10;
if (PanelCal.Width >= PanelWidth)
{
timer1.Stop();
PanelCalShow = false;
this.Refresh();
}
}
else
{
if (PanelCalShow != true)
{
PanelCal.Width = PanelCal.Width - 10;
if (PanelCal.Width <= 0)
{
timer1.Stop();
PanelCalShow = true;
this.Refresh();
}
}
}
}
// Bluetooth connection
private void BtnScan_Click(object sender, EventArgs e)
{
var instance = new BtCom();
instance.Scan();
}
public void LbClientUpdate(List<string> DiscoveredDevices)
{
listBox1.DataSource = DiscoveredDevices;
}
}
and the bluetooth connection class
public class BtCom
{
public List<string> DiscoveredDevices = new List<string>();
Guid mUUID = new Guid("00001101-0000-1000-8000-00805F9B34FB");
public void Scan()
{
Thread bluetoothScanThread = new Thread(new ThreadStart(Scanning));
bluetoothScanThread.Start();
}
BluetoothDeviceInfo[] devices;
public void Scanning()
{
var form1 = new Form1();
BluetoothClient client = new BluetoothClient();
devices = client.DiscoverDevicesInRange();
foreach (BluetoothDeviceInfo d in devices)
{
DiscoveredDevices.Add(d.DeviceName);
}
form1.LbClientUpdate(DiscoveredDevices);
}
}
The reason you aren't seeing any update on your original form is that you are creating a new instance of the Form1 class inside of your BtCom class instead of using the original instance.
One way you could fix this would be to pass the original instance of the form to your BtClass through a parameter to the Scan method, and then pass it along to your Scanning method. This way you will be calling methods on the same instance of the form that's running.
The problem with that is you will be blocking your UI while waiting for the thread to finish (assuming you call bluetoothScanThread.Join() to wait for the thread results).
A slightly different solution would be to use Tasks, with async methods that await results.
To do this, you would have your scan method return a Task<List<string>> (which is a Task that returns a List<string> representing the devices).
Then in form1 you would create an async method (called GetDatasource below) that creates an instance of your BtCom class, awaits the scan method, and returns the Task<List<string>>.
Finally, you would also make the Click method async and have it assign await the GetDatasource method and assign the Datasource when it returns.
By doing it this way, you isolate the BtCon class from having to know any details about the Form1 class, which is a good habit to get into because you end up creating more reusable and independent code.
Here's an example of all those words in code:
Make the scan method return a Task<List<string>> that can be used for the datasource (and have Scanning return a List<string>). Notice that the list is now private to the scanning method. It's a good practice to limit the scope of variables to only the level they are needed.:
public class BtCom
{
Guid mUUID = new Guid("00001101-0000-1000-8000-00805F9B34FB");
public Task<List<string>> Scan()
{
var bluetoothScanTask = Task.Factory.StartNew(Scanning);
bluetoothScanTask.Wait();
return bluetoothScanTask;
}
private List<string> Scanning()
{
BluetoothClient client = new BluetoothClient();
devices = client.DiscoverDevicesInRange();
List<string> discoveredDevices = new List<string>();
foreach (BluetoothDeviceInfo d in devices)
{
discoveredDevices.Add(d.DeviceName);
}
return discoveredDevices;
}
}
Then, write an async method that will get the datasource by creating a new instance of this class, and await for the method to return. Also, make the Click method async so it can await the datasource method:
public partial class Form1 : Form
{
// Other form code omitted...
private async void BtnScan_Click(object sender, EventArgs e)
{
listBox1.DataSource = await Task.Run(GetDatasource);
}
private async Task<List<string>> GetDatasource()
{
var btCom = new BtCom();
List<string> results = await btCom.Scan();
return results;
}
Now your users can click the button and the form will continue to respond while the scanning takes place, and your listbox will populate when the scan method finishes.
For more on async and await, check your favorite search engine (like this page on Asynchronous programming, for example).

Method to Use a fifo Queue with background worker, and populating the queue with method(parameter)

Currently I am trying to create a background worker that can loop through some class functions, I created a class to store a few objects that are getting used globally (user settings and a few other things).
I essentially need to store the following line as an Action.
GV_Acc.load_page("weburl", 0);
Here is the majority of the code that I am using
Form Containing a Button:
private void btn_initialize_Click(object sender, EventArgs e){
CusWorker worker = new CusWorker();
worker.addwork(GV_Acc.load_page("weburl", 0));
worker.addwork(GV_Acc.json_populate(0));
worker.asyncworker.RunWorkerAsync();}
These are the 2 Classes I am using:
public class CusWorker
{
public BackgroundQueue asyncqueue { get;private set; }
private int tasks;
public System.ComponentModel.BackgroundWorker asyncworker = new System.ComponentModel.BackgroundWorker();
public CusWorker()
{
asyncqueue = new BackgroundQueue();
asyncworker.DoWork += new System.ComponentModel.DoWorkEventHandler(asyncworker_DoWork);
asyncworker.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(asyncworker_RunWorkerCompleted);
}
public void addwork(Action action)
{
asyncqueue.QueueTask(action);
}
private void asyncworker_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e)
{
}
private void asyncworker_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
if (asyncqueue != null)
{
asyncworker.RunWorkerAsync();
}
}
}
public class BackgroundQueue
{
private Task previousTask = Task.FromResult(true);
private object key = new object();
public Task QueueTask(Action action)
{
lock (key)
{
previousTask = previousTask.ContinueWith(t => action()
, CancellationToken.None
, TaskContinuationOptions.None
, TaskScheduler.Default);
return previousTask;
}
}
public Task<T> QueueTask<T>(Func<T> work)
{
lock (key)
{
var task = previousTask.ContinueWith(t => work()
, CancellationToken.None
, TaskContinuationOptions.None
, TaskScheduler.Default);
previousTask = task;
return task;
}
}
}}
So Should I use "Actions" Or is there a better way to do what I am attempting?
PS:Honestly I don't have any idea what the above does at this point. All I know is that when I run my program and click the button, everything locks up for about 30 seconds. I'm trying to stop that from happening but I don't know where to start now. Am I using any of the above code correctly?
Edit: Found out that when Something is added to the background Queue it starts work immediately, So I don't need the background worker. I Will have to learn more about tasks.
I ended up using a work around method and getting rid of the BackgroundWorker.
Did not understand Tasks or Actions, now that I know more about the syntax, The background worker is completely useless. Correct Syntax
queue = new BackgroundQueue();
queue.queuetask(() => {code to run};);
I did not realize that the Method starts running the moment it is passed to QueueTask().
Feeling Pretty Stupid for this question

Subscribing to an event of a class that holds the override method of the class you reference

I am working with background workers to update a progress bar in a WPF UI I am working on. This background worker is getting its progress updates from multiple events that I am subscribed to, because the progress bar goes through several loading stages, and the percentages for those come from several places. here is some example/pseudo code explaining what I mean
The DoWork method of my background worker and the methods I am using to currently get some progress updates
// These are working fine
private void BwOnDoWork(object sender, DoWorkEventArgs doWorkEventArgs)
{
orderProcessing.OnOrderProgress += OrderStatus;
orderProcessing.OnStandardOrderProgress += StandardOrderStatus;
orderProcessing.CreateOrders(orders);
}
private void OrderStatus(int currentCount, int totalItems, string Message)
{
if (totalItems > 0)
bw.ReportProgress(Convert.ToInt32(((double)currentCount / (double)totalItems) * 100),
Message);
}
private void StandardOrderStatus(int currentCount, int totalItems, string Message)
{
if (totalItems > 0)
bw.ReportProgress(Convert.ToInt32(((double)currentCount / (double)totalItems) * 100),
Message);
}
Some code from my order processing class
public abstract class OrderProcessing
{
public delegate void OrderProgress(int CurrentItems, int TotalItems, string Message);
public event MasterSalesOrder.StandardOrderProgress OnStandardOrderProgress;
public event OrderProgress OnOrderProgress;
public abstract List<MasterSalesOrder> CreateOrders(List<Order> orders);
}
Some code from the class that holds the override method for CreateOrders()
public abstract class OrderProcessingFile : OrderProcessing
{
public event OrderProgress OnOrderProgress;
public override List<MasterSalesOrder> CreateOrders(List<Order> orders)
{
//Does Some Stuff
foreach(var stuff in stuffs)
{
OnOrderProgress(currentCount, totalCount, "Message");
}
}
}
Since I am clearly not explaining this well, I need to get info from the OrderProcessingFiles OnOrderProgress event via the OrderProcessing class that I create in the DoWork method.I am unsure on how to subscribe to an event when my code never directly instantiates an instance of the OrderProcessingFile class and it is never directly referred to.
I have tried looking for answers but as my title will show I am having a hard time even wording this in a way to get useful results, and I am genuinely stuck on this one. Let me know if more detail is needed, I tried to strip down my code to only the relevant parts but I feel like I'm explaining this strangely.
I would recommend that you create a thread safe singleton progress manager. Then have each of the background workers contact it with updates. The progress manager will use a DispatcherTimer (which runs on the GUI thread) to update the GUI appropriately.
Raw example:
public static class StatusReportManager
{
// Standard singleton code to create the manager and access it.
// Start/create the dispatch time as well.
private static DispatcherTimer Timer { get; set; }
private static object _syncObject = new object();
public static void ReportStatus(...)
{
lock (_syncObject)
{
// Process any states and set instance properties for reading
// by the timer operation.
}
}
private void ShowStatus() // Used by the dispatch timer
{
lock (_syncObject)
{
// Do any updates to the GUI in here from current state.
}
}
}
I have realized what it is I was really trying to do and have thus found an answer. Using the method found in this MSDN article I have implemented the follow code:
This is my UI
private void BwOnDoWork(object sender, DoWorkEventArgs doWorkEventArgs)
{
orderProcessing.OnOrderProgress += OrderStatus;
orderProcessing.CreateOrders(FanGlobal.BrandItems, FanGlobal.BrandItemMasterCustomers);
}
private void OrderStatus(object obj, OrderProcessing.OrderProgressEventArgs e)
{
if (e.totalCount > 0)
bw.ReportProgress(Convert.ToInt32(((double)e.currentCount / (double)e.totalCount) * 100),e.message);
}
This in my OrderProcessing class
public event EventHandler<OrderProgressEventArgs> OnOrderProgress;
public class OrderProgressEventArgs : EventArgs
{
public int currentCount;
public int totalCount;
public string message;
public OrderProgressEventArgs(int c, int t, string m)
{
currentCount = c;
totalCount = t;
message = m;
}
}
protected virtual void OnOrderProgressChanged(OrderProgressEventArgs e)
{
EventHandler<OrderProgressEventArgs> handler = OnOrderProgress;
if (handler != null)
{
handler(this, e);
}
}
public abstract List<MasterSalesOrder> CreateOrders(List<BrandItem> BrandItems = null, List<BrandItemMasterCustomer> BrandItemMasterCustomers = null);
and then I can use it in my child class OrderProcessingFile like so
public override List<MasterSalesOrder> CreateOrders(List<BrandItem> BrandItems = null, List<BrandItemMasterCustomer> BrandItemMasterCustomers = null)
{
//Do some Stuff
OnOrderProgressChanged(new OrderProgressEventArgs(count, totalItems, "Extracting"));
}
and everything is working like a charm. Sorry for the utterly confusing question and the apparent huge gap of knowledge I have/had, but hopefully this will help someone else in the future.

C# Set Variable in Thread

I'm trying to work with Threads for a private Project and I have a question which is, as I think very easy to answer.
Is it possible to set a variable in another thread?
Here a little Code example to show you what I'm trying to do:
public class PartyClass
{
public boolean partytime = true;
public void MakeParty()
{
while(partytime)
Console.WriteLine("I'm making a party here");
Console.WriteLine("The party ended. Please leave now");
}
public void StopParty()
{
partytime = false;
}
}
public class MainThread
{
public static int Main(String[] args)
{
PartyClass party = new PartyClass();
Thread partyThread = new Thread(new ThreadStart(party.MakeParty()));
partyThread.Start();
while (!partyThread.IsAlive) ;
System.Threading.Thread.Sleep(5000);
// Now I want to somehow call the StopParty() Method
}
}
I don't know if it's really stupid what I'm trying to do but I think its a nice way to stop the "Partythread" in a clean way.
Is this possible or is there a better solution for this?
Thanks for your Ideas.
(I didn't test the Code - just wrote it out of my head)
You call the stop method just the way you called the start method:
party.StopParty();
In order to ensure that the changes made in another thread aren't just cached, the partytime field should be marked as volatile as well.
You should use synchronization facilities, such as CancellationToken.
Your code will look like:
public class PartyClass
{
private readonly CancellationToken _cancellationToken;
public PartyClass(CancellationToken cancellationToken)
{
_cancellationToken = cancellationToken;
}
public void MakeParty()
{
while (!_cancellationToken.IsCancellationRequested)
Console.WriteLine("I'm making a party here");
Console.WriteLine("The party ended. Please leave now");
}
}
public class MainThread
{
public static int Main(String[] args)
{
var cancellationSource = new CancellationTokenSource();
PartyClass party = new PartyClass(cancellationSource.Token);
Thread partyThread = new Thread(party.MakeParty);
partyThread.Start();
System.Threading.Thread.Sleep(5000);
cancellationSource.Cancel();
partyThread.Join();
}
}
It is thread-safe and suitable not only for this, but also for more advanced scenarios, as well as for working with tasks.
If want more threads to ask for the same variable, take care about thread syncrhonization issues (when two threads try to access the same variable).
Im going to show the safest way (might be more than what you need). The best to way to do it is declaring an static object to set a lock in order to make sure you have one thread changing the party flag at once.
public class PartyClass
{
private object _partyTimeLock = new Object(); // executed at class init
private boolean partyTime= true;
public bool IsPartyGoingOn()
{
bool itIsGoingOn = false;
lock(_partyTimeLock) {
itIsGoingOn = partyTime;
}
return itIsGoingOn;
}
public void StopParty()
{
lock(_partyTimeLock) {
partyTime = false;
}
}
public void MakeParty()
{
while(IsPartyGoingOn()) {
Console.WriteLine("I'm making a party here");
}
Console.WriteLine("The party ended. Please leave now");
}
}
In this example here, no matter who try to call the IsPartyGoingOn(), you will never have an issue (no matter if it the class own thread or another one). The lock keyword will guarantee you are doing the right way.

Categories