我使用Caliburn Micro創建WPF MVVM應用程序。我在菜單(Ribbon)中有一組按鈕,它們位於視圖中,是我的外殼視圖模型的視圖,它是一個ScreenConductor。基於當前活動的屏幕視圖模型,如果可用於活動屏幕並且在活動屏幕上調用動作或命令,我希望禁用/啓用功能區按鈕。從父菜單綁定子ViewModel命令的模式
這似乎是一種常見的情況。有沒有創建這種行爲的模式?
我使用Caliburn Micro創建WPF MVVM應用程序。我在菜單(Ribbon)中有一組按鈕,它們位於視圖中,是我的外殼視圖模型的視圖,它是一個ScreenConductor。基於當前活動的屏幕視圖模型,如果可用於活動屏幕並且在活動屏幕上調用動作或命令,我希望禁用/啓用功能區按鈕。從父菜單綁定子ViewModel命令的模式
這似乎是一種常見的情況。有沒有創建這種行爲的模式?
爲您的shell視圖模型上的每個命令創建方法和伴隨的布爾屬性。 (請參閱下面的代碼示例。)Caliburn.Micro的約定會自動將它們連接到按鈕。然後,當您更改視圖以重新評估它們時,只需爲布爾屬性引發屬性已更改的事件。
例如,假設您有一個保存按鈕。 xaml中該按鈕的名稱將爲Save,並且在您的視圖模型中,您將有一個Save方法以及一個CanSave布爾屬性。請看下圖:
public void Save()
{
var viewModelWithSave = ActiveItem as ISave;
if (viewModelWithSave != null) viewModelWithSave.Save();
}
public bool CanSave { get { return ActivateItem is ISave; } }
然後,在你的指揮,只要你改變你的活動畫面,你會打電話NotifyOfPropertyChange(() => CanSave);
。這樣做會導致您的按鈕被禁用或啓用,具體取決於活動屏幕是否能夠處理該命令。在這個例子中,如果活動屏幕沒有執行ISave
,那麼保存按鈕將被禁用。
我會用Caliburn.Micro事件聚集在這種情況下,如下所示:
CanSave
,CanLoad
等)ScreenCapabilities
類帶式ScreenCapabilities
ScreenActivatedMessage
消息爲您的色帶表示贊同(句柄)的ScreenActivatedMessage
在功能區視圖模型的Handle
方法中,根據提供的ScreenCapabilities
設置本地CanXXX屬性。
這將是這個樣子(手工輸入代碼,未測試):
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;
// ...
}
}
然後,適當的地方,當畫面切換,發佈消息。有關更多信息,請參見see Caliburn.Micro wiki。
爲shell視圖模型中的活動屏幕定義屬性(假設ActiveScreen)。 讓我們假設你有每個按鈕的屬性,例如DeleteButton,AddButton。 屏幕是屏幕的視圖模型。
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");
}
}
你爲什麼不這樣做相反的事情,而不是檢查當前活動的屏幕都支持哪些命令,讓活動的屏幕填充所有的控制菜單或功能區選項卡,它支持,(我會讓它注入自己的用戶控件,這可能只是一個完整的菜單或功能區選項卡),這也將增強用戶體驗,因爲它只會向用戶顯示他可以用於當前活動屏幕的控件。
編輯:只是你的問題看一次我在想,這是簡單得多比它看起來
我可以看到你遇到的唯一問題是缺乏一個處理程序(和保護)方法的子VM將意味着在當前活動的虛擬機上沒有實現的按鈕仍將被啓用。
爲CM的默認策略是試圖找到一個匹配的方法名(解析動作文本後),如果沒有找到一個,獨自離開的按鈕。如果要定製該行爲以便默認禁用按鈕,則只需在shell中執行命令按鈕即可輕鬆實現該功能,並確保將命令目標設置爲活動項目:
在shell定義按鈕,確保他們有一個指向活動子目標VM
<Button cal:Message.Attach="Command1" cal:Action.TargetWithoutContext="{Binding ActiveItem}" />
然後,只需實現你的子虛擬機的方法,按通常
public void Command1() { }
和可選的CanXX後衛
public bool CanCommand1
{
get
{
if(someCondition) return false;
return true;
}
}
假設你沒有得到比這要複雜得多,它應該爲你工作
我將有一個快速瀏覽一下CM源,看看我能拿出的東西這適用於本
編輯:
好吧,你可以自定義ActionMessage.ApplyAvailabilityEffect
FUNC得到你想要的效果 - 在你的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發現 - 顯然給它一個很好的測試,你使用它:)
綁定您的按鈕你的視圖模型內relaycommand,基於當前設置canexecute前場景。 – 2013-03-06 23:29:19
那將造成的影響,但我希望的東西多了幾分動感,少蠻力。也許屏幕視圖模型可以定義哪些命令它支持,所以附加的屏幕可以不爲在ScreenConductor視圖模型的每個按鈕和屏幕顯式佈線被加入。 – grimus 2013-03-06 23:40:15