首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Windows 10通用编译绑定(x:Bind)与ViewModel冲突

Windows 10通用编译绑定(x:Bind)与ViewModel冲突
EN

Stack Overflow用户
提问于 2015-08-25 01:35:07
回答 3查看 2.3K关注 0票数 3

我想要将页面中的一个元素绑定到代码中的依赖属性,同时使用常规绑定将另一个元素绑定到ViewModel。但是它会给出一个运行时错误。

下面是我的xaml代码。

代码语言:javascript
复制
<Page
x:Class="XbindingProblem.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:XbindingProblem"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
DataContext="{Binding Main, Source={StaticResource Locator}}"
mc:Ignorable="d">
<Page.Resources>
    <DataTemplate x:Key="UserDataTemplate" x:DataType="local:User">
        <StackPanel>
            <TextBlock Text="{x:Bind Name}" />
            <TextBlock Text="{x:Bind Age}" />
        </StackPanel>
    </DataTemplate>
</Page.Resources>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <StackPanel>
        <TextBlock Text="{Binding Title}"/>
        <ContentPresenter ContentTemplate="{StaticResource UserDataTemplate}" Content="{x:Bind CurrentUser, Mode=OneWay}"/>
    </StackPanel>        
</Grid>

这里的CurrentUser是依赖属性,它最初为null,然后在运行时更改。这会产生以下运行时错误。

代码语言:javascript
复制
Incorrect type passed into template. Based on the x:DataType global::XbindingProblem.User was expected.

问题是当CurrentUser为空时,它将ViewModel传递给UserDataTemplate而不是CurrentUser依赖属性。

有人能对这个问题有一个很好的解释吗?

EN

回答 3

Stack Overflow用户

发布于 2015-08-25 15:29:21

如果您删除DataContext="{Binding Main, Source={StaticResource Locator}}",它将正常工作。为什么?因为{x:Bind CurrentUser}正在寻找一个名为CurrentUser的属性,它位于您的MainPage.xaml.cs中。由于CurrentUser确实是页面的依赖属性,因此它将正常工作。

但是,通过指定页面的DataContextx:Bind现在将在MainViewModel实例中排除CurrentUser属性,当然它将找不到该属性,因此将抛出编译时错误。

一种可能的解决方法是,甚至在调用InitializeComponent之前就设置this.CurrentUser

代码语言:javascript
复制
this.CurrentUser = new User();

InitializeComponent();

但这不是做事情的正确方法,因为它基本上是一个赛车游戏-它试图在DataContext更新之前填充ContentPresenter,最终你会得到TextBlock ( Text绑定到Title)和ContentPresenter附加到不同的上下文!

因此,问问自己,为什么需要在Page对象中为CurrentUser创建依赖属性,而不是在MainViewModel中拥有一个普通属性(使用INotifyPropertyChanged实现)?我更喜欢后者,因为它在语义上更正确。

票数 5
EN

Stack Overflow用户

发布于 2015-08-25 04:40:34

这个问题很有趣,我所做的只是删除了数据上下文,这是后面的代码与您的类似:

代码语言:javascript
复制
public sealed partial class BlankPage1 : Page
{
    public User CurrentUser
    {
        get { return (User)GetValue(CurrentUserProperty); }
        set { SetValue(CurrentUserProperty, value); }
    }

    // Using a DependencyProperty as the backing store for CurrentUser.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty CurrentUserProperty =
        DependencyProperty.Register("CurrentUser", typeof(User), typeof(BlankPage1), new PropertyMetadata(null));

    public BlankPage1()
    {
        this.InitializeComponent();

        CurrentUser = new User() { Name = "Hello", Age = "20" };
    }
}

public class User
{
    public String Name { get; set; }
    public String Age { get; set; }
}

User类可能在另一个名称空间中,或者在typeof(...)中有另一个类依赖项属性的。因为我已经测试过了,而且还能用。页面的DataContext可以是您希望它不受影响的任何内容。

然后我添加了datacontext,只是为了测试:

代码语言:javascript
复制
<Page.DataContext>
    <local:Main/>
</Page.DataContext>

代码只是为了测试:

代码语言:javascript
复制
public class Main
{
    public String Title { get; } = "title";
    public User MainUser { get; set; }
}

并且它不抛出任何异常,显示主数据和CurrentUser数据。

更新。当用户为null时,就会发生错误,因此就像x:Bind是null一样,它会传播到绑定,以解决这个问题(这很困难):

代码语言:javascript
复制
<Page x:Name="Root"
    x:Class="Deletetb.BlankPage1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:Deletetb"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d" >
    <Page.DataContext>
        <local:Main/>
    </Page.DataContext>

    <Page.Resources>
        <DataTemplate x:Key="UserDataTemplate" x:DataType="local:User">
            <StackPanel>
                <TextBlock Text="{x:Bind Name}" />
                <TextBlock Text="{x:Bind Age}" />
            </StackPanel>
        </DataTemplate>
    </Page.Resources>
    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <StackPanel DataContext="{x:Null}">
            <TextBlock Text="{Binding DataContext.Title, ElementName=Root}" />
            <ContentPresenter  ContentTemplate="{StaticResource UserDataTemplate}" Content="{x:Bind CurrentUser, Mode=OneWay}"/>
        </StackPanel>
    </Grid>
</Page>

其中绑定已定义(TextBlock)我在父容器(StackPanel)中将datacontext设置为null,并按元素名称绑定,它不会崩溃,我还添加了一个等待代码来测试和设置当前用户,它可以工作。这是一个挑战。希望它也适用于你。

票数 1
EN

Stack Overflow用户

发布于 2015-08-25 04:51:06

尽管它稍微打破了MVVM的概念,但您可以像这样向页面添加一个属性:

代码语言:javascript
复制
public MainViewModel viewModel => DataContext as MainViewModel;

然后在XAML代码中引用页面属性

代码语言:javascript
复制
<ContentPresenter Content="{x:Bind viewModel.CurrentUser, Mode=OneWay}" />
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/32188269

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档