通用应用程序:如何将 ListViewItem(容器)的 属性 绑定到实际项目(视图模型)?

Universal Apps: How to bind a property of a ListViewItem (container) to the actual item (View Model)?

我有这个ListView

<ListView ItemsSource="{Binding Items}">
    <ListView.ItemContainerStyle>
        <Style TargetType="ListViewItem">
            <Setter Property="Background" Value="{Binding IsValid, Converter={StaticResource BooleanToBrushConverter}" />
        </Style>
    </ListView.ItemContainerStyle>
    <ListView.ItemTemplate>
        <DataTemplate>
            <TextBlock Text="{Binding Name}" />
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>

我知道绑定不适用于通用应用程序中的设置器,但是,如何将项目的容器与项目本身绑定?如果您不能提供任何逻辑,只能提供常量值,那么创建自定义容器有什么意义?

您需要小心 UWP ListViewItem 中的背景,因为围绕它有很多复杂的主题,包括不同种类的按下和拖动背景

我认为实现此目的的更简单方法是更改​​ ListViewItem 中的内容对齐方式,然后将网格添加到您可以从中添加背景的项目模板

<ListView ItemsSource="{Binding Items}">
<ListView.ItemContainerStyle>
    <Style TargetType="ListViewItem">
        <Setter Property="HorizontalContentAlignment" Value="Stretch"/>
        <Setter Property="VerticalContentAlignment" Value="Stretch"/>
    </Style>
</ListView.ItemContainerStyle>
<ListView.ItemTemplate>
    <DataTemplate>
        <Grid Background="{Binding Path=IsValid, Converter={StaticResource BooleanToBrushConverter}}">
            <TextBlock Text="{Binding Name}" VerticalAlignment="Center" />
        </Grid>
    </DataTemplate>
</ListView.ItemTemplate>

这真的很简单。

I would guess you are trying to use the variable sized grid view? That's a pretty common request, actually. But what you are asking for is tricky because of the various scopes and how things are rendered.

您首先想到的是用您自己的自定义 ListView 覆盖 ListView。我们称它为 MyListView。像这样:

public class MyItem
{
    public int RowSpan { get; set; }
    public int ColSpan { get; set; }
}

public class MyListView : ListView
{
    protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
    {
        var model = item as MyItem;
        try
        {
            element.SetValue(VariableSizedWrapGrid.ColumnSpanProperty, model.ColSpan);
            element.SetValue(VariableSizedWrapGrid.RowSpanProperty, model.RowSpan);
        }
        catch
        {
            element.SetValue(VariableSizedWrapGrid.ColumnSpanProperty, 1);
            element.SetValue(VariableSizedWrapGrid.RowSpanProperty, 1);
        }
        finally
        {
            element.SetValue(VerticalContentAlignmentProperty, VerticalAlignment.Stretch);
            element.SetValue(HorizontalContentAlignmentProperty, HorizontalAlignment.Stretch);
            base.PrepareContainerForItemOverride(element, item);
        }
    }
}

一切都发生在 PrepareContainerForItemOverride 中,这是您需要在子类中重写的唯一方法。另请注意,我没有将它们设置为绑定。这是因为只有在呈现项目时才会观察到这些属性。如果您想刷新您的 ListView 并根据新值重新呈现您的项目,您需要在根面板上调用 InvalidateMeasure(),这很棘手。你可以这样做:

// MyListView

public void Update()
{
    if (!(this.ItemsPanelRoot is VariableSizedWrapGrid))
        throw new ArgumentException("ItemsPanel is not VariableSizedWrapGrid");

    foreach (var container in this.ItemsPanelRoot.Children.Cast<GridViewItem>())
    {
        var model = item as MyItem;
        VariableSizedWrapGrid.SetRowSpan(container, data.RowSpan);
        VariableSizedWrapGrid.SetColumnSpan(container, data.ColSpan);
    }

    this.ItemsPanelRoot.InvalidateMeasure();
}

如果这有意义,您可以查看整个实现 here

祝你好运!

好的,您无法使用常规方法绑定到 DataContext,但是您可以使用其他(智能)方法来完成

参见 this post。它提供了一个 "helper" 允许使用 Setter.

绑定到 DataContext