web-dev-qa-db-fra.com

Minuterie dans la bibliothèque portable

Je ne trouve pas de minuteur dans la bibliothèque portable/Windows Store. (Ciblage .net 4.5 et Windows Store aka Metro)

Quelqu'un at-il une idée sur la façon de créer une sorte d'événement de chronométrage?

J'ai besoin d'une sorte de chronomètre, donc cela devrait rafraîchir une fois par seconde

33
Boas Enkler

Mise à jour: Nous avons corrigé ce problème dans Visual Studio 2013. Les bibliothèques portables ciblant les projets Store (Windows 8.1) et .NET Framework 4.5.1 peuvent désormais référencer Timer.

C'est malheureux où nos détails de mise en œuvre fuient à l'utilisateur. Lorsque vous ciblez uniquement les applications .NET 4.5 et Windows Store, nous vous incitons à construire sur quelque chose de différent que lorsque vous ciblez une plate-forme de niveau inférieur (.NET 4, SL 4/5, Phone 7.x). Nous essayons de traiter ces deux comme les mêmes, mais des changements limités en dessous commencent à fuir (comme la minuterie et la réflexion). Nous en couvrons une partie ici: http://channel9.msdn.com/Shows/Going+Deep/NET-45-David-Kean-and-Marcea-Trofin-Portable-Libraries .

Nous chercherons à résoudre ce problème dans une future version. Jusque-là, vous avez quelques solutions:

1) Implémentez votre propre version de Timer à l'aide de Task.Delay, voici une copie rapide que nous utilisons en interne:

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<TimerCallback, object>)s;
            Tuple.Item1(Tuple.Item2);
        }, Tuple.Create(callback, state), CancellationToken.None,
            TaskContinuationOptions.ExecuteSynchronously | TaskContinuationOptions.OnlyOnRanToCompletion,
            TaskScheduler.Default);
    }

    public new void Dispose() { base.Cancel(); }
}

2) Rétrogradez votre projet vers les applications .NET 4.0 et Windows Store, qui vous donneront accès à Timer.

3) Créez un nouveau projet ciblant les applications .NET 4.0 et Windows Store, et insérez le code qui nécessite un minuteur. Référencez ensuite cela à partir du projet d'applications .NET 4.5 et Windows Store.

En remarque, j'ai déposé un élément de travail pour moi-même sur le site PclContrib pour ajouter la prise en charge de la minuterie: http://pclcontrib.codeplex.com/workitem/1251 .

44
David Kean

Suite à la suggestion n ° 3 de David Kean, voici mon adaptateur de minuterie hacky - mettez-le dans une bibliothèque PCL qui cible .net 4.0 et référencez-le à partir de 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);
        }
    }

Et puis pour l'utiliser, vous pouvez le faire à partir de votre bibliothèque 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));

    }
7
Henry C

Mise en œuvre de la suggestion # 1 de David Kean avec période:

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<TimerCallback, object>) 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(); }
}
6
Ivan Leonenko

J'ai amélioré la réponse d'Ivan Leonenko en incluant un nouveau paramètre, qui met en file d'attente les appels vers vous si la période est inférieure à la durée d'exécution du rappel. Et remplacé l'héritage TimerCallback par une action. Et enfin, utilisez notre jeton d'annulation dans le dernier délai et utilisez ConfigureAwait pour augmenter la simultanéité, car le rappel peut être exécuté sur n'importe quel thread.

internal sealed class Timer : CancellationTokenSource
{
    internal Timer(Action<object> 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<Action<object>, 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);
    }
}
4
Softlion

Vous pouvez créer une interface de temporisation à l'aide d'une bibliothèque PCL, puis créer une implémentation de cette interface dans une deuxième bibliothèque W8S à l'aide d'une minuterie W8S.

Vous pouvez ensuite utiliser l'injection de dépendances pour injecter la bibliothèque W8S dans la classe PCL.

2
Paul

Je me suis retrouvé avec Observable.Timer de Reactive Extensions (Rx). Rx était déjà inclus dans le projet, donc aucune référence supplémentaire n'était un problème.

Voici une minuterie qui se déclenche toutes les secondes:

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 la classe est en PCL Rx-Linq Package NuGet.

1
altso