我有一个MainView,其DataContext是我的MainViewModel。
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>
我曾希望Caliburn足够聪明地解决ProjectVM.ShowNewProjectDialog,但事实并非如此。有没有什么好方法可以手动设置菜单的DataContext?
答案 0 :(得分:2)
你是对的,Caliburn并不是那么聪明地以你想要的方式解析MethodName
财产。无论如何,它是一个功能强大的工具,可以根据您的需要轻松定制。
您可以在名为All About Actions的Caliburn Micro文档部分中阅读:
ActionMessage当然是Caliburn.Micro特有的部分 标记。它表明当触发发生时,我们应该发送一个 “SayHello”的消息。那么,为什么我要使用“发送消息”这个语言 在描述此功能时,而不是“执行方法”? 这是有趣而有力的部分。 ActionMessage泡泡 通过Visual Tree搜索可以的目标实例 处理它。
这意味着 - 如果您需要 - 您可以手动设置将处理您的消息的“目标”。您可以使用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
附加属性,我们声明来自Menu子项的所有消息(即ActionMessages)将由ProjectViewModel
处理。
现在,如果您运行项目,您将看到它不起作用。原因是Caliburn Micro使用VisualTreeHelper
遍历XAML树。出于我们的目的,我们需要使用LogicalTreeHelper
。
所以最后一步是在Bootstrapper
Configure
方法中添加此代码:
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;
}
}
};
我希望它可以帮到你。