I want to run timer every 10 miliseconds and update GUI label string. I have create Class TimerController, were i can set up System.Threading.Timer properties.
class TimerControl
{
private Timer _timer;
public DateTime StartTime { get; private set; }
public TimeSpan CurrentElapsedTime { get; private set; }
public TimeSpan TotalElapsedTime { get; private set; }
public event EventHandler Tick;
public bool IsTimerRunning { get; private set; }
public string CurrentElapsedTimeString { get; private set; } = "";
public TimerCallback TimerAction { get; private set; }
public object TimerParametr { get; private set; }
public int DueTime { get; private set; }
public int Period { get; private set; }
public TimerControl(TimerCallback timerAction, object state, int dueTime, int period)
{
StartTime = DateTime.Now;
CurrentElapsedTime = TimeSpan.Zero;
TotalElapsedTime = TimeSpan.Zero;
TimerAction = timerAction;
TimerParametr = state;
DueTime = dueTime;
Period = period;
}
public void StartTimer()
{
StartTime = DateTime.Now;
TotalElapsedTime = CurrentElapsedTime;
IsTimerRunning = true;
if (_timer == null)
_timer = new Timer(TimerAction, TimerParametr, DueTime, Period);
else
_timer.Change(DueTime, Period);
}
public void StopTimer()
{
_timer.Change(0, -1);
}
I create TimerControl object in MainForm.cs and I need to create function, that will be triggered by a timer. This function should update GUI time label. But in this function i dont habe access to GUI. How to fix it?
TimerControl timerControl = new TimerControl(StopWatchTimer,null, 0, 10);
public MainForm()
{
InitializeComponent();
}
private void btn_timerStart_Click(object sender, EventArgs e)
{
if(btn_timerStart.Text == "Старт")
{
timerControl.StartTimer();
btn_timerStart.Text = "Стоп";
}
else
{
timerControl.StopTimer();
btn_timerStart.Text = "Старт";
}
}
// Callback timer funnction
private static void StopWatchTimer(object label)
{
//labelTime = // labelTime doesnt exist in current context
}
}
}
I did it like that, everything works fine.
public partial class MainForm : Form
{
TimerControl timerControl;
public MainForm()
{
InitializeComponent();
timerControl = new TimerControl(StopWatchTimer, l_timer, 0, 10);
}
private void btn_timerStart_Click(object sender, EventArgs e)
{
if(btn_timerStart.Text == "Старт")
{
timerControl.StartTimer();
btn_timerStart.Text = "Стоп";
}
else
{
timerControl.StopTimer();
btn_timerStart.Text = "Старт";
}
}
private void StopWatchTimer(object label)
{
Label timeLabel = (Label)label;
TimeSpan elapsed = DateTime.Now - timerControl.StartTime;
this.Invoke(new MethodInvoker(delegate ()
{
timeLabel.Text = elapsed.ToString(#"ss\.ff");
}));
}
}
enter image description here
Related
I have Windows Forms application that connects to Oracle database using a BackgrwoundWorker.
public partial class MainForm : Form, IDataFetcher
{
public EntryForm EntryForm { get; set; }
public ApplicationSettings Settings { get; set; }
private DbDataBackgroundWorker BackgroundWorker { get; set; }
enum QueryStatus { RUNNING, NOT_STARTED }
private QueryStatus;
public BillingForm()
{
InitializeComponent();
// configure background actual opearations
BackgroundWorker = new OpearationBackgroundWorker()
{
OnDataFetch = OnDataFetch,
OnDataFetchCompleted = OnDataFetchCompleted
};
}
public BillingForm(EntryForm parentForm, ApplicationSettings appSettings) : this()
{
EntryForm = parentForm;
Settings = appSettings;
queryStatus = QueryStatus.NOT_STARTED;
}
private void ExecuteButton_Click(object sender, EventArgs e)
{
switch (queryStatus)
{
case QueryStatus.NOT_STARTED:
BackgroundWorker.Start();
break;
case QueryStatus.RUNNING:
BackgroundWorker.Stop();
break;
}
}
public void OnDataFetchCompleted(object sender, RunWorkerCompletedEventArgs e)
{
queryStatus = QueryStatus.NOT_STARTED;
if (e.Error != null)
{
MessageBox.Show(e.Error.Message, Text, MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
public void OnDataFetch(object sender, DoWorkEventArgs e)
{
var worker = sender as BackgroundWorker;
if (worker.CancellationPending)
{
e.Cancel = true;
//MessageBox.Show("Запрос остановлен");
return;
}
// create database handler
var db = new DatabaseConnector(Settings.DbConnectionString);
var model = new BillingModel(db)
{
PostTitle = PostTitle,
StartDate = StartDateTimePicker.Value,
EndDate = EndDateTimePicker.Value,
};
try
{
model.Query((datatable) =>
{
Invoke(new Action(() =>
{
QueryResultDataGridView.DataSource = null;
QueryResultDataGridView.DataSource = datatable;
}));
});
}
catch (Exception exception)
{
Invoke(new Action(() => {
queryStatus = QueryStatus.NOT_STARTED;
}));
}
}
}
When I click on "Start" button (method ExecuteButton_Click) program fetches data in background and put them into a DataGridView.
// this class starts operations in background
public class OpearationBackgroundWorker
{
public Action<object,DoWorkEventArgs> OnDataFetch { get; set; }
public Action<object, RunWorkerCompletedEventArgs> OnDataFetchCompleted { get; set; }
//public Action<object, ProgressChangedEventArgs> OnDataFetchProgress { get; set; }
private BackgroundWorker backgroundWorker = new BackgroundWorker()
{
WorkerSupportsCancellation = true
};
public void Start()
{
backgroundWorker.DoWork += new DoWorkEventHandler(OnDataFetch);
backgroundWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(OnDataFetchCompleted);
if ( !backgroundWorker.IsBusy )
backgroundWorker.RunWorkerAsync();
else
throw new Exception("Process running!");
}
public void Stop()
{
backgroundWorker.CancelAsync();
backgroundWorker.Dispose();
}
}
public class SimpleModel : AbstractModel
{
public IDatabaseFetcher Db { get; set; }
public string PostTitle { get; set; }
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
public BillingModel(IDatabaseFetcher db)
{
Db = db;
}
public override void Query(Action<DataTable> callback)
{
var query = "SELECT * FROM posts WHERE post_titile = " + PostTitle;
Db.Query = query;
Db.Fetch(data => callback(data));
}
}
The program uses C# using construct to automatically handle the database connection.
public interface IDataFetcher
{
void OnDataFetch(object sender, DoWorkEventArgs e);
void OnDataFetchCompleted(object sender, RunWorkerCompletedEventArgs e);
}
class DatabaseConnector : IDatabaseFetcher
{
private string connectionString = "";
public string Query { get; set; }
public DatabaseConnector(string connectionString)
{
this.connectionString = connectionString;
}
// here i query data from db
public void Fetch(Action<DataTable> action)
{
using (var connection = new OracleConnection(connectionString))
using (var cmd = new OracleCommand(Query, connection))
{
connection.Open();
using (var reader = cmd.ExecuteReader())
{
if (!reader.HasRows)
{
throw new Exception("Empty result!");
}
var dataTable = new DataTable();
dataTable.Load(reader);
// run callback processor
action(dataTable);
}
}
}
}
But now, I want to slightly change the program's behaviour and add the ability manually cancel an operation, and close the connection to the database.
I already know how to cancel the BackgrwoundWorker execution, but I can't find solution for how to manually close connection to database and cancel querying of data running in a background process, when user clicks on the "Stop" button.
How to do this?
I have a form that has a couple of progress bars and text labels used for updating the user during polling and copying tasks.
In order for me to update the form controls from a child worker class I decided to use IProgress/Progress for the first time and would like to know if what I am doing is ok or if there is a better more maintainable way?
MAIN FORM Code:
private Progress<int> NumFiles { get; set; }
private Progress<string> CurrentFile { get; set; }
private Progress<int> OverallProgress { get; set; }
private Progress<string> AverageIoSpeed { get; set; }
private Progress<string> UpdateTaskLabel { get; set; }
private Progress<int> CurrentFileProgress { get; set; }
private BulletTimeCopyOperations CopyOperations { get; set; }
public Form1()
{
InitializeComponent();
SetFormFields();
}
private void SetFormFields()
{
UpdateTaskLabel = new Progress<string>(value => lblTaskInfo.Text = value);
NumFiles = new Progress<int>(value => lblNumFilesTotal.Text = value.ToString());
CurrentFile = new Progress<string>(file => lblFilesProgress.Text = $"Copying File: {file}");
CurrentFileProgress = new Progress<int>(fvalue => pbFilesProgress.Value = fvalue);
OverallProgress = new Progress<int>(total => pbTotalProgress.Value = total);
AverageIoSpeed = new Progress<string>(ioSpeed => lblSpeedAverage.Text = ioSpeed);
}
private void SetProgressMethods()
{
OtherClass.SetTaskInfo = UpdateTaskLabel;
OtherClass.ProgressNumberOfFiles = NumFiles;
OtherClass.FileInUse = CurrentFile;
OtherClass.CurrentProgress = CurrentFileProgress;
OtherClass.TotalProgress = OverallProgress;
OtherClass.TransferSpeed = AverageIoSpeed;
}
private void DoWork()
{
//Call Set progress
SetProgressMethods();
//SOME TASKS.....
}
Worker Class:
public IProgress<string> SetTaskInfo { get; set; }
public IProgress<int> ProgressNumberOfFiles { get; set; }
public IProgress<string> FileInUse { get; set; }
public IProgress<int> CurrentProgress { get; set; }
public IProgress<int> TotalProgress { get; set; }
public IProgress<string> TransferSpeed { get; set; }
private async Task<bool> StartWork()
{
//Do some work
//Report some progress
SetTaskInfo.Report("Long Running Tasks Running...");
//And so on etc...
}
So I need to tidy things up and want to update a couple more controls and although this works fantastically well, I am thinking there may be a better way of implementing the above for multiple controls?
I have a Main.class which runs a timer at 5 seconds (when it resets i want to do a bunch of methods and then repeat the timer). I have a Water.class with a currentReserve-property and a pop.class with a CurrentThirst and MaximumThirst-propertys. In the Pop class i have a method thats get called when the timer reset. Basically i want the popclass to consume the currentReserve-int in the object which i have created in the Mainclass. I provide the essential code and mark with comments where the debugger thinks iam wrong:
public AITest()
{
public Water _water;
public Pop _pop;
Timer dayTimer = new Timer();
int timeLeft = 5;
public AITest()
{
InitializeComponent();
_water = new Water(8000);
lblWater.Text = _water.CurrentReserve.ToString();
_pop = new Pop(100, 100);
dayTimer.Interval = 1000;
dayTimer.Enabled = true;
dayTimer.Tick += dayTimer_Tick;
dayTimer.Start();
lblCountDown.Text = timeLeft.ToString();
}
private void dayTimer_Tick(object sender, EventArgs e)
{
lblCountDown.Text = timeLeft.ToString();
timeLeft -= 1;
if (timeLeft < 0)
{
timeLeft = 5;
_water.CurrentReserve += 100;
_pop.HaveToDrink();
lblWater.Text = _water.CurrentReserve.ToString();
}
}
}
}
public class Pop
{
public int CurrentThirst { get; set; }
public int MaximumThirst { get; set; }
public Water _water;
public Pop(int currentThirst, int maximumThirst)
{
CurrentThirst = currentThirst;
MaximumThirst = maximumThirst;
}
public void HaveToDrink()
{
CurrentThirst -= 30;
_water.CurrentReserve -= 30; //Here i get an error
}
}
public class Water
{
public int CurrentReserve { get; set; }
public Water(int currentReserve)
{
CurrentReserve = currentReserve;
}
}
You've got a _water member twice: once in AITest and once in the Pop class. Use only one, and pass it around. The exception you get will be a NullReferenceException, because nothing is ever assigned to the Pop._water member.
i try to refresh a datagridview from a external Timer Class every Time i try this i become the error: thread comprehensive not allowed
Form1.cs:
public void Main_Window_Load(object sender, EventArgs e)
{
SetDatagridView();
}
public void SetDatagridView()
{
DataTable MBServices = new DataTable();
DataView MBServicesVW = new DataView();
MBServices = getMasterServicetblOverview("8");
MBServices.Columns.Add("State", typeof(Image));
MBServices.Columns.Remove("state");
MBServicesVW = new DataView(MBServices);
dataGridSHToverview.DataSource = MBServicesVW;
}
Timer.cs:
class Timer
{
public class NamedTimer : System.Timers.Timer
{
}
public static void TimerDo()
{
Timer timer = new Timer();
timer.Interval = (60000);
timer.Elapsed += Main_Tick;
timer.AutoReset = false;
timer.Start();
}
public static void Main_Tick(object sender, EventArgs args)
{
//there a want to call SetDatagridView()
}
}
timerDo() will be called in the master Class.
thank you for our help
I suggest you to use an event pattern.
Sample Code:
/// TimerManager class
public class TimerManager
{
public void TimerDo()
{
Timer timer = new Timer();
timer.Interval = (60000);
timer.Elapsed += Main_Tick;
timer.AutoReset = false;
timer.Start();
}
public void Main_Tick(object sender, EventArgs args)
{
if (OnTimerManagerTick != null)
OnTimerManagerTick(sender, args);
}
public delegate void TimerTick(object sender, EventArgs args);
public event TimerTick OnTimerManagerTick;
}
///Form1 Class
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
InitDataGridView();
TimerManager timerManager = new TimerManager();
timerManager.OnTimerManagerTick += TimerManager_OnTimerManagerTick;
}
private void TimerManager_OnTimerManagerTick(object sender, EventArgs args)
{
InitDataGridView();
}
private void InitDataGridView()
{
List<PersonTest> personTests = new List<PersonTest>
{
new PersonTest{Name = "Luiz", LastName = "Oliveira", Age = 35},
new PersonTest{Name = "Another", LastName = "Person", Age = 25},
new PersonTest{Name = "Neymar", LastName = "Junior", Age = 28},
};
dgvTestDbClick.DataSource = personTests;
}
}
public class PersonTest
{
public string Name { get; set; }
public string LastName { get; set; }
public int Age { get; set; }
}
Hello I want to change the position of a form2 created from form1 with a thread in an other thread created in form2 with a struct
Edit:
I made an other post
There is an other problem,
How can I make Form2 stop thinking?
This is a similar code to keep it simple.
When Form1 is loaded a thread is created this thread runs a method with a infinite loop in it and at some time creates Form2 and keeps on looping the problem is that Form2 never stops thinking.
public partial class Form1 : Form
{//Form1
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
Thread childThread = new Thread(new ThreadStart(loop);
childThread.Start();
}
public void loop()
{
int i = 0;
while (true)
{
if (i == 45)
{
Form2 f = new Form2();
f.Show();
}
i = i + 1;
}
}
}
}
public partial class Form2 : Form
{//Form2
public Form2()
{
InitializeComponent();
}
public struct Rect
{
public int Left { get; set; }
public int Top { get; set; }
public int Right { get; set; }
public int Bottom { get; set; }
}
private void Form2_Load(object sender, EventArgs e)
{
Thread childThread = new Thread(new ThreadStart(method));
childThread.Start();
}
public void method()
{
Rect move = new Rect();
move.Left = 100;
move.Top = 100;
this.Invoke(new MethodInvoker(() => { mover(move); }));
}
public void mover(Rect move)
{
this.Left = move.Left;
this.Top = move.Top;
}
}
You should create a method
void SetLeft(int left)
{
this.Left = left;
}
That you can invoke it from any thread:
if (this.InvokeRequired) {
this.Invoke(new MethodInvoker(() => { SetLeft(10); } );
} else {
SetLeft(10);
}