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

C# Windows 10通用编译绑定(x:Bind)与ViewModel冲突,c#,xaml,uwp,win-universal-app,C#,Xaml,Uwp,Win Universal App,我希望使用编译绑定将页面中的一个元素绑定到代码隐藏中的依赖属性,同时使用常规绑定将另一个元素绑定到ViewModel。但它给出了一个运行时错误 Incorrect type passed into template. Based on the x:DataType global::XbindingProblem.User was expected. 这是我的xaml代码 <Page x:Class="XbindingProblem.MainPage" xmlns="http://sche

我希望使用编译绑定将页面中的一个元素绑定到代码隐藏中的依赖属性,同时使用常规绑定将另一个元素绑定到ViewModel。但它给出了一个运行时错误

Incorrect type passed into template. Based on the x:DataType global::XbindingProblem.User was expected.
这是我的xaml代码

<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时,它将ViewModel传递到UserDataTemplate而不是CurrentUser依赖项属性


有人能很好地解释这个问题吗?

这个问题很有趣,我所做的只是删除datacontext,这是与您的代码类似的代码:

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; }
}
您可能在另一个命名空间中有用户类,或者在dependency属性的typeof(…)中有另一个类。因为我已经测试过了,效果很好。页面的DataContext可以是您想要的任何内容,但不会影响它

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

<Page.DataContext>
    <local:Main/>
</Page.DataContext>
并且它不会抛出任何异常,显示主数据和当前用户数据

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



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

尽管它稍微打破了MVVM的概念,但您可以向页面添加如下属性:

public MainViewModel viewModel => DataContext as MainViewModel;
然后在XAML代码中引用page属性

<ContentPresenter Content="{x:Bind viewModel.CurrentUser, Mode=OneWay}" />

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

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

一种可能的修复方法是尽早设置
this.CurrentUser
,甚至在调用
InitializeComponent
之前

this.CurrentUser = new User();

InitializeComponent();
但这不是正确的做法,因为它基本上是一款赛车游戏-它试图在
数据上下文更新之前填充
ContentPresenter
,最终你将拥有
文本块
(其中
文本
绑定到
标题
)和
ContentPresenter
附加到不同的上下文


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

谢谢你的回答。当CurrentUser不为null时,即使不删除数据上下文,它也不会给出任何异常。但在我的例子中,CurrentUser在应用程序启动时不应该有值。不知道它为什么会这样。我已经将dp更改为这个新的PropertyMetadata(new User(){Name=“other”,Age=“18”});并删除了CurrentUser=new。。。它是有效的,你是如何引用当前用户的?对不起,我读到了,我现在应该已经理解了你刚才解释的内容,这很奇怪。让我们分析一下,是的,它将数据上下文与x:bind混合在一起,这有多奇怪,不是吗?谢谢你的回答。CurrentUser是页面中的dp,而不是视图模型中的属性感谢您的回答。在某种程度上你是对的。但即使我们在
初始化组件()之后设置CurrentUser,它也能工作。不确定数据上下文在那之后会如何变化。它可能会工作,但就像我说的,这是一个赛车游戏,你能保证吗?你知道吗,试着给你的
CurrentUser
一个默认值(即
newpropertymetadata(new User())
),这同样有效!你问了原因,我给了你-它之所以有效,是因为你在更新页面上下文之前为
ContentPresenter
分配了一个上下文。我明白你关于赛车游戏的观点。是的,我尝试为
CurrentUser
指定默认值。但在我的例子中,
CurrentUser
在应用程序启动时不应该有值。我们不能阻止将数据上下文继承到
ContentPresenter
。我将
ContentPresenter
DataContext
设置为
{x:Null}
。但它仍然不起作用。如果你真的这样做,你可以在当前用户被填充后,在你的代码中手动将pages datacontext设置为main。
<ContentPresenter Content="{x:Bind viewModel.CurrentUser, Mode=OneWay}" />
this.CurrentUser = new User();

InitializeComponent();