便携式库中的定时器

时间:2012-09-23 18:28:01

标签: c# timer microsoft-metro portable-class-library

我在便携式库/ Windows应用商店中找不到计时器。 (定位.net 4.5和Windows Store又称Metro)

有没有人知道如何创建某种计时事件?

我需要一些秒表,所以这应该每秒刷新一次

6 个答案:

答案 0 :(得分:44)

更新我们已在Visual Studio 2013中修复此问题。针对Store(Windows 8.1)和.NET Framework 4.5.1项目的可移植库现在可以引用Timer。

这是我们的实施细节泄露给用户的不幸情况。当您只针对.NET 4.5和Windows应用商店应用时,我们实际上会让您构建针对不同的东西,然后定位到低级平台(.NET 4,SL 4/5,Phone 7.x)。我们尝试将这两者视为相同,但在下面开始泄漏的有限变化(例如Timer和Reflection)。我们在此处介绍其中一些内容:http://channel9.msdn.com/Shows/Going+Deep/NET-45-David-Kean-and-Marcea-Trofin-Portable-Libraries

我们将在未来版本中考虑修复此问题。在那之前,您有几个解决方法:

1)使用Task.Delay实现您自己的Timer版本,这是我们在内部使用的快速副本:

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)将您的项目降级到.NET 4.0和Windows应用商店应用程序,这样您就可以访问Timer。

3)创建一个面向.NET 4.0和Windows应用商店应用的新项目,并将需要定时器的代码放入其中。然后从.NET 4.5和Windows应用商店应用项目中引用它。

作为旁注,我已经在PclContrib网站上为自己提交了一个工作项,以添加定时器支持:http://pclcontrib.codeplex.com/workitem/12513

答案 1 :(得分:7)

根据David Kean的建议#3,这是我的hacky Timer适配器 - 将它放在一个针对.net 4.0的PCL库中,并从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);
        }
    }

然后使用它,您可以从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));

    }

答案 2 :(得分:6)

David Kean提出的建议#1的执行期限为:

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(); }
}

答案 3 :(得分:4)

我通过包含一个新参数改进了Ivan Leonenko的答案,如果句点小于回调运行时间,则该队列将调用回调。并用一个动作取代了传统的TimerCallback。最后,在最后一个延迟中使用我们的取消令牌,并使用ConfigureAwait来增加并发性,因为回调可以在任何线程上执行。

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 :(得分:2)

您可以使用PCL库创建一个计时器界面,然后使用W8S计时器在第二个W8S库中创建该界面的实现。

然后你可以使用依赖注入将W8S库注入PCL类。

答案 5 :(得分:1)

我最后从Reactive Extensions(Rx)获得了Observable.Timer。 Rx已经包含在项目中,因此额外的参考不是问题。

这是一个每秒触发的计时器:

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类属于PCL友好Rx-Linq NuGet包。