Изменить ListView.ItemTemplate при изменении подэлемента

скажем, у нас есть простой класс данных:

public class Ex {
    public string Prop1 {...} // notify property
    public string Prop2 {...} // notify property
}

и ObservableCollection объектов этого класса. Я хочу, чтобы эта коллекция отображалась в ListView с отдельным DataTemplated, который отличается Ex.Prop2 (если он нулевой или пустой, тогда используется template01, иначе template02). Это свойство можно изменить во время выполнения, поэтому простой «трюк» с ListView.ItemTemplateSelector не работает :(

Как добиться этой функциональности? Можно ли добиться этого каким-либо другим способом, кроме прослушивания NotifyPropertyChanged для каждого объекта коллекции и ручного изменения шаблона?

Спасибо за вашу помощь.

Ниже фрагмент кода, который у меня уже есть:

<ListView x:Name="lstTerms"
    ItemsSource="{Binding Game.Words}"
    HorizontalContentAlignment="Stretch"
    Grid.IsSharedSizeScope="True">
    <ListView.ItemContainerStyle>
        <Style>
            <Setter Property="Control.Padding" Value="0" />
        </Style>
    </ListView.ItemContainerStyle>

    <!-- checks if element is null or its Prop2 is null or empty. If so, uses NullTemplate -->
    <ListView.ItemTemplateSelector>
        <local:MySelectTemplate
            NormalTemplate="{StaticResource NormalItemTemplate}"
            NullTemplate="{StaticResource NullItemTemplate}" />
    </ListView.ItemTemplateSelector>
</ListView>

person SOReader    schedule 19.01.2012    source источник


Ответы (2)


Вместо использования TemplateSelector у вас может быть один DataTemplate, содержащий две сетки, которые переключают видимость в зависимости от значений свойств.

Вот пример:

<ListView.ItemTemplate>
    <DataTemplate>
        <Grid>
            <Grid Background="LightBlue" Name="normalGrid">
                <Grid.Style>
                    <Style>
                        <Style.Triggers>
                            <DataTrigger Binding="{Binding Path=Prop1}" Value="{x:Null}">
                                <Setter Property="Grid.Visibility" Value="Hidden"></Setter>
                            </DataTrigger>
                        </Style.Triggers>
                    </Style>
                </Grid.Style>
                <TextBlock Text="{Binding Prop1}"></TextBlock>
            </Grid>
            <Grid Background="Green" Name="nullGrid">
                <Grid.Style>
                    <Style>
                        <Style.Triggers>
                            <DataTrigger Binding="{Binding ElementName=normalGrid, Path=Visibility}" Value="Visible">
                                <Setter Property="Grid.Visibility" Value="Hidden"></Setter>
                            </DataTrigger>
                        </Style.Triggers>
                    </Style>
                </Grid.Style>
                <TextBlock Text="{Binding Prop2}"></TextBlock>
            </Grid>
        </Grid>
    </DataTemplate>
</ListView.ItemTemplate>

Очевидно, вы можете заменить элементы TextBlock на элементы управления UserControl, представляющие ваши два DataTemplates.

Если вы хотите, вы также можете устранить необходимость в громоздких стилях, привязав Grid.Visibility к свойству (с именем, например, IsVisible) в вашей ViewModel и используя VisibilityConverter.

person Greg Sansom    schedule 20.01.2012
comment
На самом деле я использовал этот подход, но подход Рэйчел тоже хорош. Я не знаю, почему я сам об этом не подумал... слишком много работы убивает тебя ;D - person SOReader; 29.01.2012

Обычно я просто использую ContentControl, который меняет свой ContentTemplate на основе DataTrigger. DataTriggers реагируют на изменение значения, а DataTemplateSelectors — нет.

<Style x:Key="SomeStyleKey" TargetType="{x:Type ContentControl}">
    <Setter Property="ContentTemplate" Value="{StaticResource DefaultTemplate}" />
    <Style.Triggers>
        <DataTrigger Binding="{Binding Prop2}" Value="{x:Null}">
            <Setter Property="ContentTemplate" Value="{StaticResource NullTemplate}" />
        </DataTrigger>
        <DataTrigger Binding="{Binding Prop2}" Value="">
            <Setter Property="ContentTemplate" Value="{StaticResource NullTemplate}" />
        </DataTrigger>
    </Style.Triggers>
</Style>

...

<ListView.ItemTemplate>
    <DataTemplate>
        <ContentControl Style="{StaticResource SomeStyleKey}" />
    </DataTemplate>
</ListView.ItemTemplate>

Вы также можете использовать Converter, который возвращает String.IsNullOrEmpty(value), если вам нужен один DataTrigger

person Rachel    schedule 20.01.2012