Caliburn Micro を使用して WPF MVVM アプリを作成しています。ScreenConductor であるシェル ビュー モデルのビューにあるメニュー (リボン) に一連のボタンがあります。現在アクティブなスクリーン ビュー モデルに基づいて、アクティブなスクリーンで使用できる場合はリボン ボタンを無効/有効にし、アクティブなスクリーンでアクションまたはコマンドを呼び出したいと考えています。
これは一般的なシナリオのようです。この動作を作成するためのパターンはありますか?
Caliburn Micro を使用して WPF MVVM アプリを作成しています。ScreenConductor であるシェル ビュー モデルのビューにあるメニュー (リボン) に一連のボタンがあります。現在アクティブなスクリーン ビュー モデルに基づいて、アクティブなスクリーンで使用できる場合はリボン ボタンを無効/有効にし、アクティブなスクリーンでアクションまたはコマンドを呼び出したいと考えています。
これは一般的なシナリオのようです。この動作を作成するためのパターンはありますか?
現在アクティブな画面でサポートされているコマンドを確認する代わりに、逆のことをしないのはなぜですか。アクティブな画面に、サポートするすべてのコントロールをメニューまたはリボン タブに入力させます (独自のユーザーを挿入させます)。コントロールは完全なメニューまたはリボン タブである可能性があります)、これにより、現在アクティブな画面で操作できるコントロールのみがユーザーに表示されるため、ユーザー エクスペリエンスも向上します。
編集:あなたの質問をもう一度見るだけで、これは見た目よりもはるかに簡単だと思います
私が見ることができる唯一の問題は、子 VM にハンドラー (およびガード) メソッドがないということは、現在アクティブな VM に実装されていないボタンが引き続き有効になることです。
CM のデフォルトの戦略は、(アクション テキストの解析後に) 一致するメソッド名を見つけようとし、見つからない場合はボタンをそのままにしておくことです。デフォルトでボタンが無効になるようにその動作をカスタマイズする場合は、シェルにコマンド ボタンを実装するだけで簡単に動作させることができます。コマンド ターゲットをアクティブなアイテムに設定するようにしてください。
シェルでボタンを定義し、アクティブな子 VM を指すターゲットがあることを確認します。
<Button cal:Message.Attach="Command1" cal:Action.TargetWithoutContext="{Binding ActiveItem}" />
次に、通常どおり子 VM にメソッドを実装します。
public void Command1() { }
オプションで CanXX ガード
public bool CanCommand1
{
get
{
if(someCondition) return false;
return true;
}
}
これよりもはるかに複雑にならないと仮定すると、それはうまくいくはずです
CM のソースをざっと見て、これに適したものを思いつくことができるかどうかを確認します。
編集:
func をカスタマイズして、ActionMessage.ApplyAvailabilityEffect
必要な効果を得ることができます。bootstrapper.Configure() メソッド (または起動時のどこか) で次を使用します。
ActionMessage.ApplyAvailabilityEffect = context =>
{
var source = context.Source;
if (ConventionManager.HasBinding(source, UIElement.IsEnabledProperty))
{
return source.IsEnabled;
}
if (context.CanExecute != null)
{
source.IsEnabled = context.CanExecute();
}
// Added these 3 lines to get the effect you want
else if (context.Target == null)
{
source.IsEnabled = false;
}
// EDIT: Bugfix - need this to ensure the button is activated if it has a target but no guard
else
{
source.IsEnabled = true;
}
return source.IsEnabled;
};
これは私にとってはうまくいくようです-コマンドにバインドできなかったメソッドのターゲットがないため、その場合はIsEnabled
falseに設定しました。これは、アクティブな子 VM で一致するシグネチャを持つメソッドが見つかった場合にのみボタンをアクティブにします - 明らかに、使用する前に適切なテストを行ってください :)
このシナリオでは、次のように Caliburn.Micro イベント集計を使用します。
ScreenCapabilities
一連のブール属性で名前を付けたクラスを作成します (例: CanSave
、CanLoad
など)。ScreenActivatedMessage
タイプのプロパティで名前が付けられたメッセージを作成しますScreenCapabilities
ScreenActivatedMessage
リボン ビュー モデルのHandle
メソッドで、提供された に基づいてローカルの CanXXX プロパティを設定しScreenCapabilities
ます。
次のようになります (コードは手で入力され、テストされていません)。
public class ScreenCapabilities
{
public bool CanSave { get; set; }
// ...
}
public class ScreenActivatedMessage
{
public ScreenCapabilities ScreenCapabilities { get; set; }
// ...
}
public class RibbonViewModel : PropertyChangedBase, IHandle<ScreenActivatedMessage>
{
private bool _canSave;
public bool CanSave
{
get { return _canSave; }
set { _canSave = value; NotifyPropertyChanged(() => CanSave); }
}
// ...
public void Handle(ScreenActivatedMessage message)
{
CanSave = message.ScreenCapabilities.CanSave;
// ...
}
}
次に、適切な場所で、画面が変わったら、メッセージを公開します。詳細については、Caliburn.Micro wikiを参照してください。
シェル ビュー モデルのコマンドごとに、メソッドと付随するブール プロパティを作成します。(例については、以下のコードを参照してください。) Caliburn.Micro の規則により、それらは自動的にボタンに結び付けられます。次に、ビューを変更して再評価するときに、ブール型プロパティのプロパティ変更イベントを発生させるだけです。
たとえば、[保存] ボタンがあるとします。xaml でのそのボタンの名前は Save であり、ビュー モデルでは、CanSave ブール値プロパティと共に Save メソッドを使用します。下記参照:
public void Save()
{
var viewModelWithSave = ActiveItem as ISave;
if (viewModelWithSave != null) viewModelWithSave.Save();
}
public bool CanSave { get { return ActivateItem is ISave; } }
次に、コンダクターで、アクティブな画面を変更するたびに を呼び出しますNotifyOfPropertyChange(() => CanSave);
。これを行うと、アクティブな画面がそのコマンドを処理できるかどうかに応じて、ボタンが無効または有効になります。この例では、アクティブな画面が を実装していない場合、ISave
[保存] ボタンは無効になります。
シェル ビュー モデルでアクティブな画面のプロパティ (ActiveScreen としましょう) を定義します。また、DeleteButton、AddButton などの各ボタンのプロパティがあるとします。Screen は、スクリーンのビューモデルです。
private Screen activeScreen;
public Screen ActiveScreen
{
get
{
return activeScreen;
}
set
{
activeScreen= value;
if (activeScreen.Name.equals("Screen1"))
{
this.AddButton.IsEnabled = true;
this.DeleteButton.IsEnabled = false;
}
if else (activeScreen.Name.equals("Screen2"))
{
this.AddButton.IsEnabled = true;
this.DeleteButton.IsEnabled = true;
}
NotifyPropertyChanged("ActiveScreen");
}
}