ポータブルライブラリ/Windowsストアでタイマーが見つかりません。(.net 4.5およびWindowsStore、別名Metroをターゲットにしています)
ある種のタイミングイベントを作成する方法について何かアイデアはありますか?
ストップウォッチのようなものが必要なので、これは1秒に1回程度更新する必要があります
ポータブルライブラリ/Windowsストアでタイマーが見つかりません。(.net 4.5およびWindowsStore、別名Metroをターゲットにしています)
ある種のタイミングイベントを作成する方法について何かアイデアはありますか?
ストップウォッチのようなものが必要なので、これは1秒に1回程度更新する必要があります
更新:これはVisual Studio 2013で修正されました。ストア(Windows 8.1)および.NETFramework4.5.1プロジェクトを対象とするポータブルライブラリがタイマーを参照できるようになりました。
これは、実装の詳細がユーザーに漏れているという不幸なケースです。.NET4.5およびWindowsStoreアプリのみを対象とする場合、実際には、下位レベルのプラットフォーム(.NET 4、SL 4/5、Phone 7.x)を対象とする場合とは異なるものに対してビルドする必要があります。これら2つを同じものとして扱うようにしていますが、その下にある限られた変更(タイマー、リフレクションなど)がリークし始めています。ここでは、この一部について説明します:http: //channel9.msdn.com/Shows/Going+Deep/NET-45-David-Kean-and-Marcea-Trofin-Portable-Libraries。
将来のバージョンでこれを修正する予定です。それまでは、いくつかの回避策があります。
1)Task.Delayを使用して独自のバージョンのタイマーを実装します。これは、内部で使用しているクイックコピーです。
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)プロジェクトを.NET4.0およびWindowsStoreアプリにダウングレードします。これにより、タイマーにアクセスできるようになります。
3).NET4.0およびWindowsStoreアプリを対象とする新しいプロジェクトを作成し、その中にタイマーを必要とするコードを配置します。次に、.NET4.5およびWindowsStoreアプリプロジェクトからそれを参照します。
補足として、タイマーのサポートを追加するために、PclContribサイトに自分用の作業項目を提出しました:http://pclcontrib.codeplex.com/workitem/12513。
David Keanからの提案#3に続いて、これが私のハッキーなタイマーアダプターです-これを.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.5PCLライブラリからこれを行うことができます。
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));
}
期間付きのDavidKeanからの提案#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(); }
}
期間がコールバックの実行時間よりも短い場合にコールバックを呼び出す新しいパラメーターを含めることで、IvanLeonenkoの回答を改善しました。そして、従来の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);
}
}
PCLライブラリを使用してタイマーインターフェイスを作成し、次にW8Sタイマーを使用して2番目のW8Sライブラリにそのインターフェイスの実装を作成できます。
次に、依存性注入を使用して、W8SライブラリをPCLクラスに注入できます。
私はObservable.Timer
ReactiveExtensions(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-Linq
NuGetパッケージに含まれています。