【问题标题】:Windows 10 Universal Compiled binding (x:Bind) conflict with ViewModelWindows 10 通用编译绑定 (x:Bind) 与 ViewModel 冲突
【发布时间】:2015-08-24 17:35:07
【问题描述】:

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

这是我的 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,然后在运行时更改。这会产生以下运行时错误。

Incorrect type passed into template. Based on the x:DataType global::XbindingProblem.User was expected.

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

任何人都可以对这个问题有一个很好的解释吗?

【问题讨论】:

    标签: c# xaml uwp win-universal-app


    【解决方案1】:

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

    但是,通过指定页面的 DataContextx:Bind 现在除了 CurrentUser 实例中的 CurrentUser 属性,当然它不会找到它,所以编译时错误会被抛出。

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

    this.CurrentUser = new User();
    
    InitializeComponent();
    

    但恕我直言,这不是正确的做事方式,因为它基本上是一款赛车游戏 - 它试图在 DataContext 更新之前填充 ContentPresenter,最后你将拥有 @ 987654335@(其中Text绑定到Title)和ContentPresenter附加到不同的上下文!

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

    【讨论】:

    • 感谢您的回答。你在一定程度上是正确的。但即使我们在InitializeComponent() 之后设置 CurrentUser,它仍然有效。不确定之后数据上下文如何变化。
    • 可能工作,但就像我说的,这是一个赛车游戏,你能保证吗?你知道吗,试着给你的CurrentUser 一个默认值(即new PropertyMetadata(new User())),这也有效!你问了原因,我把它给了你 - 它只是因为你为ContentPresenter分配了一个上下文页面的上下文被更新之前。
    • 我明白你关于赛车游戏的观点。是的,我尝试为CurrentUser 分配一个默认值。但就我而言,CurrentUser 在应用程序启动时不应该有值。我们不能阻止将数据上下文继承到ContentPresenter。我将ContentPresenterDataContext 设置为{x:Null}。但是还是不行。
    • 如果你真的这样下去,你可以在填充 CurrentUser 之后,在你的代码中手动将页面 datacontext 设置为 main。
    【解决方案2】:

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

    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只是为了测试:

    <Page.DataContext>
        <local:Main/>
    </Page.DataContext>
    

    以及仅用于测试的代码:

    public class Main
    {
        public String Title { get; } = "title";
        public User MainUser { get; set; }
    }
    

    并且它不会抛出任何异常,出现 Main 数据和 CurrentUser 数据。

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

    <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并按元素名称绑定,它不会崩溃,我还添加了一个等待代码来测试和设置当前用户和有用。那是一个挑战。希望它也适合你。

    【讨论】:

    • 感谢您的回答。当 CurrentUser 不为 null 时,即使不删除数据上下文也不会给出任何异常。但就我而言, CurrentUser 在应用程序启动时不应该有值。不知道为什么会这样。
    • 我已将 dp 更改为此新的 PropertyMetadata(new User() { Name = "Another", Age = "18" }));并删除了 CurrentUser = new... 并且它起作用了,你是如何实例化 CurrentUser 的?
    • 抱歉,我现在应该已经了解了您所解释的内容,这很奇怪。让我们分析一下
    • 是的,它正在将 datacontext 与 x:bind 混合,这有多奇怪不是吗?
    【解决方案3】:

    虽然它打破了 MVVM 的想法,但您可以像这样向页面添加属性:

    public MainViewModel viewModel => DataContext as MainViewModel;
    

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

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

    【讨论】:

    • 感谢您的回答。 CurrentUser 是页面中的 dp,而不是视图模型中的属性
    猜你喜欢
    • 2023-03-21
    • 2019-01-23
    • 1970-01-01
    • 2015-08-19
    • 2015-12-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-18
    相关资源
    最近更新 更多