Skip to content

Instantly share code, notes, and snippets.

@DamianReeves
Created May 30, 2014 15:54
Show Gist options
  • Save DamianReeves/76771a031f05a8be042d to your computer and use it in GitHub Desktop.
Save DamianReeves/76771a031f05a8be042d to your computer and use it in GitHub Desktop.
Dispatcher Issue
using System.Threading.Tasks;
namespace MyCompany.Windows.Reactive
{
public interface ISplashPresenter
{
Task ShowAsync();
Task DisplayMessage(string messageFormat, params object[] args);
Task CloseAsync();
}
}
using System;
namespace yCompany.Windows.Reactive
{
public interface ISplashView
{
event EventHandler Closed;
bool IsVisible { get; }
bool Activate();
void Show();
void Hide();
void Close();
object DataContext { get; set; }
}
}
using System;
using System.Diagnostics;
using System.Diagnostics.Contracts;
using System.Net.Mime;
using System.Reactive;
using System.Reactive.Concurrency;
using System.Reactive.Linq;
using System.Reactive.Threading.Tasks;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Navigation;
using System.Windows.Threading;
namespace MyCompany.Windows.Reactive
{
public class SplashPresenter : ISplashPresenter
{
private static int _threadCounter;
private Thread _splashThread;
private readonly IScheduler _splashScheduler;
private ISplashView _splashView;
private Window _dummyWindow;
public SplashPresenter()
{
_splashScheduler = new EventLoopScheduler(CreateThread);
//SplashScheduler.Schedule(Run);
}
public IScheduler SplashScheduler
{
get { return _splashScheduler; }
}
public ISplashView SplashView
{
get { return _splashView; }
}
public Dispatcher SplashDispatcher { get; private set; }
public bool IsRunning { get; private set; }
public Task ShowAsync()
{
//var tcs = new TaskCompletionSource<Unit>();
return WithSplashViewAsync(splash =>
{
splash.Show();
});
//return tcs.Task;
}
public Task DisplayMessage(string messageFormat, params object[] args)
{
throw new NotImplementedException();
}
public Task CloseAsync()
{
return WithSplashViewAsync(splash => splash.Close(), false);
}
private ISplashView GetOrCreateSplashView()
{
var splash = SplashView;
if (splash == null)
{
splash = CreateSplashView();
}
return splash;
}
protected ISplashView CreateSplashView()
{
return CreateCustomSplashView() ?? new SplashWindow();
}
protected virtual ISplashView CreateCustomSplashView()
{
return null;
}
protected Task WithSplashViewAsync(Action<ISplashView> action, bool ensureRunning = true)
{
if (ensureRunning)
{
return GetSplashViewAsync()
.ContinueWith(t =>
{
InvokeOnDispatcherAsync(() => action(t.Result));
}, TaskContinuationOptions.OnlyOnRanToCompletion);
}
else
{
var splash = SplashView;
if (splash != null)
{
return InvokeOnDispatcherAsync(() => action(splash));
}
}
return InvokeOnDispatcherAsync(() => { });
}
private Task<ISplashView> GetSplashViewAsync()
{
var tcs = new TaskCompletionSource<ISplashView>();
if (IsRunning)
{
tcs.SetResult(_splashView);
return tcs.Task;
}
SplashScheduler.Schedule(()=>Run(tcs.SetResult));
return tcs.Task;
}
private void StartThread()
{
}
private void Run(Action<ISplashView> callback)
{
if (callback == null) throw new ArgumentNullException("callback");
Contract.EndContractBlock();
SplashDispatcher = Dispatcher.CurrentDispatcher;
SplashDispatcher.BeginInvoke((Action) (() =>
{
Dispatcher.CurrentDispatcher.ShutdownFinished += OnSplashDispatcherShutdownFinished;
Debug.Print("[Before] Setting IsRunning = true on Thread[{0}:{1}]", Thread.CurrentThread.Name,
Thread.CurrentThread.ManagedThreadId);
_splashView = GetOrCreateSplashView();
_dummyWindow = _dummyWindow ?? CreateDummyWindow();
callback(_splashView);
IsRunning = true;
Debug.Print("[After] Setting IsRunning = true on Thread[{0}:{1}]", Thread.CurrentThread.Name,
Thread.CurrentThread.ManagedThreadId);
}));
Debug.Print("[Before] Dispatcher.Run on Thread[{0}:{1}]", Thread.CurrentThread.Name, Thread.CurrentThread.ManagedThreadId);
Dispatcher.Run();
Debug.Print("[After] Dispatcher.Run on Thread[{0}:{1}]", Thread.CurrentThread.Name, Thread.CurrentThread.ManagedThreadId);
IsRunning = false;
}
private void OnSplashDispatcherShutdownFinished(object sender, EventArgs e)
{
var dispatcher = Dispatcher.FromThread(Thread.CurrentThread);
if (dispatcher != null)
{
dispatcher.Invoke(() => { });
}
SplashDispatcher = null;
IsRunning = false;
}
private Task InvokeOnDispatcherAsync(Action action)
{
return SplashDispatcher.BeginInvoke(action).Task;
}
private Thread CreateThread(ThreadStart threadStart)
{
var thread = new Thread(InterceptThreadStart(threadStart))
{
Name = "Splash Event Loop " + Interlocked.Increment(ref _threadCounter),
IsBackground = true
};
thread.SetApartmentState(ApartmentState.STA);
return thread;
}
private ThreadStart InterceptThreadStart(ThreadStart threadStart)
{
ThreadStart newThreadStart = () =>
{
Debug.WriteLine("[Begin]ThreadStart");
threadStart();
Debug.WriteLine("[End]Stopping thread");
};
return newThreadStart;
}
private Window CreateDummyWindow()
{
var window = new Window();
window.WindowState = WindowState.Minimized;
window.Show();
window.Hide();
return window;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment