yotiky Tech Blog

とあるエンジニアの備忘録

WPF - CommandLocker

Button にバインディングしたコマンドを共通してロックする機構 CommandLocker。

github.com

ロックの種類は、Guidをキーとしたシンプルなロックと、ロックする時に渡した値をアンロックする際にも渡す必要があるトリガーロックの2種類です。

トリガーロックは、コマンド実行後に「ステータスの変更を監視してロックを解除する」非同期なロックを想定しています。

トリガーロックよりシンプルなロックの方が強くなっています。 トリガーロックで非同期待ちしている状態でも、シンプルなロックを共有するコマンドは実行可能となります。逆に、シンプルなロック中はトリガーロックを共有するコマンドは実行できません。トリガーロックを待つ必要があるコマンドは、トリガーロックを共有してください。

使い方

CommandLocker を共通でロックしたい Command に共有します。

var locker = new CommandLocker<int>();

DefaultLock1Command = new ReactiveCommand(locker.CanExecute);
DefaultLock2Command = new ReactiveCommand(locker.CanExecute);
TriggerLock1Command = new ReactiveCommand(locker.CanExecuteWithTrigger);
TriggerLock2Command = new ReactiveCommand(locker.CanExecuteWithTrigger);

シンプルなロックは、 Lock() に成功したら実処理を実行し、Release() 時にキーを渡します。

var locked = locker.Lock();
if (!locked.result) { return; }

// なにかの処理

locker.Release(locked.key);

トリガーロックは、Lock() する時にトリガーとなる値を渡します。

var locked = locker.Lock(3);
if (!locked.result) { return; }

// なにかの処理

何かしらのステータスを監視し値が変更されたら、TryRelease()`でアンロックを試みます。

var result = locker.TryRelease(clickedCount);

トリガーロックは、ロックする時にアンロックを待つメソッドも用意しています。

var locked = locker.Lock(3);

// なにかの処理

await locker.WaitReleaseAsync();

// 後続処理

ロックが解除できなくなった場合のために、ReleaseForce() で強制アンロックができます。

locker.ReleaseForce();