I come from embedded C programming and I don't absolutely know anything about oop, task, threads, ecc..
I'm figuring out the basics concepts of C# and oop and wanted to try coding a simple stopwatch.
Goal: Toggle the stopwatch state with a button and show the value inside the button itself.
So far I've come to this:
namespace Cronometro
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
public class MyTimerClass
{
private DateTime TimeValue;
private System.Timers.Timer TimerObj;
private const int TimeInterval = 1;
public MyTimerClass()
{
TimeValue new DateTime(0, 0, 0, 0, 0, 0, 0, DateTimeKind.Local);
TimerObj = new System.Timers.Timer(TimeInterval);
TimerObj.AutoReset = true;
TimerObj.Elapsed += TimerObj_Elapsed;
}
private void TimerObj_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
TimeValue = TimeValue.AddMilliseconds((double)TimeInterval);
}
public void Start()
{
TimerObj.Start();
}
public void Stop()
{
TimerObj.Stop();
}
}
}
Come to this point, I just wanted to print the value with Button.Content but seems like I cannot use it outside of the MainWindow class.
What should I do? I started thinking about creating a task inside the main class and wait for the timer event to print the value but I know nothing about threads and tasks.
It's unclear exactly what you mean, as at the moment you can't use it in the MainWindow class at all. Just expose the value via a public function, and you can fetch the instance's value anywhere you want.
namespace Cronometro
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
var x = new MyTimerClass(); // <<<
x.Start(); // <<<
var timeValue = x.GetTimeValue(); // <<<
}
}
public class MyTimerClass
{
private DateTime TimeValue;
private System.Timers.Timer TimerObj;
private const int TimeInterval = 1;
public MyTimerClass()
{
TimeValue new DateTime(0, 0, 0, 0, 0, 0, 0, DateTimeKind.Local);
TimerObj = new System.Timers.Timer(TimeInterval);
TimerObj.AutoReset = true;
TimerObj.Elapsed += TimerObj_Elapsed;
}
private void TimerObj_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
TimeValue = TimeValue.AddMilliseconds((double)TimeInterval);
}
public DateTime GetTimeValue() // <<<
{ // <<<
return TimeValue; // <<<
} // <<<
public void Start()
{
TimerObj.Start();
}
public void Stop()
{
TimerObj.Stop();
}
}
First, you don't have to write your own Stopwatch class. DotNet has its own:
https://learn.microsoft.com/en-us/dotnet/api/system.diagnostics.stopwatch?view=netframework-4.8
Second, try to understand async/await pattern:
https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/async/
So, you can do something like this:
https://wouterdekort.com/2013/10/01/async-and-await-and-the-ui-thread/
One way you could populate the button text is you create an event in the MyTimerClass class, and attach a listener delegate to it from inside the MainWindow class.
Then, fire the event in TimerObj_Elapsed in MyTimerClass, so that the event handler in MainWindow will be called, and you will have a chance to populate the button.
However, there is something other than "practicing class" you have to deal with. Because you are using a thread pool timer, so the Elapsed event handler of the timer is invoked from a worker thread. In a GUI app, only the main / UI thread can interactive with controls. So chances are, you will get an exception when trying to set Button.Text. You will need to marshal the set action to the UI thread.
Frankly, if you are practicing writing classes in C#, I'd recommend you using a Console App to start with rather than a GUI app. It is probably not a good idea if people throwing out too many concepts on the table that you don't know at the same time.
Related
I'm making a service. Among other things, it must read the value from registry and configure "Primary" timers' interval according to this value. Now it should do it not only in constructor phase, but also be able to change this interval when it has been changed in registry. How do I do that?
In other words, I have a code like this:
using System.Timers;
public static class GVARS
{
public static Config valFromRegistry = new ConfigRegistry().Result;
}
class Program
{
static void Main(string[] args)
{
int timerPeriod = (int)(uint)GVARS.valFromRegistry.Interval * 60000;
Timer primaryTimer = new Timer(timerPeriod);
primaryTimer.Interval = timerPeriod;
primaryTimer.Elapsed += RunPrimaryPayload;
primaryTimer.Start();
Console.ReadLine();
}
private static void RunPrimaryPayload(object sender, ElapsedEventArgs eventArgs)
{
SomeClass.SomeMethod(someArgs);
GVARS.valFromRegistry.Interval = (int)someNewValue;
}
}
Somewhere down the code I might change valFromRegistry.Interval and I want primaryTimer to respond to those changes. How do I get there?
PS. It's not really important to me how I'm gonna check for changes in registry, be it another timer or event. Whatever works.
PS2. I have a possible idea to use System.Threading.Timer for primaryTimer instead of System.Timers.Timer since the former has method Change() which I might call from another little configrationUpdateTimer every minute or so and make primaryTimer respond to configuration changes this way. Am I right?
Ok, I've managed to solve my problem by creating class with 2 Timers: one checking for config changes (master) and another (slave) is doing actual payload:
public class UniversalTimer
{
public System.Threading.Timer MasterTimer;
public System.Threading.Timer SlaveTimer;
private int lastMainTimerInterval = 0;
public UniversalTimer()
{
MasterTimer = new System.Threading.Timer(new System.Threading.TimerCallback(MasterTimerCallback), null, 0, 60000);
SlaveTimer = new System.Threading.Timer(new System.Threading.TimerCallback(SlaveTimerCallback), null, System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite);
}
private void MasterTimerCallback(object obj)
{
Log.Debug("[{0}:{1}:{2}] Re-reading configuration from registry", "Program", "UniversalTimer", "MasterTimerCallback");
var currentSettings = new ConfigRegistry().Result;
if ((int)currentSettings.Interval != lastMainTimerInterval)
{
Log.Debug("[{0}:{1}:{2}] Setting interval for Main Timer", "Program", "UniversalTimer", "SlaveTimerCallback");
lastMainTimerInterval = (int)currentSettings.Interval;
this.MainTimer.Change(0, (int)currentSettings.Interval * 60000);
}
}
private void SlaveTimerCallback(object obj)
{
Log.Debug("[{0}:{1}:{2}] Executing main cycle", "Program", "UniversalTimer", "SlaveTimerCallback");
}
public void Dispose()
{
MasterTimer.Dispose();
SlaveTimer.Dispose();
}
}
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
}
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.
I want to have a timer in my windows phone 8 app, that´s counting/running independent of current shown page.
It should connect to server - when possible in a UI independet task/thread - and store data in a global object/list.
The Independence from current shown page is my point.
I tried following in App.xaml.cs:
public partial class App : Application
{
// creating timer instance
DispatcherTimer gAppTimer = new DispatcherTimer();
// timer interval specified as 1 minute
gAppTimer.Interval = TimeSpan.FromSeconds(60);
// Sub-routine OnTimerTick that will be called at specified intervall
gAppTimer.Tick += OnTimerTick;
// starting the timer
gAppTimer.Start();
public void OnTimerTick(Object sender, EventArgs args)
{
// text box property is set to current system date.
// ToString() converts the datetime value into text
MessageBox.Show("TIMER fired");
}
:
:
But this doesn´t work. Than I tried just declaring the object in App.xaml.cs:
public partial class App : Application
{
// creating timer instance
DispatcherTimer gAppTimer = new DispatcherTimer();
public void OnTimerTick(Object sender, EventArgs args)
{
// text box property is set to current system date.
// ToString() converts the datetime value into text
MessageBox.Show("TIMER fired");
}
:
:
And on my startpage.xaml.cs:
// timer interval specified as 1 minute
App.gAppTimer.Interval = TimeSpan.FromSeconds(60);
// Sub-routine OnTimerTick that will be called at specified intervall
App.gAppTimer.Tick += OnTimerTick;
// starting the timer
App.gAppTimer.Start();
But this doesn´t work, too.
Any ideas how to handle my Problem? What I don´t want to use is a Background Task, because it runs only every 30 minutes. My solution should only run, if the app is "active" (in foreground).
That's normally done using a static or singleton class. Both will be global and you'll have access to them from every page.
Also, the DispatcherTimer invokes it's TimerTick method on the UI thread. If you don't need to be in the UI thread, you should use a System.Threading.Timer, which invokes a method in a background thread.
Here's an example:
public static class SomeManager {
private static Timer gAppTimer;
private static object lockObject = new object();
public static void StartTimer() {
if (gAppTimer == null) {
lock (lockObject) {
if (gAppTimer == null) {
gAppTimer = new Timer(OnTimerTick, null, 60 * 1000, 60 * 1000);
}
}
}
}
public static void StopTimer() {
if (gAppTimer != null) {
lock (lockObject) {
if (gAppTimer != null) {
gAppTimer.Change(Timeout.Infinite, Timeout.Infinite);
gAppTimer = null;
}
}
}
}
private static void OnTimerTick(object state) {
Action();
}
public static void Action() {
// Do what you need to do
}
}
Just call SomeManager.StartTimer() from your first page or from App.xaml.cs and the timer will start.
Update
I updated the code a little:
Renamed the Initialize method to StartTimer.
Added StopTimer method which stops the timer. You can then start it again by calling SomeManager.StartTimer.
Added Action method which is the one actually donig the work. You can invoke it from anywhere, anytime.
Note: the the timer will call this method in a background thread and you should do the same using something like Task.Run(() => SomeManager.Action());
Added a lock to ensure that the Start/Stop methods will not throw exceptions if invoked from multiple threads at the same time.
I'm not sure how you have arranged your code, but as I've tried:
public partial class App : Application
{
public static PhoneApplicationFrame RootFrame { get; private set; }
public DispatcherTimer gAppTimer = new DispatcherTimer();
public void OnTimerTick(Object sender, EventArgs args)
{
MessageBox.Show("TIMER fired");
}
public App()
{
gAppTimer.Interval = TimeSpan.FromSeconds(2);
// Sub-routine OnTimerTick that will be called at specified intervall
gAppTimer.Tick += OnTimerTick;
// starting the timer
gAppTimer.Start();
// rest of the code
the above code works. MessageBox shows every 2 seconds, if you had declared your DispatcherTimer as public, then you will be able to access it like this:
(App.Current as App).gAppTimer.Stop();
Note also that depending on what you want to achieve you may also use System.Threading.Timer.
On the other hand you may also think of using public static DispatcherTimer somewhere.
Let me first confess that I am a fairly green programmer but I am in dire straits trying to figure out what is wrong with my application.
The goal so far is to make a timer kick off when the button is clicked and the elapsed time continually display on the text box.
There are probably better ways to implement this but humor me for a second and I practice creating events and using them in programs.
What I see happening when I launch the code is that it just freezes and never recovers, I need to end the app with the task manager.
Any pointers on what I may be doing wrong and how to fix it will be appreciated.
// see clock class below containing delegate and event instantiation
public class Clock
{
public delegate void TimeChangedHandler(object clock, TimeEventArgs timeInfo);
public TimeChangedHandler TimeChanged;
public void RunClock()
{
TimeEventArgs e = new TimeEventArgs();//initialize args
while (e.keepCounting)
{
Thread.Sleep(1000);
e.EndTime = DateTime.Now;
if (e.StartTime != e.EndTime)
{
e.duration = e.EndTime.Subtract(e.StartTime);
}
if (TimeChanged != null)
{
TimeChanged(this, e);
}
}
}
//see timeevent args description below:
public class TimeEventArgs : EventArgs
{
public TimeSpan duration;
public DateTime StartTime { get; set; }
public DateTime EndTime { get; set; }
public bool keepCounting = false;
public TimeEventArgs()
{
StartTime = DateTime.Now;
EndTime = DateTime.Now;
keepCounting = true;
}
}
//See form class below:
public partial class TimeApp : Form
{
public TimeApp()
{
InitializeComponent();
}
private void startStopButton_Click(object sender, EventArgs e)
{
var theClock = new Clock();
var timeApp = new TimeApp();
timeApp.Subscribe(theClock);
theClock.RunClock();
}
public void Subscribe(Clock theClock)
{
theClock.TimeChanged += new Clock.TimeChangedHandler(NewTime);
}
public void NewTime(object theClock, TimeEventArgs e)
{
displayBox.Text = e.duration.Hours.ToString() + ":"
+ e.duration.Minutes.ToString() + ":" + e.duration.Seconds.ToString();
}
}
Your RunClock method blocks the UI (because of the Thread.Sleep(1000); call), which makes it impossible to stop.
Instead of looping, you should look at adding a Windows.Forms.Timer to your form, and using it to drive the clock.
You are suspending your main (UI) thread when calling Thread.Sleep(1000) - which is why your app is non-responsive.
Use a Timer (instead of Thread.Sleep()) and spin off any processing/long running code to a BackgroundWorker with for any processing you need to do. That way, your UI will stay responsive.