using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Timers;
using Xamarin.Forms;
namespace TimerTest
{
public partial class MainPage : ContentPage
{
Label label;
int i = 0;
private static System.Timers.Timer aTimer;
public MainPage()
{
InitializeComponent();
label = new Label
{
Text = ""+i,
VerticalOptions = LayoutOptions.Center,
HorizontalOptions = LayoutOptions.Center
};
this.Content = label;
SetTimer();
//this.Content = label;
}
public void SetTimer()
{
// Create a timer with a two second interval.
aTimer = new System.Timers.Timer(2000);
// Hook up the Elapsed event for the timer.
aTimer.Elapsed += OnTimedEvent;
aTimer.AutoReset = true;
aTimer.Enabled = true;
}
private async void OnTimedEvent(Object source, ElapsedEventArgs e)
{
i++;
label.Text = ""+i;
//this.Content = label;
}
}
}
I have followed Microsoft's definition for implementing the timer method, however, When trying to actually implement it, nothing is ever updated to the screen.
Below I have set up a simple program in Xamarin.Forms that should update the label to the count of whatever i is every 2 seconds, however, the screen just sits at 0 (what the label was initiated to).
Does anyone have any understand what I am doing wrong and how to fix my issue?
Thanks in advance!
You are not on the UI thread as the Timer callback is on a background thread, use Device.BeginInvokeOnMainThread when updating UI elements in that case:
So when updating your label instance in the callback, do this:
Device.BeginInvokeOnMainThread(() => label.Text = "" +i;);
If I were to update the text and update another element, would I just place a , after label.Text = ""+1or would I have to have a whole other line replicated,
The parameter provided to BeginInvokeOnMainThread is an Action, so you can execute as much code as needed on the UI thread using just one "block":
Device.BeginInvokeOnMainThread(() =>
{
...;
...;
...;
});
Or:
void UIThreadAction()
{
...;
...;
...;
}
Device.BeginInvokeOnMainThread(UIThreadAction);
Related
I have a long running method, so I created a progress bar to show what percentage my method was to completing, but I am having difficulty figuring out how to do this ,syncing my progress bar with my method => excelHelper.InsertNewRows();.
public void ButtonSubmit_Click(object sender, EventArgs e)
{
if (isProcessRunning)
{
MessageBox.Show("A Process is aleady running");
return;
}
Thread backgroundThread = new Thread(
new ThreadStart(() =>
{
for (int n = 0; n < 100; n++)
{
isProcessRunning = true;
Thread.Sleep(50);
progressBar1.Invoke(
new Action(() =>
{
progressBar1.Value = n;
label3.Text = ("Progress: " + n + "%");
}
));
}
MessageBox.Show("Thread completed!");
progressBar1.Invoke(
new Action(() =>
{
progressBar1.Value = 0;
}
));
isProcessRunning = false;
}
));
backgroundThread.Start();
excelHelper.InsertNewRows();
var folder = TextOutputFile.Text + #"\" +
DateTime.Now.ToString("yyyy_MM_dd_") + "SA_Analysis_Report.xlsx";
excelHelper.Save(folder);
MessageBox.Show("File has been added to file");
}
IMO your problem is, that you dont have any communication between the working thread excelHelper.InsertNewRows(); and your progressbar thread. Both threads are running without any information about the other thread and their progress.
You could rewrite the background thread, so its capable of taking the percentage as a parameter, which is shown in the moment you call the thread.
E.g. Pseudo Code:
public void progressbarThread(int percentage)
{
// Invoke the percentage to your progressbar, then terminate this thread
}
public void InsertNewRows()
{
// Do something...
// 10%
Thread backgroundThread = new Thread(new ThreadStart(() => progressBarThread(10)));
backgroundThread.Start();
// Do something...
// 50%
Thread backgroundThread = new Thread(new ThreadStart(() => progressBarThread(50)));
backgroundThread.Start();
// etc. etc.
}
Update:
I've found this on my own research how to build a smooth loadingbar with an extra form, it maybe useful: Form in an extra Thread
I use this form class: -
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Yournamespace
{
public partial class frmProgressBar : Form
{
public Action Worker { get; set; }
System.Diagnostics.Stopwatch watch = System.Diagnostics.Stopwatch.StartNew();
public const long MINIMUM_DISPLAY_TIME = 300; // Minimum time to display the progress bar is 300 milliseconds
public frmProgressBar(Action worker)
{
InitializeComponent();
if(worker == null)
{
throw new ArgumentNullException();
}
Worker = worker;
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
Task.Factory.StartNew(Worker).ContinueWith(t => { this.Close(); }, TaskScheduler.FromCurrentSynchronizationContext());
}
protected override void OnClosed(EventArgs e)
{
// the code that you want to measure comes here
watch.Stop();
var elapsedMs = watch.ElapsedMilliseconds;
if (elapsedMs < MINIMUM_DISPLAY_TIME) //ensure progress bar is displayed for at least 100 milliseconds, otherwise it just looks like the parent main form glitches.
{
long lDelay = MINIMUM_DISPLAY_TIME - elapsedMs;
Thread.Sleep((int)lDelay);
}
}
}
}
The form design looks like this : -
[![enter image description here][1]][1]
[1]: https://i.stack.imgur.com/n4XqU.jpg
And I call it like this: -
using (Yournamespace.frmProgressBar frm = new Yournamespace.frmProgressBar(Yourprocess))
{
frm.ShowDialog(this);
}
Yourprocess is the code that you want to execute that is causing the delay.
The main problem with this implementation is Yourprocess cannot return a value or take parameters. I am sure the code can be changed to accomodate this but I did not have time so I use globals to pass in data and to see results(shame on me).
This is not my code, although I have modified it, came from a you tube video - Wait Form Dialog.
Edit. I forgot to say that I set my form to 100% opacity so the progress bar seems to float above my winform whenever I use it.
There are definitely resources to help you figure this out, however I spent like 2 days figuring it out so I'm going to help you out here and save you the headache. The progress bar itself is under the main UI thread (like any objects in your form) and needs to be handled by the main thread. Whatever you are trying to do on the side can be handled by a thread like this
String a, b;
a = txtUsername.Text;
b = txtPassword.Password;
Thread Login = new Thread(() => CompleteLogin(a, b));
InversePbVisibility();
Login.Start();
The InversePbVisibility() method would be replaced by whatever you are doing to make the progress bar visible to the user. A quick side note, any methods that are run on your declared thread can only pass variables and not anything already under the control of the main thread.
This question already has answers here:
How do I update the GUI from another thread?
(47 answers)
Closed 5 years ago.
I recently started learning C#, I am trying to repeat a method every minute with help of a timer. The method changes the value of the label. However, I get the following error:
$exception {"Cross-thread operation not valid: Control 'label1'
accessed from a thread other than the thread it was created
on."} System.Exception {System.InvalidOperationException}
I have tried searching for a solution and each thread confuses me. I don't just need the correct code but also explanation as I want to learn manipulating UI with help of Timers and Threading.
The following is my code:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Timers;
using System.Windows.Forms;
namespace Winforms_Timer_and_Thread
{
public partial class Form1 : Form
{
System.Timers.Timer myTimer = new System.Timers.Timer();
public Form1()
{
myTimer.Enabled = true;
InitializeComponent();
}
public void startMethod(object senter, ElapsedEventArgs e)
{
int cntr = 0;
cntr++;
label1.Text = "Executed: " + cntr.ToString();
}
private void button1_Click(object sender, EventArgs e)
{
label1.Text = "Started!";
myTimer.Enabled = true;
myTimer.Interval=(1*60*1000);
myTimer.Elapsed += new System.Timers.ElapsedEventHandler(startMethod);
}
}
}
If you change to use a System.Windows.Forms.Timer, you won't have that problem, since it's executed in the same UI thread.
You only have to change:
The type of the timer from System.Timers.Timer to System.Windows.Forms.Timer
The event that gets subscribed to from Elapsed to Tick
The event signature, where ElapsedEventArgs e becomes EventArgs e
You also only need to subscribe to the event once, not each time the timer is enabled, so move that to the Form_Load event instead, along with the Interval assignment (although that can be changed any time).
You also might want to store the counter variable outside of the StartMethod, so it increments each time it's executed, and then reset it to zero each time you start the timer:
public partial class Form1 : Form
{
readonly System.Windows.Forms.Timer myTimer = new System.Windows.Forms.Timer();
private int tickCounter;
public Form1()
{
InitializeComponent();
myTimer.Interval = (int)TimeSpan.FromMinutes(1).TotalMilliseconds;
myTimer.Tick += StartMethod;
}
private void StartMethod(object sender, EventArgs e)
{
tickCounter++;
label1.Text = "Number of executions: " + tickCounter;
}
private void button1_Click(object sender, EventArgs e)
{
tickCounter = 0;
label1.Text = "Started!";
myTimer.Enabled = true;
}
}
I'm using GTK# to build a GUI. Some data is processed in the background and I'd like to see some info about the progress on the user interface. Here is some code demonstrating the way I am trying to do this:
using System;
using Gtk;
using System.Threading.Tasks;
using System.Threading;
public partial class MainWindow: Gtk.Window
{
//a button and a textfield
private VBox VB = new VBox();
private Button B = new Button("Push dis");
private Label L = new Label("0");
public MainWindow () : base (Gtk.WindowType.Toplevel)
{
B.Clicked += OnClickEvent;
////////////////////
VB.PackStart (B);
VB.PackStart (L);
Add (VB);
ShowAll ();
Build ();
}
protected void OnDeleteEvent (object sender, DeleteEventArgs a)
{
Application.Quit ();
a.RetVal = true;
}
//async method incrementing variable, simulating some work and sending its progress
protected async Task CounterGUIUpdateAsync(IProgress<string> progress)
{
await Task.Run (() => {
for (int i = 0; i <= 10000; i++) {
Thread.Sleep (100);
if(progress != null)
{
var stri = Convert.ToString(i);
progress.Report(stri);
}
}
});
}
//event handler for the button
protected async void OnClickEvent(object sender, EventArgs e)
{
var ProgressIndicator = new Progress<string> (ReportProgress);
await CounterGUIUpdateAsync (ProgressIndicator);
}
//action connected to the progress instance
protected void ReportProgress(string value)
{
L.Text = value;
}
}
Running the code will start off as expected, but at some point it is likely that the displayed counter gets stuck. The GUI won't update anymore, turning black if it has been minimized. It is still functional though.
Help is appreciated very much.
I think your problem is that you're not using the main thread (the gui thread) when using the Gtk+ API. You need to use Gtk.Application.Invoke() passing a delegate that manipulates the UI, so the action is performed in the correct thread.
You can read more about this here.
In the designer i put backgroundworker and i have two events: Do Work and Progress Changed.
I used breakpoint and its getting inside the Do Work event but it never get into the Progress Changed event. Its never stop there like the event isnt working. Why the progrss changed event isnt working ?
This is the code:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Google.GData.Client;
using Google.GData.Extensions;
using Google.GData.Extensions.MediaRss;
using Google.GData.YouTube;
using Google.YouTube;
using System.Threading;
namespace YoutubeTesting
{
public partial class Form1 : Form
{
YouTubeRequestSettings settings;
YouTubeRequest request;
string devkey = "AI39si6xhSQXx95FTYIACWPfq-lLIphblgaReuz9z6VEjR1Q6YjrV6FRN2U6FN6P6-lGF2OYaUZhCVOKJ_MCk4o6kPeUszvf5A";
string username = "chocolade13091972#gmail.com";
string password = "password";
public Form1()
{
InitializeComponent();
worker.RunWorkerAsync();
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void upload()
{
try
{
settings = new YouTubeRequestSettings("You Manager", devkey, username, password);
settings.Timeout = -1;
request = new YouTubeRequest(settings);
Video video = new Video();
video.Title = "test";
video.Tags.Add(new MediaCategory("Comedy", YouTubeNameTable.CategorySchema));
video.Keywords = "Comedy";
video.Private = false;
video.MediaSource = new MediaFileSource("d:\\VIDEO0037.3gp", "video/3gp");
request.Upload(video);
MessageBox.Show("Successfully Uploaded");
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
private void worker_DoWork(object sender, DoWorkEventArgs e)
{
upload();
}
private void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
textBox1.Text = e.ProgressPercentage.ToString();
}
}
}
You need to report the progress using worker.ReportProgress()
From MSDN:
If you need the background operation to report on its progress, you
can call the ReportProgress method to raise the ProgressChanged event.
The WorkerReportsProgress property value must be true, or
ReportProgress will throw an InvalidOperationException.
It is up to you to implement a meaningful way of measuring your
background operation's progress as a percentage of the total task
completed.
The call to the ReportProgress method is asynchronous and returns
immediately. The ProgressChanged event handler executes on the thread
that created the BackgroundWorker.
You have to set this.
backgroundWorker.WorkerReportsProgress = true;
Gets or sets a value indicating whether the BackgroundWorker can
report progress updates.
EDIT
If still not working checks whether you have bind the event properly in the designer code. Or just add something like below in your class.
backgroundWorker1.ProgressChanged += new System.ComponentModel.ProgressChangedEventHandler(this.worker_ProgressChanged);
In your Upload method you have to report progress. Otherwise above event won't fire. Keep in mind that, it's not easy to report actual progress always.
Below is an example code for a DoWork method. Look at here if you want to see a complete example.
static void bw_DoWork (object sender, DoWorkEventArgs e)
{
for (int i = 0; i <= 100; i += 20)
{
if (_bw.CancellationPending) { e.Cancel = true; return; }
_bw.ReportProgress (i);
Thread.Sleep (1000); // Just for the demo... don't go sleeping
} // for real in pooled threads!
e.Result = 123; // This gets passed to RunWorkerCompleted
}
I am tryig to fade-in a windows form using c# but it doesnt seem to work after I have shown the form. Is it possible to change the forms opacity after Ive shown it?
Code:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Timers;
namespace ToolStrip
{
public partial class Form1 : Form
{
Form ToolForm = new ToolForm();
Form PropForm = new PropertyGrid();
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
ToolForm.FormBorderStyle = FormBorderStyle.FixedToolWindow;
ToolForm.Owner = this;
ToolForm.Show();
ToolForm.Location = new Point(50, 50);
}
private void button2_Click(object sender, EventArgs e)
{
PropForm.FormBorderStyle = FormBorderStyle.FixedToolWindow;
PropForm.Owner = this;
PropForm.Show();
PropForm.Location = new Point(50, 50);
System.Timers.Timer aTimer = new System.Timers.Timer(10000);
aTimer.Elapsed += new ElapsedEventHandler(OnTimedEvent);
aTimer.Interval = 2000;
aTimer.Enabled = true;
Console.WriteLine("Press the Enter key to exit the program.");
Console.ReadLine();
}
private void OnTimedEvent(object source, ElapsedEventArgs e)
{
PropForm.Opacity = PropForm.Opacity - 0.25;
Console.WriteLine(PropForm.Opacity);
}
}
}
because you r using System.Timers.Timer which is a multithread timer, in it's OnTimedEvent() it calls control created by another thread, which cause exception.
If you use System.Windows.Forms.Timer, it will work. i tested.
Using your code (and creating the other necessary Form classes), I get a cross-threading exception the first time the timer fires and the event handler is called, as Benny suggests.
Making changes to your code to check InvokeRequired in the timer event handler, and use Invoke if necessary to change PropForm.Opacity, results in the opacity changing after the form is shown, as required.
Note that you probably want to start with an Opacity of 0, and increase it gradually - otherwise your form will start off completely solid and gradually fade out
I will mention in passing that Opacity will have no effect on some versions of Windows, though you say you have Opacity effects working elsewhere, so it shouldn't be that in this case.
Ive gotten it to work without timers:
int Loop = 0;
for (Loop = 100; Loop >= 5; Loop -= 10)
{
this.PropForm.Opacity = Loop / 95.0;
this.PropForm .Refresh();
System.Threading.Thread.Sleep(100);
}
but i cant seem to change this example to fade-in instead of out.