Caliburn.Micro:将屏幕中的按钮绑定到ScreenConductor中的命令 [英] Caliburn.Micro: Binding Button in a Screen to a command in ScreenConductor

查看:65
本文介绍了Caliburn.Micro:将屏幕中的按钮绑定到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 =显示第二页 Horizo​​ntalAlignment = 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>类如下:



http://caliburnmicro.codeplex.com/SourceControl/changeset/view/35582bb2a8dfdd3fcd71a07fa82581ddb93a786f#src/Caliburn.Micro.Silverlight/Message.cs



(是的,这是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:

http://caliburnmicro.codeplex.com/SourceControl/changeset/view/35582bb2a8dfdd3fcd71a07fa82581ddb93a786f#src/Caliburn.Micro.Silverlight/Message.cs

(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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆