Universal Apps: How to bind a property of a ListVi

2019-08-14 19:19发布

问题:

I have this 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>

I KNOW Bindings don't work for Setters in Universal Applications but, then how do I bind a the container of an item with the item itself? What's the point of creating a custom container if you cannot provide any logic, but constant values?

回答1:

You need to be careful with backgrounds in the UWP ListViewItem, as there is a lot of complex theming around this including different kinds of pressed and drag backgrounds

I think an easier way to achieve this is to change the content alignments in the ListViewItem, and then add a grid to your item template from which you can add your background

<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>



回答2:

OK, you can't bind to the DataContext using the normal ways, but you can do it using other (smart) methods:

Please, look at this post. It provides a "helper" that allows to bind to the DataContext with a Setter.

http://dlaa.me/blog/post/10089023



回答3:

This is pretty easy, really.

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.

The first think you will need to do is override ListView with your own custom ListView. Let's call it MyListView. Like this:

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);
        }
    }
}

Everything takes place in PrepareContainerForItemOverride and it's the only method you need override in the subclass. Please also notice that I have not set them to a binding. This is because these properties are only observed when the item is rendered. If you want to refresh your ListView and re-render your items based on new values, you need to call InvalidateMeasure() on the root panel, which is tricky. You can do it like this:

// 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();
}

If this makes sense, you can see the entire implementation here.

Best of luck!