These are the solutions I came out with that do not use Control.Invoke.
The code for IMainView, MainView, and Program is the same in both solutions:
IMainView
namespace Tester1
{
interface IMainView
{
string ControlProperty { get; set; }
}
}
MainView
using System.Windows.Forms;
namespace Tester1
{
public partial class MainView : Form, IMainView
{
public MainView()
{
InitializeComponent();
}
public string ControlProperty
{
get => MyControl.Text;
set => MyControl.Text = value;
}
}
}
Program
using System;
using System.Windows.Forms;
namespace Tester1
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
// models
EventRaisingObject eventRaisingObject = new EventRaisingObject();
// views
MainView mainView = new MainView();
// presenters
MainPresenter mainPresenter = new MainPresenter(mainView, eventRaisingObject);
Application.Run(mainView);
}
}
}
Solution 1: Using SynchronizationContext
MainPresenter
using System.Threading;
namespace Tester1
{
class MainPresenter
{
private readonly IMainView mainView;
private readonly EventRaisingObject eventRaisingObject;
private readonly SynchronizationContext viewContext;
public MainPresenter(IMainView mainView, EventRaisingObject eventRaisingObject)
{
this.mainView = mainView;
viewContext = SynchronizationContext.Current;
this.eventRaisingObject = eventRaisingObject;
this.eventRaisingObject.MyEvent +=
(sender, s) => viewContext.Post(d => OnMyEvent(s), null);
}
private void OnMyEvent(string e)
{
// do some work
mainView.ControlProperty = e;
// do some work
}
}
}
This solution uses a SynchronizationContext object, initialised in the constructor with the context of the UI.
Solution 2: Using TaskFactory
using System.Threading.Tasks;
namespace Tester1
{
class MainPresenter
{
private readonly IMainView mainView;
private readonly EventRaisingObject eventRaisingObject;
private readonly TaskFactory viewTaskFactory;
public MainPresenter(IMainView mainView, EventRaisingObject eventRaisingObject)
{
this.mainView = mainView;
viewTaskFactory = new TaskFactory(TaskScheduler.FromCurrentSynchronizationContext());
this.eventRaisingObject = eventRaisingObject;
this.eventRaisingObject.MyEvent +=
(sender, s) => viewTaskFactory.StartNew(() => OnMyEvent(s));
}
private void OnMyEvent(string e)
{
// do some work
mainView.ControlProperty = e;
// do some work
}
}
}
This solution uses a TaskFactory object, initialised in the constructor with the TaskScheduler of the UI. When the event is raised a new task is started by using the TaskFactory object.