Caliburn.Micro:将屏幕中的按钮绑定到ScreenConductor中的命令 [英] Caliburn.Micro: Binding Button in a Screen to a command in ScreenConductor
问题描述
我在本教程上< Caliburn.Micro
框架中的code> Screen 和 ScreenConductors
。
我使用的是WPF,而不是Silverlight,并且我对App.xaml进行了相应的更改(对引导程序使用MergedDictionary)。
原始的简单导航示例具有一个带有两个按钮的外壳,以及一个内容区域,其中显示了两个可能的屏幕,由 ShellViewModel
进行。
然后,我尝试将每个按钮移至对应的View,这样PageOne会有一个按钮可以移至PageTwo,反之亦然。之所以这样做,是因为我不希望Home Shell在应用程序中不断显示其内幕。
事实是,如果我只是将按钮移到屏幕
视图,它不再绑定到命令,该命令位于 ShellViewModel
中,而不位于屏幕
ViewModel本身。我知道这些绑定是按惯例发生的,但是我不知道惯例是否涵盖了这种情况,或者我需要进行配置。
我要面对的症状是:当我运行该应用程序时,将显示PageOneView,其中带有转到第二页按钮,但是当我单击该按钮时,什么也没发生。
我问的问题是:如何正确地将ScreenView.xaml中的按钮气泡到ScreenConductorViewModel.cs中的操作?
我当前的代码如下:
PageOneView.xaml
< UserControl x:Class = ScreenConductor.PageOneView
xmlns = http://schemas.microsoft.com/winfx/2006/xaml/presentation
xmlns:x = http: //schemas.microsoft.com/winfx/2006/xaml
xmlns:mc = http://schemas.openxmlformats.org/markup-compatibility/2006
xmlns:d = http: //schemas.microsoft.com/expression/blend/2008
mc:Ignorable = d
d:DesignHeight = 300 d:DesignWidth = 300>
< Grid Background = LightGreen>
<按钮x:Name = ShowPageTwo Content =显示第二页 HorizontalAlignment = Center VerticalAlignment = Top />
< / Grid>
< / UserControl>
PageOneViewModel
使用Caliburn.Micro;
命名空间ScreenConductor {
公共类PageOneViewModel:屏幕{
受保护的重写void OnActivate(){
base.OnActivate();
}
}
}
ShellView.xaml
< Window x:Class = ScreenConductor.ShellView
xmlns = http://schemas.microsoft.com/winfx/2006/xaml/presentation
xmlns:x = http://schemas.microsoft.com/winfx/2006/xaml
Title = MainWindow Height = 350 Width = 525>
< ContentControl x:Name = ActiveItem />
< / Window>
ShellViewModel.cs
使用Caliburn.Micro;
命名空间ScreenConductor
{
public class ShellViewModel:Conductor< object>
{
public ShellViewModel()
{
ShowPageOne();
}
public void ShowPageOne()
{
ActivateItem(new PageOneViewModel());
}
public void ShowPageTwo()
{
ActivateItem(new PageTwoViewModel());
}
}
}
您可以使用以下命令:
< SomeControl cal:Message .Attach = SomeMethodOnParentVM />
这将尝试将消息冒泡到控件层次结构中,直到找到合适的处理程序为止。请注意,如果找不到处理程序,这将引发异常。我记得有一个选项可以关闭绑定,但是您可能要检查一下文档(此刻我正在从手机提供信息:-)
编辑:
好吧,如果您想在幕后获得更多信息,最好的检查来源就是信息源。作者Rob Eisenberg提到源代码非常小(我认为大约2700行代码),因此易于检查并且易于掌握
阅读CodePlex站点上的所有文档(虽然页面很少,但它们解释得足够详细,以便您将其余部分拼凑起来。)。
不确定多少您知道有关 Message
类的内容,该类包含 Attach
附加属性,但这是在标准时启动操作绑定的原因不使用控制 Name
约定(我假设您知道WPF / SL中的附加属性)。您可以在 ViewModelBinder
类
消息$>中看到标准的命名约定。 c $ c>类如下:
(是的,这是Silverlight的源代码,但这是所有其他版本的基础,并且它只是一些编译器指令以及在其他版本(例如WPF / WinRT)中出现的一些其他类)
如果您查看源代码,可以看到设置了附加属性后, Parser
类开始了解析字符串。解析器实际上会解析几种不同的格式,因此您可以附加到不同的事件和方法,还可以传递属性,例如
<按钮cal:Message.Attach = [事件点击] = [动作SomeButtonWasClicked()] />
或
< Button cal:Message.Attach = [Event MouseEnter] = [Action MouseEnteredAButton($ eventargs) />
上面您可以看到 $ eventargs
使用特殊值。有几个现成的特殊值,您也可以编写自己的特殊值(请查看此SO问题在Windows 8应用中使用MessageBinder.SpecialValues无法正常工作吗?用户正在使用 SpecialValues
进行传递
您还可以传递其他控件的CM默认属性,例如
< TextBox x:Name = TextBox1 />
<按钮cal:Message.Attach = MouseClicked(TextBox1) />
其中 Text
值 TextBox1
将被传递到VM上的 MouseClicked
方法。这是在 TextBox
的默认约定绑定中指定的(请查看 ConventionManager.AddElementConvention
及其文档)
冒泡的工作方式是检查视觉树并尝试绑定到每个级别(在<$ c $中的 SetMethodBinding
中发生c> ActionMessage 类)
这很简单但有效(它只使用 VisualTreeHelper
沿着视觉树走,直到找到合适的处理程序为止)。
不确定您可能还需要:P
上的信息吗?
I'm following this tutorial on Screen
and ScreenConductors
in Caliburn.Micro
Framework.
I'm using WPF, not Silverlight, and I have made the corresponding changes in App.xaml (using MergedDictionary for the Bootstrapper).
The original Simple Navigation example has a shell with two buttons, and a content area where two possible screens are displayed, conducted by the ShellViewModel
.
Then I tried to move each button to its counterpart View, so that PageOne would have a button to take to PageTwo, and vice-versa. I did it because I don't want the home shell continuously "showing its entrails" across the application.
The fact is, if I just move a button to a Screen
View, it no longer binds to the command, which is in the ShellViewModel
, not in the Screen
ViewModel itself. I know these bindings happen by convention, but I don't know if the convention covers this case, or I'd need to configure.
The symptom I am facing is: When I run the app, PageOneView shows, with the "Go to Page Two" button in it, but when I click the button nothing happens.
The question I ask is: "How is the proper way to "bubble" a button in a ScreenView.xaml to an action in the ScreenConductorViewModel.cs?
My current code is below:
PageOneView.xaml
<UserControl x:Class="ScreenConductor.PageOneView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid Background="LightGreen">
<Button x:Name="ShowPageTwo" Content="Show Page Two" HorizontalAlignment="Center" VerticalAlignment="Top" />
</Grid>
</UserControl>
PageOneViewModel
using Caliburn.Micro;
namespace ScreenConductor {
public class PageOneViewModel : Screen {
protected override void OnActivate() {
base.OnActivate();
}
}
}
ShellView.xaml
<Window x:Class="ScreenConductor.ShellView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<ContentControl x:Name="ActiveItem" />
</Window>
ShellViewModel.cs
using Caliburn.Micro;
namespace ScreenConductor
{
public class ShellViewModel : Conductor<object>
{
public ShellViewModel()
{
ShowPageOne();
}
public void ShowPageOne()
{
ActivateItem(new PageOneViewModel());
}
public void ShowPageTwo()
{
ActivateItem(new PageTwoViewModel());
}
}
}
As long as you bind the command using the explicit action binding syntax the bubbling should work fine. This is because to bind by name CM examines controls on the view bound to the current vm. If there is no matching named control a binding does not get created.
You can use this:
<SomeControl cal:Message.Attach="SomeMethodOnParentVM" />
This will attempt to bubble the message up the control hierarchy until a suitable handler is found. Note that this will throw an exception if no handler could be found. I recall there being an option to turn this off on the binding but you might want to check the docs (I'm contributing from my mobile at the moment :-)
Edit:
Ok if you want more info for behind the scenes, the best place to check really is the source. The author Rob Eisenberg mentions that the source is quite small (something like 2700 lines of code I think) so it's easy to inspect and easy to hold in your head
It's worth reading through all the documentation on the CodePlex site (there's a fair few pages but they explain everything in enough detail for you to piece together the rest).
Not sure how much you know about the Message
class that contains the Attach
attached property but this is what kicks off the action binding when the standard control Name
conventions aren't used (I assume you know about attached properties in WPF/SL). You can see the standard named conventions in the ViewModelBinder
class
The Message
class looks like:
(yes it's the Silverlight source but this is the base for all other versions and it's just a few compiler directives and some additional classes that appear in other version such as WPF/WinRT)
If you look at the source you can see that when the attached property is set, the Parser
class kicks off to parse the string. The parser actually parses several different formats so you can attach to different events and methods, and also pass properties e.g.
<Button cal:Message.Attach="[Event Click] = [Action SomeButtonWasClicked()]" />
or
<Button cal:Message.Attach="[Event MouseEnter] = [Action MouseEnteredAButton($eventargs)" />
Above you can see that the $eventargs
special value was used. There are several out-of-box special values, and you can also write your own (check out this SO question Using MessageBinder.SpecialValues in Windows 8 app not working? where a user was using SpecialValues
to pass the horizontal mouse position from controls for use in a synthesizer app)
You can also pass the CM default property of other controls e.g.
<TextBox x:Name="TextBox1" />
<Button cal:Message.Attach="MouseClicked(TextBox1)" />
Where the Text
value of TextBox1
will be passed to the MouseClicked
method on the VM. This is specified in the default convention bindings for TextBox
(look at ConventionManager.AddElementConvention
and the docs on that)
The bubbling works by inspecting the visual tree and attempting to bind to each level (happens in SetMethodBinding
in ActionMessage
class)
It's pretty simple but effective (it just uses VisualTreeHelper
to walk up the visual tree until a suitable handler is found)
Not sure what else you might need info on :P
这篇关于Caliburn.Micro:将屏幕中的按钮绑定到ScreenConductor中的命令的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!