XAML中的GridView ItemTemplate无法绑定到控件

3
我有一个带有ItemTemplate的GridView,其中包含一个自定义控件:

<GridView
    ItemsSource="{Binding Ubicaciones.Ubicaciones}">
    <GridView.ItemTemplate>
        <DataTemplate>
            <ctr:HabitacionControl
                Width="70"
                Height="140"
                Ubicacion="{Binding}"/>
        </DataTemplate>
    </GridView.ItemTemplate>
</GridView>

以下是我的自定义用户控件:

<UserControl
        x:Class="MySln.Mucama.Controls.HabitacionControl"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="using:MySln.Mucama.Controls"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d"
        d:DesignHeight="200"
        d:DesignWidth="97">
    <UserControl.DataContext>
        <local:HabitacionControlVM/>
    </UserControl.DataContext>
    <Grid>
        <RelativePanel>
            <Image x:Name="Puerta" Source="ms-appx:///Assets/Puerta.jpg"
                   Grid.RowSpan="5"/>
            <TextBlock Text="{Binding Ubicacion.StrNombreMesa,FallbackValue=####}"
                       HorizontalAlignment="Center"
                       VerticalAlignment="Center"
                       Foreground="AliceBlue"
                       FontWeight="ExtraBold"
                           RelativePanel.AlignHorizontalCenterWithPanel="True"/>
        </RelativePanel>
    </Grid>
</UserControl>

代码如下:

public sealed partial class HabitacionControl : UserControl
{
    public HabitacionControl()
    {
        this.InitializeComponent();
    }

    public MyClass Ubicacion
    {
        get { return (MyClass)GetValue(UbicacionProperty); }
        set { SetValue(UbicacionProperty, value); }
    }

    // Using a DependencyProperty as the backing store for Ubicacion.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty UbicacionProperty =
        DependencyProperty.Register("Ubicacion", typeof(MyClass), typeof(HabitacionControl), new PropertyMetadata(new PropertyChangedCallback(OnUbicacionChanged)));

    private static void OnUbicacionChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
       //...
    }
}

现在,我需要将每个Ubicaciones.Ubicaciones绑定到自定义控件的Ubicación属性上。
在运行时,我的网格视图生成了所有的项,但是绑定到其Ubicacion属性从未发生过。
输出窗口中没有任何警告。
我缺少什么?还是做错了什么?

1
无论在线教程或博客文章中告诉你什么,永远不要显式设置UserControl的DataContext。 - Clemens
@Clemens 我不同意关闭这个问题。这个问题的UserControlViewModel和伪DataContext使它与众不同。这也是一个很好、清晰的例子,我可以为那些正在做完全相同事情的人提供参考。我会将“DataContext = this;”问题重定向到你的示例中 :) - user1228
将UserControl的DataContext设置为本地视图模型或自身并不重要,重要的是这样做可以防止控件从其父级继承DataContext。因此,请参见Binding to custom control inside DataTemplate for ItemsControl以了解如何在UserControl的XAML中编写绑定。 - Clemens
1个回答

10

哎呀,看这个:

<UserControl.DataContext>
    <local:HabitacionControlVM/>
</UserControl.DataContext>

有人向您出售了一张肮脏、污秽的货物清单。可能是那些到处告诉人们DataContext = this;是个好主意的混蛋之一。

抱歉,扯远了。现在看看这个:

<ctr:HabitacionControl 
    Width="70" 
    Height="140" 
    Ubicacion="{Binding}"/>

我看到的是什么?那是一个伪数据上下文属性吗?是的,那是一个伪数据上下文属性。问题在于Binding针对的是HabitacionControl的数据上下文内的对象而不是它的父级。那么HabitacionControl的数据上下文是什么?

<UserControl.DataContext>
    <local:HabitacionControlVM/>
</UserControl.DataContext>

这就是为什么不要为您的UserControls创建视图模型。您已经破坏了数据绑定的工作方式。视图模型必须通过DataContext沿着可视树向下流动。当您中断此流程时,您会失败。

让我问你-TextBox是否有TextBoxViewModel? 没有。它有一个Text属性,您可以将其绑定到。您如何绑定它?您的视图模型流入TextBox.DataContext,从而允许您将视图模型的属性绑定到TextBox上公开的属性。

还有其他hacky方法可以解决这个问题,但最好的解决方案是首先不要陷入这种情况。

您需要放弃那个HabitacionControlVM,并在您的UserControl表面上公开DependencyProperties,以便您的视图模型可以绑定,提供您的UserControl需要的任何内容以使其正常工作。将UI逻辑放在HabitacionControl的代码后面。

不,这不会破坏MVVM。 UI逻辑在代码后台中很好。

如果你的 HabitacionControlVM 在执行本不应该在代码后台中进行的繁重工作,那么只需将其重构为代码后台调用的类即可。
人们认为 UserControlViewModel 反模式是正确的做法。但实际上并非如此。祝好运。

只是一个问题,我的控件现在可以访问每个_Ubicacion_,但是我的DP从未触发它的_PropertyChangedCallback_委托。 - Juan Pablo Gomez
我认为这个答案中有一些很好的建议,但是我发现很难从讽刺/挖苦中分辨出来。我希望它能够被写成直接了当的建议 :/ - Hugh W

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接