在 UWP 中的 GridView 的 ItemTemplate 布局用户控件中使用 x:Bind

Using x:Bind inside the GridView's ItemTemplate layout User Control in UWP

在通用 Windows 平台 API 中,如何使用 x:Bind 在用户控件(旨在作为 GridView 的 ItemTemplate 的布局)内部绑定到实例属性GridView 的 ItemSource?

背景

我正在尝试 re-create 在 Windows 10 个股票应用程序中找到的布局,例如体育、新闻、财经等

我在应用程序的主要区域使用了两个 GridView;一张用于 "featured articles"(2 张带标题的大照片),一张用于所有其他文章(带标题的小照片)。

我能够绑定到我在后面的代码中提供的数据源(一个列表,其中 NewsItem 是一个带有图像和标题的 POCO 属性)这是 MainPage.xaml:

<Page ...
   xmlns:data="using:NewsApp.Models" />

.... 

<GridView Name="FeaturedItems" Grid.Row="0">
  <GridView.ItemTemplate>
    <DataTemplate x:DataType="data:NewsItem">
      <Grid Name="mainPanel" HorizontalAlignment="Stretch" Width="500" >
        <Image Source="{x:Bind Image}" HorizontalAlignment="Stretch" />
        <TextBlock Text="{x:Bind Headline}" />
      </Grid>
    </DataTemplate>
  </GridView.ItemTemplate>
</GridView>

....

图像和标题绑定得很好(即使它们的样式不正确)。但是,相反,我认为我需要绑定到用户控件以获得我想要的样式选项,控制调整大小尤其是。当使用视觉状态触发器并总体上简化 XAML 时(至少,这是向我建议的技术。)

所以,我在项目中添加了一个新的用户控件 (FeaturedItemControl.xaml),并复制到 DataTemplate 的 child 网格中:

<UserControl ... >
  <Grid Name="mainPanel" HorizontalAlignment="Stretch" Width="500" >
    <Image Source="{x:Bind Image}" HorizontalAlignment="Stretch" />
    <TextBlock Text="{x:Bind Headline}" />
  </Grid>
</UserControl>

然后返回 MainPage.xaml,我更改 DataTemplate 以引用新的 FeaturedItemControl:

<GridView Name="FeaturedItems" Grid.Row="0">
  <GridView.ItemTemplate>
    <DataTemplate x:DataType="data:NewsItem">
      <local:FeaturedItemControl HorizontalAlignment="Stretch" VerticalAlignment="Stretch" />
    </DataTemplate>
  </GridView.ItemTemplate>
</GridView>

但是,我收到图像和标题属性的错误消息:绑定路径无效 'Headline':无法在类型 'FeaturedItemControl' 上找到 属性 'Headline' .

我已经尝试了一些事情,但我只是在不理解我在做什么的情况下就把代码扔到问题上。任何帮助将不胜感激。

感谢您的关注。

x:Bind 并不像 Binding/DataContext 那样真正分层。此外,当您不直接在 DataTemplate 内(例如在您的用户控件内)时,x:Bind 尝试使用的 object 是 'this' 而不是 'this.DataContext'。我目前关于如何解决此类问题的思路是尽量不要在任何地方使用 UserControls。相反,更喜欢包含在 ResourceDictionary 中的 DataTemplates。不过,这种方法有一些非常强烈的警告,例如,如果您在从 ResourceDictionary 项模板(添加新项)创建的数据模板中使用 x:Bind,您将导致 xaml 编译器崩溃。你可以在这里找到一个非常完整的例子 https://github.com/Microsoft/Windows-universal-samples/tree/master/Samples/XamlBind 重要的是要注意他们在示例中显示正在使用的 ResourceDictionary 它实际上不仅仅是一个 ResourceDictionary.xaml 它也是一个 ResourceDictionary.xaml.cs (这是 x:Bind 生成的代码结束的地方)

另一种选择是将标题和图像作为属性添加到您的用户控件上,然后从模板中 x:Bind 添加它们,然后像​​您当前所做的那样在用户控件中添加 x:Bind,但现在 x:Bind 生成的路径 'this.Headline' 将存在。不幸的是,事物的顺序实际上是绑定的,这意味着您在用户控件中拥有的 x:Bind 必须是 OneWay 而不是默认的 OneTime。这是因为 x:Bind OneTime 在 InitializeComponent 调用中进行了绑定,任何一组 properties/DataContext 的东西都没有完成,直到已经 运行.

总而言之,您有两个选择,在任何地方使用数据模板,或者绑定到直接在用户控件上的属性。

嗯,可以在用户控件中使用 x:Bind,但您需要在后面添加一些额外的代码。 我在我的项目中遇到了同样的问题,你可以在这里看到结果:https://github.com/AppCreativity/Kliva/tree/master/src/Kliva/Controls

所以您需要做的是,在您的用户控件的隐藏代码中创建一个指向正确的 DataContext 的 属性。 如果这样做,您可以在控件的 xaml 中使用该 DataContext 的属性:例如: 请注意,在控件的构造函数中,您确实需要添加:DataContextChanged += (sender, args) => this.Bindings.Update(); 因为数据上下文会根据使用控件的页面而变化!

然后在放置此控件的页面上,您还需要执行相同的操作才能使 x:bind 正常工作。 您将在我的 MainPage.DeviceFamily-Mobile.xamlMainPage.xaml.cs 文件示例中看到这一点。

希望对您有所帮助。

根据 Depechie 的回答,我为后代制定了这个小骗局:

请注意,您必须使用此技术将 VisualStateManager 与数据绑定控件(GridView、ListView)数据模板中的项目一起使用。

1) 创建用户控件。

2) 在您的页面中剪切 DataTemplate 的内容并将其粘贴到替换模板网格的用户控件中。

3) 从数据模板中引用用户控件:

4) 修改用户控件的内容,更改 x:Bind 语句以使用 object.property 符号:

<UserControl>
  <StackPanel>
    <Image Source="{x:Bind NewsItem.LeadPhoto}" />
    <TextBlock Text="{x:Bind NewsItem.Headline}" />
    <TextBlock Text="{x:Bind NewsItem.Subhead}" />
  </StackPanel>
</UserControl>

5) 在用户控件的隐藏代码中添加:

public Models.NewsItem NewsItem { get { return this.DataContext as Models.NewsItem; } }

public ContactTemplate()
{
  this.InitializeComponent();
  this.DataContextChanged += (s, e) => Bindings.Update();
}