【问题标题】:How to bind from DataTemplate to Page's DataContext?如何从 DataTemplate 绑定到 Page 的 DataContext?
【发布时间】:2017-08-05 12:03:00
【问题描述】:

我有一个页面,其中使用 DataTemplate 绑定到该内容的模型,例如:

<DataTemplate x:DataType="models:MyDataType">
    ... content ...
</DataTemplate>

在该内容中,我需要能够绑定 Click 事件。我需要该单击事件存在于设置为页面的 DataContext 的视图模型中:

<Page.DataContext>
    <vm:MyViewModel x:Name="ViewModel">
</Page.DataContext>

但我真的很难让它编译。我尝试的每种方法都会导致编译错误“对象引用未设置为对象的实例”。

我知道我不能使用 x:Bind,因为它会绑定到 DataTemplate 的 DataContext,所以我一直在尝试使用 Binding,并且根据我读过的其他 SO 答案,它 似乎 em> 就像答案应该是:

Click="{Binding DataContext.Button_Click, ElementName=Page}"

其中页面定义为页面的 x:Name。我试过删除 DataContext。我已经尝试添加 ViewModel。

我误会了什么?难道不能做我想做的事吗?我尝试过使用代码隐藏,但我使用的是模板 10,它会将几乎所有内容都推送到视图模型上,这让我更难从代码隐藏中访问导航服务等内容。

【问题讨论】:

  • github.com/Windows-XAML/Template10/blob/master/Samples/Search/… 总有另一种方式。这是一个非常古老的示例,但它显示了使用 T10 完成绑定的一些最初方式,在代码隐藏中命名 ViewModel,但实例化也通过 XAML 标记进入视图。看看 MainPage 的视图。
  • 另一种方式是通过行为
  • @mvermef,第一个例子是使用x:Bind,它在数据模板中不起作用。在这里使用Behavior 有点过头了。

标签: binding uwp datacontext uwp-xaml template10


【解决方案1】:

tl;博士;使用消息。

@justinXL 是对的,'ElementName'可以工作。但它是最好的吗?

您尝试解决的问题已通过消息传递解决。大多数 MVVM 实现都包含消息传递解决方案。 Prism 使用 PubSubEvents; MVVM Light 有自己的信使。还有其他人。

这个想法是一个外部类,通常描述为message aggregator,负责无状态地接收和多播消息。这意味着您需要对聚合器的引用,而不是对发件人的引用。很美丽。

例如

一个常见的用例可能是邮件客户端,以及列表中消息的数据模板如何包含垃圾/删除按钮。当您单击该按钮时,应该调用什么?通过消息传递,您可以处理模型中的 button_press 并发送/发布消息(传递项目的消息)。

托管视图模型已订阅聚合器并正在侦听特定消息,即我们刚刚发送的 Delete 消息。收到后,它会从列表中删除它并开始从缓存/数据库或其他任何东西中删除它的过程 - 包括提示用户“你确定吗?”

这意味着您的数据模板中的所有数据绑定都是本地的,并且不会扩展到其本地范围之外。为什么这很重要?因为如果您使用元素绑定到达托管页面,则意味着您不能 1) 将此模板移动到资源字典或 2) 重用此模板。

还有两个原因。

  1. 您不能使用已编译的 x:Bind 来执行此操作,因为它已经限制了这种痛苦的绑定方法的使用 - 这很重要,因为数据模板通常位于列表中,并且应始终优先考虑性能,并且
  2. 它增加了相当大的复杂性

复杂性?

我非常喜欢复杂的解决方案。我认为它们很少见,并且是真正聪明的开发人员的商标。我喜欢看这样的代码/解决方案。复杂不等于复杂。谈到复杂性,我不是粉丝。数据绑定已经很难理解了;跨范围边界多源数据绑定纯粹是复杂的。

我就是这么想的。

【讨论】:

    【解决方案2】:

    您的绑定表达式是正确的,但它不适用于Button_Click 事件处理程序。您需要在页面的 ViewModel 中定义一个 ICommand

    由于您使用的是Template10,您应该可以像这样创建一个名为ClickCommandDelegateCommand

    private DelegateCommand<MyDataType> _clickCommand;
    public DelegateCommand<MyDataType> ClickCommand
    {
        get
        {
            _clickCommand = _clickCommand ?? new DelegateCommand<<MyDataType>>((model) =>
            {
                // put your logic here.
            });
    
            return _clickCommand;
        }
    }
    

    并且绑定将更新为

    <Button Command="{Binding DataContext.ClickCommand, ElementName=Page}" CommandParameter="{x:Bind}" />
    

    请注意,我还为按钮添加了CommandParameter 绑定,因为您可能想知道哪个MyDataType 实例与单击的按钮相关联。

    【讨论】:

    • 另请注意,如果您在 MyDataType 类中定义您的 ClickCommand,您将能够直接将 x:Bind 传递给它。这也不是一个糟糕的解决方案。
    猜你喜欢
    • 2014-07-30
    • 2013-02-04
    • 2011-11-27
    • 2015-06-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-12-29
    相关资源
    最近更新 更多