Reputation: 2086
I am trying to develop a simple application that must have GUI components. It will be a service in the taskbar tray but must query the database every few minutes to check for changes, then post to a web server the results. It will run 24/7.
This is my first application, and so have been getting some help along the way from SO. When I first used the FluentScheduler, I had trouble (C# FluentScheduler Job not Repeating) but got it working as a simple proof of concept with a console app.
As I tried taking what I had learned and implementing it with my Windows Forms solution, I couldn't get it working at all because once it ran the Application.Run(ThisForm);
command, the scheduler did nothing. Eventually while troubleshooting, I stumbled across this:
https://github.com/fluentscheduler/FluentScheduler/issues/169
I see that you're using the library from something like a Windows Forms/WPF application. Starting threads/tasks from a GUI application is a pain in the ***, maybe that's what's biting you. Fingers crossed to be something else, diving up on STA threads, dispatchers, synchronization contexts and alikes is no fun.
So now I am left wondering what I am supposed to do? Am I supposed to develop the scheduled tasks as a console app leaving an API for a WPF application to communicate with, or am I supposed to work through the pain he is describing and make it work within WPF?
As this is my first C# project, it seems pretty complicated to separate the two components, but am willing to learn if that is the right choice. I am still very early in the project just doing proof of concepts of each needed feature and so can easily switch to WPF, UWP, or whatever else is most appropriate. It will have minimal GUI, just a few forms to fill out username / password type stuff and options to sync.
Even though this FluentScheduler has about a quarter million downloads, maybe there is a better one that doesn't suffer from the same limitations you could recommend.
Upvotes: 0
Views: 1716
Reputation: 25623
Based on the earlier post you linked to, I see a few problems with your code:
Your call to JobManager.Initialize
is unreachable because it occurs after Application.Run
, which blocks until the application shuts down (e.g., when the last window is closed).
The FluentScheduler
will schedule your job to run on an arbitrary worker thread, but your action accesses or manipulates UI elements. In both WPF and Windows Forms, you can only touch UI elements from the main thread. If your job needs to touch the UI, it must first marshal itself back onto the UI thread.
The scheduled action in your original post does not make sense:
Action someMethod = new Action(() =>
{
Form1 ThisForm = new Form1();
ThisForm.Text ="HELLO";
});
Specifically, you are creating a new window that is never shown, rather than modifying one that already exists.
Here is a simple example project that you should be able to use as a starting point. It displays the current time, updating once per second. I used WPF, as I haven't used Windows Forms in years, and there's no compelling reason to use it these days.
SchedulerText.xaml:
<Window x:Class="WpfTest.SchedulerTest"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid>
<TextBlock x:Name="_textBlock"
FontSize="18pt"
TextAlignment="Center"
VerticalAlignment="Center" />
</Grid>
</Window>
SchedulerTest.xaml.cs:
using System;
using FluentScheduler;
namespace WpfTest
{
public partial class SchedulerTest
{
public SchedulerTest()
{
InitializeComponent();
JobManager.AddJob(
this.DoScheduledWork,
schedule => schedule.ToRunNow().AndEvery(1).Seconds());
}
private void DoScheduledWork()
{
// Go query your database, or do whatever your main job is.
// You don't want to do this on the UI thread, because it
// will block the thread and prevent user interaction.
DoPrimaryWorkOffUIThread();
// If you need to communicate some sort of result to the user,
// do it on the UI thread.
Dispatcher.Invoke(new Action(ShowResultsOnUIThread));
}
private DateTime _currentResult;
private void DoPrimaryWorkOffUIThread()
{
_currentResult = DateTime.Now;
}
private void ShowResultsOnUIThread()
{
_textBlock.Text = $"{_currentResult:h:mm:ss}";
}
}
}
Note that you don't have to initialize the job in the windows's constructor, but that would be the easiest place to do it.
Upvotes: 1