#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
в предопределенном стиле. Предполагается, что это свойство доступно только для чтения, и я не понимаю, как его определить. Есть идея?