What I want
I want to do something whenever the timezone in Windows is changed.
What I have so far
For this purpose I have implemented the event SystemEvents.TimeChanged as follows:
In the constructor:
SystemEvents.TimeChanged += SystemEvents_TimeChanged;
The event body:
void SystemEvents_TimeChanged(object sender, EventArgs e)
{
MessageBox.Show("Test1");}
What is the problem
The event is not fired when the time or the time zone is changed in Windows.
What I have tried
When I write the above code in a clean WinForms application, everything works as expected. But not in my application, which of cause contains a lot of other code.
I do not see that I have any other events, which should block the firing of the above event.
My question is
Does anyone have an idea what could cause the above code to not be firing in my application, but work as expected when I create a new project/application only containing the above code?
UPDATE 1
It happens because I show a splash screen in a separate thread before calling
Application.Run(new FormMain());
Then SystemEvents sticks to the thread created by the splash screen, even this thread is terminated when the application has loaded.
The question is now if there is a way to tell SystemEvents that is should now use the "correct" UI thread when the application has loaded?
This answer regarding to question UPDATE 1 part, hence the provided code example in the original question is working.
I have spent some time to figure this it out. Hence I do not have full overview of your code I have improvised 2 WinForms for this solution (one as splash and the other as main), of course this is just example to illustrate the concept.
From what I understand you when you start your software, it starts with splash part as separate thread and when splash is done, than FormMain start afterwards. You can do something better, use ApplicationContext. You make your own context class that is extended from ApplicationContext and in that class you declare your Splash and FormMain with their respective own logic. Now in your case you need to make sure FormMain starts at some point after Splash or some thing like that (I do not know how your software works/flow).
In context class you create the methods to subscribe and unsubscribe to SystemEvents.TimeChanged so you can listen to time changing. I have for demonstration purpose also created a BindingList to demonstrate time changes.
Now lets show some code:
public static void Main()
{
// use own created context
MainApplicationContext context = new MainApplicationContext();
Application.Run(context);
}
// just quick way to demonstrate how we collect time changes
public static BindingList<string> Logs { get; private set; }
private class MainApplicationContext : ApplicationContext
{
private int _formCount;
public MainApplicationContext()
{
Logs = new BindingList<string>();
_formCount = 0;
// splash screen
var splash = new FormSplash();
splash.Closed += OnFormClosed;
splash.Load += OnFormOpening;
splash.Closing += OnFormClosing;
_formCount++;
splash.Show();
// For demo, make some logic that close splash when program loaded.
Thread.Sleep(2000);
var main = new FormMain();
main.Closed += OnFormClosed;
main.Load += OnFormOpening;
main.Closing += OnFormClosing;
_formCount++;
splash.Close();
main.Show();
}
private void OnFormOpening(object sender, EventArgs e)
{
SystemEvents.TimeChanged += SystemEvents_TimeChanged;
}
private void OnFormClosing(object sender, CancelEventArgs e)
{
SystemEvents.TimeChanged -= SystemEvents_TimeChanged;
}
private void OnFormClosed(object sender, EventArgs e)
{
_formCount--;
if (_formCount == 0)
{
ExitThread();
}
}
private void SystemEvents_TimeChanged(object sender, EventArgs e)
{
var text = $"TimeChanged, Time changed; it is now {DateTime.Now.ToLongTimeString()}";
Logs.Add(text);
}
}
Now in our FormMain, create listbox call it LogListBox:
public FormMain()
{
InitializeComponent();
Load += ListChanged;
}
// this keep list of time changes events updated if changed this could be log or some thing else.
private void ListChanged(object sender, EventArgs e)
{
LogListBox.DataSource = Program.Logs;
}
And here how it works:
Documentation
https://learn.microsoft.com/en-us/dotnet/api/system.windows.forms.application.run?view=netframework-4.8
https://learn.microsoft.com/en-us/dotnet/api/microsoft.win32.systemevents?view=netframework-4.8
Before showing the splash screen in a separate thread, you can call the method bellow from the main thread, to associate this thread with the system thread that listens for system events.
private static void InitializeSystemEvents()
{
var timerId = SystemEvents.CreateTimer(1);
SystemEvents.KillTimer(timerId);
}
This way you will be able to subscribe later to SystemEvents from your main Form, which runs in the main thread.
Update: the root of the problem is that the system thread that listens for system events is killed when the splash screen is closed. So another way to solve this problem would be to keep the splash screen open (but hidden) during the whole lifetime of the application.
Related
I'm writing a simple game that uses timers from the system.threading namespace to simulate wait times for actions. My goal is to have the timer execute once every second for x amount of seconds. To achieve this I added a counter in the timer callback.
The problem is any code I place after invoking the DeliveryProgressChangedEvent event seems to get ignored. My counter is never incremented thus allowing the timer to run forever.
If I invoke the event after I increment the counter, everything works fine. Just nothing after invoking the event will execute. Rather than going the easy route I'd like to understand if not resolve this problem.
I did a fair bit of research into the system.threading timer object as well as events but wasn't able to find any information related to my issue.
I created a bare bones example of my project to demonstrate the issue below.
Game Class
class Game
{
private Timer _deliveryTimer;
private int _counter = 0;
public event EventHandler DeliveryProgressChangedEvent;
public event EventHandler DeliveryCompletedEvent;
public Game()
{
_deliveryTimer = new Timer(MakeDelivery);
}
public void StartDelivery()
{
_deliveryTimer.Change(0, 1000);
}
private void MakeDelivery(object state)
{
if (_counter == 5)
{
_deliveryTimer.Change(0, Timeout.Infinite);
DeliveryCompletedEvent?.Invoke(this, EventArgs.Empty);
}
DeliveryProgressChangedEvent?.Invoke(this, EventArgs.Empty);
++_counter;
}
}
Form Class
public partial class Form1 : Form
{
Game _game = new Game();
public Form1()
{
InitializeComponent();
_game.DeliveryProgressChangedEvent += onDeliveryProgressChanged;
_game.DeliveryCompletedEvent += onDeliveryCompleted;
pbDelivery.Maximum = 5;
}
private void onDeliveryProgressChanged(object sender, EventArgs e)
{
if (InvokeRequired)
pbDelivery.BeginInvoke((MethodInvoker)delegate { pbDelivery.Increment(1); });
MessageBox.Show("Delivery Inprogress");
}
private void onDeliveryCompleted(object sender, EventArgs e)
{
MessageBox.Show("Delivery Completed");
}
private void button1_Click(object sender, EventArgs e)
{
_game.StartDelivery();
}
}
EDIT
Just to clarify what I mean. Any code I put after DeliveryProgressChangedEvent?.Invoke(this, EventArgs.Empty); will not execute. In my example ++_counter will not run. The event does fire and the onDeliveryProgressChanged handler does run.
The problem:
Using a System.Threading.Timer class, when the TimerCallback is called, events are raised, to notify the subscribers of the DeliveryProgressChangedEvent and DeliveryCompletedEvent of custom Game class of the progress of a procedure and the termination of it.
In the sample class, the subscriber (a Form class, here) updates an UI, settings the value of a ProgressBar control and also showing a MessageBox (used in the actual implementation of the class sample shown here).
It appears that after the first event is invoked:
DeliveryProgressChangedEvent?.Invoke(this, EventArgs.Empty);
++_counter;
the line where the _counter should be increased is never reached, thus the code that inspects the _counter to set the Timer to a new value is never executed.
What happens:
The System.Threading.Timer is served by ThreadPool threads (more than one). Its callback is called on a thread other than the UI thread. The events invoked from the callback are also raised in a ThreadPool thread.
The code in the handler delegate, onDeliveryProgressChanged, is then run on the same Thread.
private void onDeliveryProgressChanged(object sender, EventArgs e)
{
if (InvokeRequired)
pbDelivery.BeginInvoke((MethodInvoker)delegate { pbDelivery.Increment(1); });
MessageBox.Show("Delivery Inprogress");
}
When the MessageBox is shown - it's a Modal Window - it blocks the Thread from where it's run, as usual. The code following the line where the event is invoked is never reached, so _counter is never increased:
DeliveryProgressChangedEvent?.Invoke(this, EventArgs.Empty);
++_counter;
The System.Threading.Timer can be served by more than one thread. I'm just quoting the Docs on this point, it's quite straightforward:
The callback method executed by the timer should be reentrant, because
it is called on ThreadPool threads. The callback can be executed
simultaneously on two thread pool threads if the timer interval is
less than the time required to execute the callback, or if all thread
pool threads are in use and the callback is queued multiple times.
What happens, in practice, is that while the Thread where the CallBack is executed, is blocked by the MessageBox, this doesn't stop the Timer from executing the CallBack from another thread: a new MessageBox is shown when the event is invoked and it keeps on running until it has resources.
The MessageBox has no Owner. When a MessageBox is shown without specifying the Owner, its class uses GetActiveWindow() to find an Owner for the MessageBox Window. This function tries to return the handle of the active window attached to the calling thread's message queue. But the thread from which the MessageBox is run has no active Window, as a consequence, the Owner is the Desktop (actually, IntPtr.Zero here).
This can be manually verified by activating (clicking on) the Form where the MessageBox is called: the MessageBox Window will disappear under the Form, since it's not owned by it.
How to solve:
Of course, using another Timer. The System.Windows.Forms.Timer (WinForms) or the DispatcherTimer (WPF) are the natural substitutes. Their events are raised in the UI Thread.
► The code presented here is just a WinForms implementation made to
reproduce a problem, hence these may not apply to all contexts.
Use a System.Timers.Timer: the SynchronizingObject property provides means to marshal the events back to the Thread that created the current class instance (same consideration in relation to the concrete implementation context).
Generate an AsyncOperation using the AsyncOperationManager.CreateOperation() method, then use a SendOrPostCallback delegate to let the AsyncOperation call the SynchronizationContext.Post() method (classic BackGroundWorker style).
BeginInvoke() the MessageBox, attaching it to the UI Thread SynchronizationContext. E.g.,:
this.BeginInvoke(new Action(() => MessageBox.Show(this, "Delivery Completed")));
Now the MessageBox is owned by the Form and it will behave as usual. The ThreadPool thread is free to continue: the Modal Window is synched with the UI Thread.
Avoid using a MessageBox for this kind of notifications, since it's really annoying :) There are many other ways to notify a User of status changes. The MessageBox is probably the less thoughtful.
To make them work as intended, without changing the current implementation, the Game and Form1 classes can be refactored like this:
class Game
{
private System.Threading.Timer deliveryTimer = null;
private int counter;
public event EventHandler DeliveryProgressChangedEvent;
public event EventHandler DeliveryCompletedEvent;
public Game(int eventsCount) { counter = eventsCount; }
public void StartDelivery() {
deliveryTimer = new System.Threading.Timer(MakeDelivery);
deliveryTimer.Change(1000, 1000);
}
public void StopDelivery() {
deliveryTimer?.Dispose();
deliveryTimer = null;
}
private void MakeDelivery(object state) {
if (deliveryTimer is null) return;
DeliveryProgressChangedEvent?.Invoke(this, EventArgs.Empty);
counter -= 1;
if (counter == 0) {
deliveryTimer?.Dispose();
deliveryTimer = null;
DeliveryCompletedEvent?.Invoke(this, EventArgs.Empty);
}
}
}
public partial class Form1 : Form
{
Game game = null;
public Form1() {
InitializeComponent();
pbDelivery.Maximum = 5;
game = new Game(pbDelivery.Maximum);
game.DeliveryProgressChangedEvent += onDeliveryProgressChanged;
game.DeliveryCompletedEvent += onDeliveryCompleted;
}
private void onDeliveryProgressChanged(object sender, EventArgs e)
{
this.BeginInvoke(new MethodInvoker(() => {
pbDelivery.Increment(1);
// This MessageBox is used to test the progression of the events and
// to verify that the Dialog is now modal to the owner Form.
// Of course it's not used in an actual implentation.
MessageBox.Show(this, "Delivery In progress");
}));
}
private void onDeliveryCompleted(object sender, EventArgs e)
{
this.BeginInvoke(new Action(() => MessageBox.Show(this, "Delivery Completed")));
}
private void button1_Click(object sender, EventArgs e)
{
game.StartDelivery();
}
}
Good information. The callback method executed by the timer should be reentrant, because it is called on ThreadPool threads. The callback can be executed simultaneously on two thread pool threads if the timer interval is less than the time required to execute the callback, or if all thread pool threads are in use and the callback is queued multiple times.
Long story short, due to shifting business requirements, I need to be able to show the end user the progress of a file archival process controlled by a C# console application. The console app essentially gets a list of local files from a db and then copies them to an archive location. This was originally supposed to be a background process triggered by a scheduled task. Now, however, users can launch it manually with various arguments from the command line, so I was recently tasked with letting the user know the status of the archive process.
I thought I would just use a WPF ProgressBar control for this, but now I'm going in circles trying to sort out the best way to do this. I've been working with the answer from #JamesWilkins here: WPF window from a Console project?
I've added the ProgressBar window to the console application, and added the following to the Main method in the console(super simplified for clarity):
[STAThread]
static void Main(string[] args)
{
// EXISTING CONSOLE LOGIC
ParseCommandLineArgs();
Configure();
// ADDED
InitializeWindows(); // opens the WPF window and waits here
// EXISTING CONSOLE LOGIC
BeginArchival();
}
static void InitializeWindows()
{
WinApp = new Application();
WinApp.Run(ProgressBar = new ProgressBar()); // blocking call
}
Then in the ProgressBar.xaml code behind:
public partial class ProgressBar : Window
{
public ProgressBar()
{
InitializeComponent();
}
private void ProgressBar_OnContentRendered(object sender, EventArgs e)
{
BackgroundWorker worker = new BackgroundWorker();
worker.WorkerReportsProgress = true;
worker.DoWork += worker_DoWork;
worker.ProgressChanged += worker_ProgressChanged;
worker.RunWorkerAsync();
}
private void worker_DoWork(object sender, DoWorkEventArgs e)
{
// foreach file that is archived, report progress to the
// ProgressBar.
for (int i = 0; i < 100; i++)
{
(sender as BackgroundWorker).ReportProgress(i);
}
}
private void worker_ProgressChanged(object sender,
ProgressChangedEventArgs e)
{
PbArchiveStatus.Value = e.ProgressPercentage;
}
}
The process waits at the InitializeWindows() method until the progress bar is closed, so it doesn't hit any of the archive logic that I need the progress bar to show progress for. It seems that I essentially need to put all of the existing console logic inside the ProgressBar.worker_DoWork() method, but at this point my brain is starting to hurt so I thought I'd reach out.
Am I on the right track, or is there a better way to add a GUI-based progress bar to a console utility? Let me know if I can clarify anything at all.
I hand-rolled a MVC-style implementation of a game that I want to autoplay. By "autoplay" I mean that the buttons that normally a user would click while playing I want a controller to automatically initiate. That way I can watch the game play itself for quality control reasons. This particular game has a lot of code, so instead of providing it as an example I've created a silly HelloWorld example using the same approach.
Before I provide the example, here is my issue: everything you see below is functional, and "works"; except for one thing: I'm unable to shut-off the autoplay because the UI becomes unresponsive and the button to turn it off won't respond to a click event.
First create a .Net 4.6.1 winforms project in a solution. (.net version probably doesn't matter as long as it is >= 4.5). Create a Form that looks like this:
In the code behind, copy paste this: (change names as needed to compile)
using System;
using System.Threading;
using System.Windows.Forms;
namespace WinformsExample
{
public partial class HelloWorldView : Form
{
private readonly HelloWorldController MyHelloWorldController;
public HelloWorldView()
{
InitializeComponent();
MyHelloWorldController = new HelloWorldController();
}
private void button1_Click(object sender, EventArgs e)
{
MyHelloWorldController.HelloWorldRequested();
if (MyHelloWorldController.IsAutomated)
{
Thread.Sleep(2000);
button1.PerformClick();
}
}
private void HelloWorldView_Load(object sender, EventArgs e)
{
MyHelloWorldController.HelloWorldRequestedEvent += OnHelloWorldRequested;
}
private void OnHelloWorldRequested(HelloWorldParameters parameters)
{
textBox1.Text += parameters.HelloWorldString + Environment.NewLine;
textBox1.Update();
}
private void button2_Click(object sender, EventArgs e)
{
MyHelloWorldController.IsAutomated = !MyHelloWorldController.IsAutomated;
if (MyHelloWorldController.IsAutomated)
{
button2.Text = "hello world - is on";
button2.Update();
button1.PerformClick();
}
else
{
button2.Text = "hello world - is off";
button2.Update();
}
}
}
}
And create a class titled HelloWorldController.cs and copy paste this in to it:
namespace WinformsExample
{
public class HelloWorldParameters
{
public string HelloWorldString { get; set; }
}
public delegate void HelloWorldEventHandler(HelloWorldParameters parameters);
public class HelloWorldController
{
private readonly HelloWorldParameters _parameters;
public event HelloWorldEventHandler HelloWorldRequestedEvent;
public bool IsAutomated { get; set; }
public HelloWorldController()
{
_parameters = new HelloWorldParameters();
}
public void HelloWorldRequested()
{
_parameters.HelloWorldString = "Hello world!!";
if (HelloWorldRequestedEvent != null)
HelloWorldRequestedEvent(_parameters);
}
}
}
...go ahead and rename things if you need to. Now build the program. Click the first button. You will see "hello world". Now click the second button, you will see "hello world" printed every 2 seconds.
The way I thought this would work is that by clicking button2 a second time, that it would stop the autoplay. However, the UI is unresponsive and the button click event never happens.
What is going on here that is causing the UI to be unresponsive and how can I fix it so that I get the intended behavior?
*UPDATE - HERE IS THE SOLUTION *
Keep everything the same as above except for HelloWorldView.cs. Remove the call to Thread.Sleep(). Drag and drop a timer from the toolbox to the design surface. You will see an icon on the bottom of the designer surface labeled
timer1
Copy paste the following code in to HelloWorldView.cs. Compile and execute. If everything is correct you should be able to turn on and off the "hello world" display by clicking the button at any time - the UI stays responsive.
using System;
using System.Windows.Forms;
namespace WinformsExample
{
public partial class HelloWorldView : Form
{
private readonly HelloWorldController MyHelloWorldController;
public HelloWorldView()
{
InitializeComponent();
MyHelloWorldController = new HelloWorldController();
}
private void onTimerTick(object sender, EventArgs e)
{
button1.PerformClick();
}
private void OnHelloWorldRequested(HelloWorldParameters parameters)
{
textBox1.Text += parameters.HelloWorldString + Environment.NewLine;
textBox1.Update();
}
private void HelloWorldView_Load(object sender, EventArgs e)
{
MyHelloWorldController.HelloWorldRequestedEvent += OnHelloWorldRequested;
}
private void button1_Click(object sender, EventArgs e)
{
MyHelloWorldController.HelloWorldRequested();
}
private void button2_Click(object sender, EventArgs e)
{
MyHelloWorldController.IsAutomated = !MyHelloWorldController.IsAutomated;
if (MyHelloWorldController.IsAutomated)
{
button2.Text = "hello world - is on";
button2.Update();
timer1.Interval = 2000;
timer1.Tick += onTimerTick;
timer1.Start();
}
else
{
timer1.Stop();
button2.Text = "hello world - is off";
button2.Update();
}
}
}
}
WinForms uses a single message pump thread (called the UI thread). (If you are unfamiliar with the concept you should research Windows messages and Windows message pump).
Thread.Sleep causes the currently executing thread the sleep, or pause, for a time. This sleep/pause is like death to the thread - it is aware of nothing and unable to do anything.
As the currently executing thread in a WinForms app is usually the UI thread - Thread.Sleep will cause the UI to become unresponsive because it is no longer able to pump messages.
An alternative design would be to use a form-based Timer. Place your game playing code in the Timer's Tick event.
What is going on here that is causing the UI to be unresponsive and how can I fix it so that I get the intended behavior?
There are essentially two reasons why your app becomes unresponsive.
1. Thread.Sleep() in UI thread
GUI applications on Windows are generally driven by messages (mouse clicks; keyboard; screen drawing) posted to it which are placed on a queue. The UI thread processes these messages one by one dispatching the message to the appropriate handler. In this way it is known as the Message Pump. If during processing one of these messages too much time elapses, then the UI will appear to freeze. Event handlers should be as fast as possible.
During your click handlers you are using Thread.Sleep(2000); which will prevent the UI thread from updating the UI of your application, in essence simulating an event handler that takes far too long to process an event. It is perhaps no different to say performing a lengthy database or WCF operation on the UI thread, hence why people tend to put such calls on a separate thread or task.
Recommend you remove the Thread.Sleep and replace it with a timer as others have indicated.
2. Infinite Recursive Loop on button1 handler
When you click button2 for the first time, the click handler for button2 is invoked where automation is enabled. You then simulate button1 being clicked via button1.PerformClick();.
During the call to button1.PerformClick, the click handler for button1 button1_Click() is invoked. It is there that you sleep for 2 seconds (which isn't healthy for the UI) but the secondary problem is that you immediately call button1.PerformClick(); from inside the button1 click handler, in essence setting up an infinite recursive loop.
If you were to remove the Thread.Sleep(2000) your app will eventually lead to a StackOverflowException. Your code as it stands now (even with the sleep) will still overflow, it's just that it will take much longer to become apparent.
Again, consider replacing it with a timer.
3. Exclusivity
It's important to note that ignoring the stack fault for a moment, the design is such that your app can't do anything else whilst this infinite loop is running. So if your game had other buttons to click; scores to display; sound effects to play; all from the point of view of the button2 handler, most likely it will never happen because it is too busy exclusively processing button1.
Conclusion
Keep UI responsive: Avoid Thread.Sleep() in your code
Avoid recursion: Don't use PerformClick() for a button whilst you are inside the click handler for said button
Your "Thread.Sleep()" call puts the UI thread to sleep. Use a Timer instead. Then terminate the Timer on the second press. (You could also do this with Tasks, if you want to use another thread you need to make the 2 threads communicate in someway so that the UI thread is the only one actually updating the UI)
Desktop applications have a so called UI thread. It's basically an infinite loop which keeps checking if something happened, such as a mouse click, and redraws the window if needed. Coding in WinAPI you would need to write this loop yourself, WinForms and other UI frameworks hide it away. But your click handler is called from inside this loop. So if your code takes too much time - like, because you call Thread.Sleep inside - the loop will not continue and will not be able to process anything that is happening to the application. This why long-running processes need to take place on a separate thread.
As others have said, you are blocking the UI thread with the Thread.Sleep and recursive button1.PerformClick(); call. You have to let the UI run as freely as possible and let it go idle quickly.
So, just for the fun of it I have rewritten your code to do just that. I've also implemented it with Microsoft's Reactive Extensions (Rx) - just NuGet "Rx-WinForms" to get the bits. Rx allows you to do some very funky things that you can't easily do with events.
Here's your form now:
public partial class HelloWorldView : Form
{
private readonly HelloWorldController MyHelloWorldController =
new HelloWorldController("Hello world!!", TimeSpan.FromSeconds(1.0));
public HelloWorldView()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
MyHelloWorldController.Messages
.ObserveOn(this)
.Subscribe(message =>
{
textBox1.Text += message + Environment.NewLine;
});
MyHelloWorldController.IsAutomateds
.ObserveOn(this)
.Subscribe(isAutomated =>
{
button2.Text = "hello world - is " + (isAutomated ? "on" : "off");
});
}
private void button1_Click(object sender, EventArgs e)
{
MyHelloWorldController.Trigger();
}
private void button2_Click(object sender, EventArgs e)
{
MyHelloWorldController.IsAutomated = !MyHelloWorldController.IsAutomated;
}
}
You'll notice that I've simplified down the UI. It really does as little as possible to update itself and to notify the HelloWorldController of its actions.
The worst part of the code are the two .Subscribe calls in Form1_Load. These are simply looking at the two observables (Rx's version of events if you like) and makes sure the events are run on the UI thread with the .ObserveOn(this) call, and then they subscribe to values produced from the HelloWorldController.
The UI is simply updating itself from the controller and telling the controller what it is doing. There is virtually no logic being performed in the UI. This is how it should be with any MVC-style coding.
Now the HelloWorldController is where the fun is.
It starts off pretty simply:
private string _message;
private TimeSpan _automatedPeriod;
public HelloWorldController(string Message, TimeSpan automatedPeriod)
{
_message = Message;
_automatedPeriod = automatedPeriod;
}
This is basically the information about what message to send to the UI and how often when the controller is automating the values.
It then tracks whether it is automated or not:
private bool _isAutomated = false;
Now it contains the Rx observables - these are like the events you were using.
private Subject<string> _messages = new Subject<string>();
public IObservable<string> Messages { get { return _messages.AsObservable(); } }
private Subject<bool> _isAutomateds = new Subject<bool>();
public IObservable<bool> IsAutomateds { get { return _isAutomateds.AsObservable(); } }
private SerialDisposable _serialSubscription = new SerialDisposable();
In Rx an IObservable<T> is something I can subscribe to to get a series of values - just like an event. The Subject<T> is something that I can manually push values into, but it also can be an IObservable<T> that can be subscribed to. It's the pair of these that lets me raise events. Think of the Subject<string> to be the equivalent of the HelloWorldRequested method in your code and the IObservable<string> to be the equivalent of the HelloWorldRequestedEvent event.
If I call _messages.OnNext("Hello") then any subscribers to IObservable<string> Messages would get a "Hello" sent to them. Just like an event.
IsAutomated looks like this:
public bool IsAutomated
{
get { return _isAutomated; }
set
{
_isAutomated = value;
_isAutomateds.OnNext(value);
if (_isAutomated)
{
this.Trigger();
}
}
}
So it does its job of updating its own internal state, but it also calls _isAutomateds.OnNext(value) to push out the updates to any subscribers of IObservable<bool> IsAutomateds. It also works out if it needs to trigger the controller to produce messages with the this.Trigger() call.
Finally the Trigger method looks like this:
public void Trigger()
{
if (_isAutomated)
{
_serialSubscription.Disposable =
Observable
.Interval(_automatedPeriod)
.StartWith(0)
.TakeUntil(_isAutomateds.Where(x => x == false))
.Subscribe(n => _messages.OnNext(_message));
}
else
{
_messages.OnNext(_message);
}
}
The easy part of this is when the _isAutomated is false then it simply sends one message out via the _messages.OnNext(_message) call.
When _isAutomated is true it uses some of the coolness of Rx to set up effectively a timer to produce values every TimeSpan _automatedPeriod. From your code you wanted every 2 seconds so the TimeSpan would be TimeSpan.FromSeconds(2.0).
Observable.Interval(_automatedPeriod) defines a timer that begins producing values after the first period of time and then every period of time between.
So the .StartWith(0) says that it should immediately produce a value when it is subscribed to.
The .TakeUntil(_isAutomateds.Where(x => x == false)) is the best part here - it says that it will take the values from the the Observable.Interval(_automatedPeriod).StartWith(0) and stop when it gets a value from _isAutomateds.Where(x => x == false) - in other words when the IsAutomated is set to false.
The .Subscribe(n => _messages.OnNext(_message)); simply pushes a value to the _messages subject so that all subscribers of IObservable<string> Messages gets their messages.
Just put all of the HelloWorldController I've given you in public class HelloWorldController { ... } and you're good to go.
The works I think like it should and shows how lightweight the UI code can be.
I hope you find this worth playing with.
You'll need to add these using's to the top of your code to get all of the code to compile:
using System.Reactive.Disposables;
using System.Reactive.Linq;
using System.Reactive.Subjects;
I know this has probably already been asked, but I am unsure of what to search for. I have a Windows Form Application and I want to run some code after everything has loaded and is visable. My application only has a label. I want to run this code after you can read the label and the window is showing.
You can subscribe and use Form.Shown event.
Use form shown event.
public MainForm()
{
this.Shown += new System.EventHandler(this.MainForm_Shown);
}
private void MainForm_Shown(object sender, EventArgs e)
{
MessageBox.Show("Everything is loaded");
}
You can either use the Form.Shown-Event (already mentioned) or use a Thread to accomplish what you want.
I prefer threading because your Form is this responsible when it's running the code in background.
Look at this tutorial for more info to Threading.
So your code should look like this:
public MainForm()
{
Thread t = new Thread (myMethodWithCode);
t.Start();
}
private void myMethodWithCode()
{
//MY CODE
}
In my application I am using a timer to check for updates in an RSS feed, if new items are found I pop up a custom dialog to inform the user. When I run the check manually everything works great, but when the automatic check runs in the timers Elapsed event the custom dialog is not displayed.
First of all is this a thread issue? (I am assuming it is because both the manual and automatic check use the same code).
When I run the automatic check, do I have to invoke the method that runs the check from the Timers Elapsed event handler?
Is there something I need to do in my custom dialog class?
Edit:
this is a winforms application.
Here is an example of what the code is like. (Please don't point out syntax errors in this code example, this is just a simple example not real code).
public class MainForm : System.Windows.Forms.Form
{
//This is the object that does most of the work.
ObjectThatDoesWork MyObjectThatDoesWork = new ObjectThatDoesWork();
MyObjectThatDoesWork.NewItemsFound += new NewItemsFoundEventHandler(Found_New_Items);
private void Found_New_Items(object sender, System.EventArgs e)
{
//Display custom dialog to alert user.
}
//Method that doesn't really exist in my class,
// but shows that the main form can call Update for a manual check.
private void Button_Click(object sender, System.EventArgs e)
{
MyObjectThatDoesWork.Update();
}
//The rest of MainForm with boring main form stuff
}
public class ObjectThatDoesWork
{
System.Timers.Timer timer;
public ObjectThatDoesWork()
{
timer = new System.Timers.Timer();
timer.Interval = 600000;
timer.AutoReset = true;
timer.Elapsed += new new System.Timers.ElapsedEventHandler(TimeToWork);
timer.Start();
}
private void TimeToWork(object sender, System.Timers.ElapsedEventArgs e)
{
Update();
}
public void Update()
{
//Check for updates and raise an event if new items are found.
//The event is consumed by the main form.
OnNewItemsFound(this);
}
public delgate void NewItemsFoundEventHandler(object sender, System.EventArgs e);
public event NewItemsFoundEventHandler NewItemsFound;
protected void OnNewItemsFound(object sender)
{
if(NewItemsFound != null)
{
NewItemsFound(sender, new System.EventArgs());
}
}
}
After reading some of the comments and answers, I think my problem is that I am using a System.Timers.Timer not a System.Windows.Forms.Timer.
EDIT:
After changing to a Forms.Timer initial testing looks good (but no new items exist yet so have not seen the custom dialog). I added a bit of code to output the thread ID to a file when the update method is called. Using the Timers.Timer the thread ID was not the GUI thread, but using the Forms.Timer the thread ID is the same as the GUI.
Which timer are you using? System.Windows.Forms.Timer automatically fires the event on the UI thread. If you are using other one you will need to use Control.Invoke to call the method on UI thread.
You should use Forms.Timer here, or if you use other kind of timers, serialize calls to UI with .Invoke()
Is your application a WPF-Application? If so, you must delegate the work from your background-thread to the Dispatcher associated with the UI thread.
Post some code, so you can get better help and have a look at the Dispatcher class http://msdn.microsoft.com/en-us/library/system.windows.threading.dispatcher.invoke.aspx
private static System.Threading.SynchronizationContext _UI_Context;
//call this function once from the UI thread
internal static void init_CallOnUIThread()
{
_UI_Context = System.Threading.SynchronizationContext.Current;
}
public static void CallOnUIThread(Action action, bool asynchronous = false)
{
if (!asynchronous)
_UI_Context.Send((o) =>
{
action();
}, null);
else
_UI_Context.Post((o) =>
{
action();
}, null);
}