33

我在便携式库/Windows Store 中找不到计时器。(针对 .net 4.5 和 Windows Store aka Metro)

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

我需要某种秒表,所以它应该每秒刷新一次左右

4

6 回答 6

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 Store 应用程序,这将使您能够访问 Timer。

3) 创建一个针对 .NET 4.0 和 Windows Store 应用程序的新项目,并将需要计时器的代码放入其中。然后从 .NET 4.5 和 Windows Store 应用程序项目中引用它。

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

于 2012-10-08T21:55:25.493 回答
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));

    }
于 2012-11-21T17:21:23.277 回答
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(); }
}
于 2014-01-13T15:42:04.770 回答
4

I improved Ivan Leonenko answer by including a new parameter, which queue calls to you callback if the period is less than the callback run time. And replaced the legacy TimerCallback with an action. And finally, use our cancel token in the last delay, and used ConfigureAwait to increase concurrency, as the callback can be executed on any 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);
    }
}
于 2014-05-09T14:27:02.960 回答
2

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

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

于 2013-07-16T18:49:09.867 回答
1

我最终Observable.Timer来自 Reactive Extensions (Rx)。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-LinqNuGet 包中。

于 2016-01-21T02:59:33.267 回答