ListView: определение ItemsPanelTemplate в словаре ресурсов

#wpf #listview #mvvm #datatemplate #itemspanel

#wpf #listview #mvvm #datatemplate #itemspanel

Вопрос:

У меня есть ListView, макет которого выглядит как вид проводника Windows (значок некоторые подробности), привязанный к списку где-нибудь в ViewModel.

Моя цель здесь — иметь возможность переключаться между представлением Explorer или классическим представлением, когда мы захотим.

Я мог бы определить, что ItemsPanelTemplate выполняет именно ту работу, чтобы правильно отобразить макет, непосредственно в ListView.ItemsPanel поле. Теперь я хотел бы определить его в ресурсах, чтобы я мог использовать его в разных представлениях, и особенно в одном элементе управления, пользователь должен иметь выбор между представлением Explorer или классическим представлением списка (отображение списка по умолчанию)

Как вы это сделали? Я не могу определить ни одного ItemsPanelTemplate в своем ResourceDictionary , и если я определю DataTemplate , это будет несовместимо (хотя я думал, что, следуя чистой логике, ItemsPanelTemplate должно наследовать от DataTemplate , но на самом деле это выглядит не так).

Фрагмент кода для фактического списка:

 <ListView.ItemsPanel>
    <ItemsPanelTemplate>
        <WrapPanel
            Width="{Binding (FrameworkElement.ActualWidth),
                RelativeSource={RelativeSource 
                AncestorType=ScrollContentPresenter}}"
            ItemWidth="{Binding (ListView.View).ItemWidth,
                RelativeSource={RelativeSource AncestorType=ListView}}"
            ItemHeight="{Binding (ListView.View).ItemHeight,
                RelativeSource={RelativeSource AncestorType=ListView}}" 
            />
            <!--
            MinWidth="{Binding ItemWidth, RelativeSource={RelativeSource Self}}"
            -->
    </ItemsPanelTemplate>
</ListView.ItemsPanel>

<ListView.ItemTemplate>
    <DataTemplate>
        <StackPanel
            Orientation="Horizontal" 
            Height="Auto" 
            Width="150" >
            <Image 
                Source="{Binding Path=Appli.AppType, Converter={StaticResource TypeToIconConverter}}"
                Margin="5"
                Height="50"
                Width="50" />
            <StackPanel 
                VerticalAlignment="Center"
                Width="90" >
                <TextBlock 
                    Text="{Binding Path=Appli.AppName}" 
                    FontSize="13" 
                    HorizontalAlignment="Left" 
                    TextWrapping="WrapWithOverflow"
                    Margin="0,0,0,1" />
                <TextBlock 
                    Text="{Binding Path=Appli.AppType}" 
                    FontSize="9" 
                    HorizontalAlignment="Left" 
                    Margin="0,0,0,1" />
            </StackPanel>
        </StackPanel>
    </DataTemplate>
</ListView.ItemTemplate>
  

Сохранить ItemTemplate в статическом ресурсе было легко, но теперь я ничего не могу сделать с ItemsPanelTemplate

Есть идеи? Я использую MVVM, поэтому в идеале стараюсь по возможности не использовать code-behind

Ответ №1:

Для этого вы бы использовали стиль для всего ListView. Итак, вы бы сделали:

 <Grid.Resources>
    <Style x:Key="ListViewStyle" TargetType="ListView">
        <Setter Property="ItemsPanel">
            <Setter.Value>
                <ItemsPanelTemplate>
                    <WrapPanel 
                        Width="{Binding (FrameworkElement.ActualWidth), RelativeSource={RelativeSource AncestorType=ScrollContentPresenter}}"
                        ItemWidth="{Binding (ListView.View).ItemWidth, RelativeSource={RelativeSource AncestorType=ListView}}"
                        ItemHeight="{Binding (ListView.View).ItemHeight, RelativeSource={RelativeSource AncestorType=ListView}}" />
                        <!-- 
                        MinWidth="{Binding ItemWidth, RelativeSource={RelativeSource Self}}" 
                        -->
                </ItemsPanelTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</Grid.Resources>

<ListView
    SelectionMode="Single"
    VerticalAlignment="Stretch"
    HorizontalAlignment="Stretch"
    HorizontalContentAlignment="Center"
    VerticalContentAlignment="Bottom"
    ScrollViewer.VerticalScrollBarVisibility="Auto"
    ItemsSource="{Binding ListUserApps, UpdateSourceTrigger=PropertyChanged}"
    SelectedIndex="{Binding SelectedUserApp, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
    Background="White"
    Style="{StaticResource ListViewStyle}">

    <ListView.ItemTemplate>
        <DataTemplate>
            <StackPanel
                Orientation="Horizontal"
                Height="Auto"
                Width="150">
                <Image
                    Source="{Binding Path=Appli.AppType, Converter={StaticResource TypeToIconConverter}}"
                    Margin="5"
                    Height="50"
                    Width="50"/>
                <StackPanel
                    VerticalAlignment="Center"
                    Width="90">

                    <TextBlock
                        Text="{Binding Path=Appli.AppName}"
                        FontSize="13"
                        HorizontalAlignment="Left"
                        TextWrapping="WrapWithOverflow"
                        Margin="0,0,0,1" />
                    <TextBlock
                        Text="{Binding Path=Appli.AppType}"
                        FontSize="9"
                        HorizontalAlignment="Left"
                        Margin="0,0,0,1" />

                </StackPanel>
            </StackPanel>
        </DataTemplate>
    </ListView.ItemTemplate>

</ListView>
  

Если вы хотите, чтобы пользователь мог переключаться между Explorer и классическим представлением, просто определите второй стиль и измените стиль listview. Это можно сделать, например, с помощью некоторых VisualStates и ‘DataStateBehavior’.

В качестве альтернативы вы могли бы создать стиль с некоторыми дататриггерами и настройщиками для отдельных панелей ItemsPanels.

Комментарии:

1. Потрясающе, спасибо, не могу поверить, что я не подумал о стилизации всего ListView. Думаю, тогда я посмотрю на DataStateBehavior. Еще раз спасибо

2. Другой связанный с этим вопрос: я не могу понять, как определить ListView.GroupStyle в предопределенном стиле. Предполагается, что это свойство доступно только для чтения, и я не понимаю, как его определить. Есть идея?