2016-02-02 92 views
0

我有一個MainView,其DataContext是我的MainViewModel。Caliburn.Micro:調用DataContext屬性的方法

MainViewModel:

class MainViewModel : PropertyChangedBase 
{ 
    #region Properties 

    /// <summary> 
    /// The ProjectViewModel. 
    /// </summary> 
    public ProjectViewModel ProjectVM 
    { 
     get { return _projectVM; } 
     private set 
     { 
      _projectVM = value; 
      NotifyOfPropertyChange(() => ProjectVM); 
     } 
    } 
    private ProjectViewModel _projectVM; 

    #endregion 

    /// <summary> 
    /// Constructor. 
    /// </summary> 
    public MainViewModel() 
    { 
     ProjectVM = new ProjectViewModel(); 
    } 
} 

現在,我有我的MainView菜單。我想將MenItems的Click事件綁定到ProjectVM對象上的方法。當然我知道我可以設置MenuItems的DataContext,但我想知道是否有更簡單的方法。

目前我的MainView是這樣的:

<Grid> 
<Grid.RowDefinitions> 
    <RowDefinition Height="Auto"/> 
</Grid.RowDefinitions> 

<Menu Grid.Row="0"> 
    <MenuItem Header="File"> 
    <MenuItem Header="New Project..."> 
     <i:Interaction.Triggers> 
     <i:EventTrigger EventName="Click"> 
      <cal:ActionMessage MethodName="ProjectVM.ShowNewProjectDialog"/> 
     </i:EventTrigger> 
     </i:Interaction.Triggers> 
    </MenuItem> 
    <MenuItem Header="Load Project..."/> 
    <MenuItem Header="Close Project..."/> 
    </MenuItem> 
</Menu> 

我希望卡利很聰明,解決ProjectVM.ShowNewProjectDialog,但事實並非如此。有沒有什麼好的方法來做到這一點,而不必手動設置菜單的DataContext?

回答

1

你說得對,Caliburn並不是那麼聰明,以你想要的方式解析MethodName屬性。無論如何,這是一個強大的工具,可以根據您的需求輕鬆定製。

正如你可以在名爲All About Actions的卡利微文檔部分閱讀:

ActionMessage的是,當然,這 標記的具體Caliburn.Micro部分。它表明,當觸發發生時,我們應該發送一個「SayHello」的消息 。那麼,爲什麼在描述此功能時,我使用語言「發送消息」 而不是「執行方法」? 這是一個有趣而有力的部分。 ActionMessage通過可視樹搜索可以處理它的目標實例來泡泡 。

這意味着 - 如果您需要 - 您可以手動設置將處理您的消息的「目標」。您可以通過使用附加屬性Action.Target來完成。你當然不希望將其設置爲每MenuItem,這樣你就可以在你的Menu對象直接設置:

<Menu cal:Action.Target="{Binding Path=ProjectVM, Mode=OneWay}"> 
    <MenuItem Header="File"> 
     <MenuItem Header="New Project..." cal:Message.Attach="ShowNewProjectDialog" /> 
     <MenuItem Header="Load Project..."/> 
     <MenuItem Header="Close Project..."/> 
    </MenuItem> 
</Menu> 

通過設置我們宣佈了Action.Target附加屬性,其中來自所有郵件(即ActionMessages)菜單的孩子將由ProjectViewModel處理。 現在如果你運行你的項目,你會看到它不工作的屬性。原因是Caliburn Micro使用VisualTreeHelper遍歷XAML樹。對於我們的目的,我們需要使用LogicalTreeHelper

所以最後一步是在BootstrapperConfigure方法添加以下代碼:

ActionMessage.SetMethodBinding = delegate(ActionExecutionContext context) 
{ 
    FrameworkElement source = context.Source; 
    for (DependencyObject dependencyObject = source; dependencyObject != null; dependencyObject = LogicalTreeHelper.GetParent(dependencyObject)) 
    { 
     if (Caliburn.Micro.Action.HasTargetSet(dependencyObject)) 
     { 
      object handler = Message.GetHandler(dependencyObject); 
      if (handler == null) 
      { 
       context.View = dependencyObject; 
       return; 
      } 
      MethodInfo methodInfo = ActionMessage.GetTargetMethod(context.Message, handler); 
      if (methodInfo != null) 
      { 
       context.Method = methodInfo; 
       context.Target = handler; 
       context.View = dependencyObject; 
       return; 
      } 
     } 
    } 
    if (source != null && source.DataContext != null) 
    { 
     object dataContext = source.DataContext; 
     MethodInfo methodInfo2 = ActionMessage.GetTargetMethod(context.Message, dataContext); 
     if (methodInfo2 != null) 
     { 
      context.Target = dataContext; 
      context.Method = methodInfo2; 
      context.View = source; 
     } 
    } 
}; 

我希望它可以幫助你。