Temporizador en biblioteca portátil

No puedo encontrar un temporizador en la biblioteca portátil / Windows Store. (Targeting .net 4.5 y Windows Store también conocido como Metro)

¿Alguien tiene una idea sobre cómo crear algún tipo de evento de tiempo?

Necesito algún tipo de cronómetro, así que debería refrescarse una vez más o menos.

Actualización: Hemos solucionado esto en Visual Studio 2013. Las bibliotecas portátiles que se dirigen a Store (Windows 8.1) y los proyectos de .NET Framework 4.5.1 ahora pueden hacer referencia a Timer.

Este es un desafortunado caso de que nuestros detalles de implementación se estén filtrando al usuario. Cuando se dirige solo a las aplicaciones .NET 4.5 y Windows Store, en realidad hacemos que construya algo diferente que cuando se dirige a una plataforma de nivel inferior (.NET 4, SL 4/5, Teléfono 7.x). Tratamos de tratar estos dos como iguales, pero los cambios limitados debajo empiezan a filtrarse (como el Temporizador y la Reflexión). Cubrimos algo de esto aquí: http://channel9.msdn.com/Shows/Going+Deep/NET-45-David-Kean-and-Marcea-Trofin-Portable-Libraries .

Veremos cómo solucionar esto en una versión futura. Hasta entonces, tienes un par de soluciones:

1) Implemente su propia versión de Timer usando Task.Delay, aquí hay una copia rápida que estamos usando internamente:

internal delegate void TimerCallback(object state); internal sealed class Timer : CancellationTokenSource, IDisposable { internal Timer(TimerCallback callback, object state, int dueTime, int period) { Contract.Assert(period == -1, "This stub implementation only supports dueTime."); Task.Delay(dueTime, Token).ContinueWith((t, s) => { var tuple = (Tuple)s; tuple.Item1(tuple.Item2); }, Tuple.Create(callback, state), CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously | TaskContinuationOptions.OnlyOnRanToCompletion, TaskScheduler.Default); } public new void Dispose() { base.Cancel(); } } 

2) Reduzca su proyecto a .NET 4.0 y las aplicaciones de Windows Store, lo que le dará acceso a Timer.

3) Cree un nuevo proyecto que tenga como objective las aplicaciones .NET 4.0 y Windows Store, y coloque el código que requiere el temporizador. Luego haga referencia a eso desde el proyecto de aplicaciones .NET 4.5 y Windows Store.

Como nota al margen, he archivado un elemento de trabajo para mí en el sitio PclContrib para agregar compatibilidad con el temporizador: http://pclcontrib.codeplex.com/workitem/12513 .

Siguiendo la sugerencia n. ° 3 de David Kean, aquí está mi adaptador Hacky Timer: colóquelo en una biblioteca PCL que tenga como objective .net 4.0 y haga referencia a él desde 4.5:

  public class PCLTimer { private Timer _timer; private Action _action; public PCLTimer(Action action, TimeSpan dueTime, TimeSpan period) { _action = action; _timer = new Timer(PCLTimerCallback, null, dueTime, period); } private void PCLTimerCallback(object state) { _action.Invoke(); } public bool Change(TimeSpan dueTime, TimeSpan period) { return _timer.Change(dueTime, period); } } 

Y luego para usarlo, puede hacer esto desde su biblioteca 4.5 PCL:

  private void TimeEvent() { //place your timer callback code here } public void SetupTimer() { //set up timer to run every second PCLTimer _pageTimer = new PCLTimer(new Action(TimeEvent), TimeSpan.FromMilliseconds(-1), TimeSpan.FromSeconds(1)); //timer starts one second from now _pageTimer.Change(TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(1)); } 

Implementación de la sugerencia n. ° 1 de David Kean con punto:

 public delegate void TimerCallback(object state); public sealed class Timer : CancellationTokenSource, IDisposable { public Timer(TimerCallback callback, object state, int dueTime, int period) { Task.Delay(dueTime, Token).ContinueWith(async (t, s) => { var tuple = (Tuple) s; while (true) { if (IsCancellationRequested) break; Task.Run(() => tuple.Item1(tuple.Item2)); await Task.Delay(period); } }, Tuple.Create(callback, state), CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously | TaskContinuationOptions.OnlyOnRanToCompletion, TaskScheduler.Default); } public new void Dispose() { base.Cancel(); } } 

Mejoré la respuesta de Ivan Leonenko al incluir un nuevo parámetro, que llama a la callback si el período es menor que el tiempo de ejecución de la callback. Y reemplazó el TimerCallback heredado con una acción. Y, por último, utilice nuestro token de cancelación en el último retraso, y use ConfigureAwait para boost la concurrencia, ya que la callback puede ejecutarse en cualquier subproceso.

 internal sealed class Timer : CancellationTokenSource { internal Timer(Action callback, object state, int millisecondsDueTime, int millisecondsPeriod, bool waitForCallbackBeforeNextPeriod = false) { //Contract.Assert(period == -1, "This stub implementation only supports dueTime."); Task.Delay(millisecondsDueTime, Token).ContinueWith(async (t, s) => { var tuple = (Tuple, object>) s; while (!IsCancellationRequested) { if (waitForCallbackBeforeNextPeriod) tuple.Item1(tuple.Item2); else Task.Run(() => tuple.Item1(tuple.Item2)); await Task.Delay(millisecondsPeriod, Token).ConfigureAwait(false); } }, Tuple.Create(callback, state), CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously | TaskContinuationOptions.OnlyOnRanToCompletion, TaskScheduler.Default); } protected override void Dispose(bool disposing) { if(disposing) Cancel(); base.Dispose(disposing); } } 

Podría crear una interfaz de temporizador usando una biblioteca PCL y luego crear una implementación de esa interfaz en una segunda biblioteca W8S usando un temporizador W8S.

Entonces podría usar la dependency injection para inyectar la biblioteca W8S en la clase PCL.

Terminé con Observable.Timer de Reactive Extensions (Rx). Rx ya estaba incluido en el proyecto, por lo que la referencia adicional no era un problema.

Aquí hay un temporizador que se dispara cada segundo:

 IDisposable timer = Observable.Timer(TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(1)) .Subscribe(_ => /* your useful code here */); // unsubscribe/stop when timer is no longer needed timer.Dispose(); 

System.Reactive.Linq.Observable clase System.Reactive.Linq.Observable está en el paquete compatible con PCL Rx-Linq NuGet.