ListView

29 篇文章 0 订阅
19 篇文章 1 订阅

ListView 继承自简单的没有特色的ListBox。增加了对基于列显示的支持,并增加了快速切换视图或显示模式的能力,而不需要重新绑定数据以及重新构建列表。

ListView类是一个特殊的列表类,它是专门针对显示相同数据的不同视图而设计的。如果要构建显示每个数据项几部分信息的多列视图,ListView 控件特别有用。ListView类继承自ListBox类,并使用 View 属性进行扩展。

GridView

GridView类继承自ViewBase类,表示具有多列的列表视图。通过GridVeiw.Columns 集合添加GridViewColumn 对象可定义这些列。

<ListView Grid.Row="0" Grid.Column="0" Margin="5" ItemsSource="{Binding Path=Orders}">
    <ListView.View>
        <GridView>
            <GridView.Columns>
                <GridViewColumn Header="Price" DisplayMemberBinding="{Binding Path=Price}" />
                <GridViewColumn Header="Volume" DisplayMemberBinding="{Binding Path=Volume}" />
                <GridViewColumn Header="OrderDate" DisplayMemberBinding="{Binding Path=OrderDate, StringFormat={}{0:D}}" />
            </GridView.Columns>
        </GridView>
    </ListView.View>
</ListView>

单元格模板

可以视图单元格模板 CellTemplate 属性来重写列,它与DataTemplate很相似,但是只能应用于一列数据。单元格模板并不局限于只能使用TextBlock属性,也可以使用其他元素,比如使用 转化器帮助从文件系统加载相应的图像文件。

<ListView Grid.Row="0" Grid.Column="1" Margin="5" ItemsSource="{Binding Path=Orders}">
    <ListView.View>
        <GridView>
            <GridView.Columns>
                <GridViewColumn Header="Price" DisplayMemberBinding="{Binding Path=Price}" />
                <GridViewColumn Header="Volume" DisplayMemberBinding="{Binding Path=Volume}" />
                <GridViewColumn Header="OrderDate" Width="100">
                    <GridViewColumn.CellTemplate>
                        <DataTemplate>
                            <TextBlock Text="{Binding Path=OrderDate, StringFormat={}{0:D}}" TextWrapping="Wrap"></TextBlock>
                        </DataTemplate>
                    </GridViewColumn.CellTemplate>
                </GridViewColumn>
                <GridViewColumn Header="Image" >
                    <GridViewColumn.CellTemplate>
                        <DataTemplate>
                            <Image Source="{Binding Path=Image, Converter={StaticResource ImagePathConverter}}"></Image>
                        </DataTemplate>
                    </GridViewColumn.CellTemplate>
                </GridViewColumn>
            </GridView.Columns>
        </GridView>
    </ListView.View>
</ListView>

创建自定义视图

如果GridView视图不能满足需要,可以创建自己的视图以扩展ListView 控件的功能。不过,这并不容易实现。

视图通过重写两个受保护的属性进行工作:DefaultStyleKey 和 ItemContainerDefaultStyleKey。这两个属性都返回名为 ResourceKey 的特殊对象,该对象指向在XAML中定义的样式。DefaultStyleKey 将被用于配置整个ListView控件的样式,而ItemContainerDefaultStyleKey 则将被用于配置ListView控件中的每个ListViewItem元素的样式。尽管这些样式可修改任意属性,但它们通常通过替换用于ListView 控件的ControlTemplate 以及用于每个ListViewItem元素的DataTemplate 进行工作。

视图类

这里除了ViewBase 所需的DefaultStyleKey 与 ItemContainerDefaultStyleKey 外,还另外定义了三个属性:ItemTemplate、SelectedBackground、SelectedBorderBrush,其中ItemTemplate用于使用时提供正确的数据模板,SelectedBackground与SelectedBorderBrush用于为视图传递额外信息,在视图样式中可以绑定到这两个属性来使用。

public class TileView : ViewBase
{
    private DataTemplate itemTemplate;
    public DataTemplate ItemTemplate
    {
        get { return itemTemplate; }
        set { itemTemplate = value; }
    }
    private Brush selectedBackground = Brushes.Transparent;
    public Brush SelectedBackground
    {
        get { return selectedBackground; }
        set { selectedBackground = value; }
    }
    private Brush selectedBorderBrush = Brushes.Black;
    public Brush SelectedBorderBrush
    {
        get { return selectedBorderBrush; }
        set { selectedBorderBrush = value; }
    }
    protected override object DefaultStyleKey
    {
        get { return new ComponentResourceKey(GetType(), "TileView"); }
    }

    protected override object ItemContainerDefaultStyleKey
    {
        get { return new ComponentResourceKey(GetType(), "TileViewItem"); }
    }
}

视图样式

视图样式需要放到资源文件,为了自动检索到,我们这里将其命名为Generic.xaml 并放到Themes文件夹下。视图样式中主要为 ListView 的 ItemsPanel 属性应用了 WrapPanel,将View.ItemTemplate 绑定到ListViewItem的 ContentTemplate属性上,同时应用了再视图里面定义的 SelectedBackground与SelectedBorderBrush 这两个属性。

Themes/Generic.xaml

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:local="clr-namespace:TestListView">
    <Style x:Key="{ComponentResourceKey TypeInTargetAssembly={x:Type local:TileView}, ResourceId=TileView}" TargetType="{x:Type ListView}" BasedOn="{StaticResource {x:Type ListBox}}">
        <Setter Property="BorderBrush" Value="Black"></Setter>
        <Setter Property="BorderThickness" Value="0.5"></Setter>
        <Setter Property="Grid.IsSharedSizeScope" Value="True"></Setter>
        <Setter Property="ItemsPanel">
            <Setter.Value>
                <ItemsPanelTemplate>
                    <WrapPanel Width="{Binding (FrameworkElement.ActualWidth), RelativeSource={RelativeSource AncestorType=ScrollContentPresenter}}"/>
                </ItemsPanelTemplate>
            </Setter.Value>
        </Setter>
    </Style>
    <Style x:Key="{ComponentResourceKey TypeInTargetAssembly={x:Type local:TileView},ResourceId=TileViewItem}" TargetType="{x:Type ListViewItem}" BasedOn="{StaticResource {x:Type ListBoxItem}}">
        <Setter Property="Padding" Value="3"/>
        <Setter Property="Margin" Value="5"/>
        <Setter Property="HorizontalContentAlignment" Value="Center"/>
        <Setter Property="ContentTemplate" Value="{Binding Path=View.ItemTemplate, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ListView}}}"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type ListBoxItem}">
                    <Border Name="Border" BorderThickness="1" CornerRadius="3" >
                        <ContentPresenter />
                    </Border>
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsSelected" Value="True">
                            <Setter TargetName="Border" Property="BorderBrush" Value="{Binding Path=View.SelectedBorderBrush, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ListView}}}"/>
                            <Setter TargetName="Border" Property="Background" Value="{Binding Path=View.SelectedBackground, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ListView}}}"/>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

创建视图

我们在窗口资源里面来创建视图,主要需要为视图类提供数据模板,如果有需要的话,也可以为在视图类里面定义的SelectedBackground与SelectedBorderBrush 这两个属性提供数据。

<Window.Resources>
    <local:ImagePathConverter x:Key="ImagePathConverter"></local:ImagePathConverter>
        
    <GridView x:Key="NormalGridView">
        <GridView.Columns>
            <GridViewColumn Header="Price" DisplayMemberBinding="{Binding Path=Price}" />
            <GridViewColumn Header="Volume" DisplayMemberBinding="{Binding Path=Volume}" />
            <GridViewColumn Header="OrderDate" DisplayMemberBinding="{Binding Path=OrderDate, StringFormat={}{0:D}}" />
        </GridView.Columns>
    </GridView>
    <local:TileView x:Key="ImageView" SelectedBackground="DarkRed">
        <local:TileView.ItemTemplate>
            <DataTemplate>
                <StackPanel Width="150" VerticalAlignment="Top">
                    <Image Source="{Binding Path=Image, Converter={StaticResource ImagePathConverter}}"></Image>
                    <TextBlock TextWrapping="Wrap" HorizontalAlignment="Center"  Text="{Binding Path=Price}" ></TextBlock>
                </StackPanel>
            </DataTemplate>
        </local:TileView.ItemTemplate>
    </local:TileView>
    <local:TileView x:Key="ImageDetailView" SelectedBackground="LightSteelBlue">
        <local:TileView.ItemTemplate>
            <DataTemplate>
                <Grid>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="Auto"></ColumnDefinition>
                        <ColumnDefinition Width="Auto" SharedSizeGroup="Col2"></ColumnDefinition>
                    </Grid.ColumnDefinitions>
                    <Image Margin="5"  Width="100" Source="{Binding Path=Image, Converter={StaticResource ImagePathConverter}}"></Image>
                    <StackPanel Grid.Column="1" VerticalAlignment="Center">
                        <TextBlock FontWeight="Bold" Text="{Binding Path=Price}"></TextBlock>
                        <TextBlock Text="{Binding Path=Volume}"></TextBlock>
                        <TextBlock Text="{Binding Path=OrderDate, StringFormat={}{0:D}}"></TextBlock>
                    </StackPanel>
                </Grid>
            </DataTemplate>
        </local:TileView.ItemTemplate>
    </local:TileView>
</Window.Resources>

使用视图

使用视图比较简单,视图已经在窗口资源里面定义了,只需要绑定到ListView的View属性上即可。因为这里创建了多个视图,我们可以根据用户选择来使用不同的视图。

<ListView Grid.Row="1" Grid.ColumnSpan="2" Margin="5" ItemsSource="{Binding Path=Orders}" View="{StaticResource ImageView}" Name="customListView" />

<Grid Grid.Row="2" Grid.ColumnSpan="2">
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="Auto"/>
        <ColumnDefinition/>
    </Grid.ColumnDefinitions>
    <TextBlock Margin="5" VerticalAlignment="Center">Choose your view:</TextBlock>
    <ComboBox Grid.Column="1" Margin="5" Width="150" HorizontalAlignment="Left" SelectionChanged="ComboBox_SelectionChanged">
        <ComboBoxItem>NormalGridView</ComboBoxItem>
        <ComboBoxItem>ImageView</ComboBoxItem>
        <ComboBoxItem>ImageDetailView</ComboBoxItem>
    </ComboBox>
</Grid>
private void ComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    ComboBoxItem selectedItem = (ComboBoxItem)((ComboBox)sender).SelectedItem;
    customListView.View = (ViewBase)this.FindResource(selectedItem.Content);
}

下面给出完整代码,Themes/Generic.xmal 在前面已经完整给出了,这里不再重复

MainWindow.xaml

<Window x:Class="TestListView.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:TestListView"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">

    <Window.Resources>
        <local:ImagePathConverter x:Key="ImagePathConverter"></local:ImagePathConverter>

        <GridView x:Key="GridView">
            <GridView.Columns>
                <GridViewColumn Header="Price" DisplayMemberBinding="{Binding Path=Price}" />
                <GridViewColumn Header="Volume" DisplayMemberBinding="{Binding Path=Volume}" />
                <GridViewColumn Header="OrderDate" DisplayMemberBinding="{Binding Path=OrderDate, StringFormat={}{0:D}}" />
                <GridViewColumn Header="Image" DisplayMemberBinding="{Binding Path=Image, Converter={StaticResource ImagePathConverter}}" />
            </GridView.Columns>
        </GridView>
        <local:TileView x:Key="ImageView" SelectedBackground="DarkRed">
            <local:TileView.ItemTemplate>
                <DataTemplate>
                    <StackPanel Width="150" VerticalAlignment="Top">
                        <Image Source="{Binding Path=Image, Converter={StaticResource ImagePathConverter}}"></Image>
                        <TextBlock TextWrapping="Wrap" HorizontalAlignment="Center"  Text="{Binding Path=Price}" ></TextBlock>
                    </StackPanel>
                </DataTemplate>
            </local:TileView.ItemTemplate>
        </local:TileView>
        <local:TileView x:Key="ImageDetailView" SelectedBackground="LightSteelBlue">
            <local:TileView.ItemTemplate>
                <DataTemplate>
                    <Grid>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="Auto"></ColumnDefinition>
                            <ColumnDefinition Width="Auto" SharedSizeGroup="Col2"></ColumnDefinition>
                        </Grid.ColumnDefinitions>
                        <Image Margin="5"  Width="100" Source="{Binding Path=Image, Converter={StaticResource ImagePathConverter}}"></Image>
                        <StackPanel Grid.Column="1" VerticalAlignment="Center">
                            <TextBlock FontWeight="Bold" Text="{Binding Path=Price}"></TextBlock>
                            <TextBlock Text="{Binding Path=Volume}"></TextBlock>
                            <TextBlock Text="{Binding Path=OrderDate, StringFormat={}{0:D}}"></TextBlock>
                        </StackPanel>
                    </Grid>
                </DataTemplate>
            </local:TileView.ItemTemplate>
        </local:TileView>
    </Window.Resources>
    
    <Grid Name="myGrid">
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>

        <ListView Grid.Row="0" Grid.Column="0" Margin="5" ItemsSource="{Binding Path=Orders}">
            <ListView.View>
                <GridView>
                    <GridView.Columns>
                        <GridViewColumn Header="Price" DisplayMemberBinding="{Binding Path=Price}" />
                        <GridViewColumn Header="Volume" DisplayMemberBinding="{Binding Path=Volume}" />
                        <GridViewColumn Header="OrderDate" DisplayMemberBinding="{Binding Path=OrderDate, StringFormat={}{0:D}}" />
                    </GridView.Columns>
                </GridView>
            </ListView.View>
        </ListView>

        <ListView Grid.Row="0" Grid.Column="1" Margin="5" ItemsSource="{Binding Path=Orders}">
            <ListView.View>
                <GridView>
                    <GridView.Columns>
                        <GridViewColumn Header="Price" DisplayMemberBinding="{Binding Path=Price}" />
                        <GridViewColumn Header="Volume" DisplayMemberBinding="{Binding Path=Volume}" />
                        <GridViewColumn Header="OrderDate" Width="100">
                            <GridViewColumn.CellTemplate>
                                <DataTemplate>
                                    <TextBlock Text="{Binding Path=OrderDate, StringFormat={}{0:D}}" TextWrapping="Wrap"></TextBlock>
                                </DataTemplate>
                            </GridViewColumn.CellTemplate>
                        </GridViewColumn>
                        <GridViewColumn Header="Image" >
                            <GridViewColumn.CellTemplate>
                                <DataTemplate>
                                    <Image Source="{Binding Path=Image, Converter={StaticResource ImagePathConverter}}"></Image>
                                </DataTemplate>
                            </GridViewColumn.CellTemplate>
                        </GridViewColumn>
                    </GridView.Columns>
                </GridView>
            </ListView.View>
        </ListView>

        <ListView Grid.Row="1" Grid.ColumnSpan="2" Margin="5" ItemsSource="{Binding Path=Orders}" View="{StaticResource GridView}" Name="customListView" />

        <Grid Grid.Row="2" Grid.ColumnSpan="2">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto"/>
                <ColumnDefinition/>
            </Grid.ColumnDefinitions>
            <TextBlock Margin="5" VerticalAlignment="Center">Choose your view:</TextBlock>
            <ComboBox Grid.Column="1" Margin="5" Width="150" HorizontalAlignment="Left" SelectionChanged="ComboBox_SelectionChanged">
                <ComboBoxItem>GridView</ComboBoxItem>
                <ComboBoxItem>ImageView</ComboBoxItem>
                <ComboBoxItem>ImageDetailView</ComboBoxItem>
            </ComboBox>
        </Grid>
    </Grid>
</Window>

MainWindow.xaml.cs

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Media;
using System.Windows.Media.Imaging;

namespace TestListView;


public class ViewModelBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler? PropertyChanged;
    protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
    protected virtual bool SetProperty<T>(ref T member, T value, [CallerMemberName] string? propertyName = null)
    {
        if (EqualityComparer<T>.Default.Equals(member, value))
        {
            return false;
        }
        member = value;
        OnPropertyChanged(propertyName);
        return true;
    }
}
public class Order : ViewModelBase
{
    public decimal price = 0;
    public decimal Price { get => price; set => SetProperty(ref price, value); }
    public int volume = 0;
    public int Volume { get => volume; set => SetProperty(ref volume, value); }

    public DateTime orderDate = DateTime.Now;
    public DateTime OrderDate { get => orderDate; set => SetProperty(ref orderDate, value); }

    public string image = string.Empty;
    public string Image { get => image; set => SetProperty(ref image, value); }
}
public class ImagePathConverter : IValueConverter
{
    private string imageDirectory = Directory.GetCurrentDirectory();
    public string ImageDirectory
    {
        get { return imageDirectory; }
        set { imageDirectory = value; }
    }
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        string imagePath = Path.Combine(ImageDirectory, (string)value);
        return new BitmapImage(new Uri(imagePath));
    }
    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotSupportedException("The method or operation is not implemented.");
    }
}

public class TileView : ViewBase
{
    private DataTemplate itemTemplate;
    public DataTemplate ItemTemplate
    {
        get { return itemTemplate; }
        set { itemTemplate = value; }
    }
    private Brush selectedBackground = Brushes.Transparent;
    public Brush SelectedBackground
    {
        get { return selectedBackground; }
        set { selectedBackground = value; }
    }
    private Brush selectedBorderBrush = Brushes.Black;
    public Brush SelectedBorderBrush
    {
        get { return selectedBorderBrush; }
        set { selectedBorderBrush = value; }
    }
    protected override object DefaultStyleKey
    {
        get { return new ComponentResourceKey(GetType(), "TileView"); }
    }

    protected override object ItemContainerDefaultStyleKey
    {
        get { return new ComponentResourceKey(GetType(), "TileViewItem"); }
    }
}


public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        myGrid.DataContext = this;

        InitOrder();
    }
    public ObservableCollection<Order> Orders { get; set; } = new();
    public void InitOrder()
    {
        Order order1 = new Order();
        Order order2 = new Order();
        Order order3 = new Order();
        Order order4 = new Order();

        order1.Price = 100;
        order1.Volume = 10;
        order1.Image = "image1.gif";

        order2.Price = 1000;
        order2.Volume = 100;
        order2.Image = "image2.gif";

        order3.Price = 10000;
        order3.Volume = 1000;
        order3.Image = "image3.gif";

        order4.Price = 100000;
        order4.Volume = 10000;
        order4.Image = "image4.gif";

        Orders.Add(order1);
        Orders.Add(order2);
        Orders.Add(order3);
        Orders.Add(order4);
    }

    private void ComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        ComboBoxItem selectedItem = (ComboBoxItem)((ComboBox)sender).SelectedItem;
        customListView.View = (ViewBase)this.FindResource(selectedItem.Content);
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值