首页 > 代码库 > [Aaronyang] 写给自己的WPF4.5 笔记9[复杂数据处理三步曲,数据展示ListView泪奔2/3]

[Aaronyang] 写给自己的WPF4.5 笔记9[复杂数据处理三步曲,数据展示ListView泪奔2/3]

 我的文章一定要做到对读者负责,否则就是失败的文章  ---------   www.ayjs.net    aaronyang技术分享

作者留言:

       小小的推荐,作者的肯定,读者的支持。推不推荐不重要,重要的是希望大家能把WPF推广出去,别让这么好的技术消失了,求求了,让我们为WPF技术做一份贡献。其实写这篇文章时候已经哭了,最近几篇文章,在我个人看来都是wpf的宝藏文章。每天读者也就200-400多人,也说明了WPF的人越来也少了。但是我的Blend教程和WPF控件开发,3D WPF的思路教程还是会慢慢出来的。学习最好的工具,无需买书:MSDN

 

博文摘要:

  1. 先深入浅出让你了解ListView控件
  2. ListView控件View的了解,已经Header模板和cell的模板的简单深入
  3. 深入浅出ComponentResourceKey,TextBlock的多值绑定时候,换行的解决
  4. 了解View后,我们知道了ListView.View原来是ViewBase,我们成功地自定义一个View,我的电脑,资源视图方式展示
  5. 完整效果图展示:
  6. 技术分享
  7. demo下载:百度云下载,里面有本次图标的素材

 

 

1.我们接着上一课的项目,继续写。新建窗体Window2.xaml

基本布局,跟上节课一样,窗口被平均分为4份:

<Window x:Class="TemplateDemo.Window2"        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"        Title="Window2" Height="600" Width="1000">    <UniformGrid Columns="2" Rows="2">        <DockPanel Grid.Column="0" Grid.Row="0" Background="#FFF7F6F6" LastChildFill="True" x:Name="demo1">            <Grid DockPanel.Dock="Top" Background="#0786F6" Height="28">                <TextBlock VerticalAlignment="Center" HorizontalAlignment="Center" Foreground="#fff">DEMO1    ListView数据展示</TextBlock>            </Grid>            <Grid>                <ListView x:Name="lvDemo1">                                    </ListView>            </Grid>        </DockPanel>    </UniformGrid></Window>

1. 创建一个假数据源

using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;using System.Windows;using System.Windows.Controls;using System.Windows.Data;using System.Windows.Documents;using System.Windows.Input;using System.Windows.Media;using System.Windows.Media.Imaging;using System.Windows.Shapes;namespace TemplateDemo{    /// <summary>    /// Window2.xaml 的交互逻辑    /// </summary>    public partial class Window2 : Window    {        public Window2()        {            InitializeComponent();        }        private void Window_Loaded(object sender, RoutedEventArgs e)        {            //DEMO1            DateTime dt = DateTime.Now.AddMonths(-13);            Random monthRandom = new Random();            Random dayRandom = new Random();            Random minRandom = new Random();            BlogCollectionLive bl = new BlogCollectionLive();            for (int i = 1; i <= 100; i++)            {                AyBlogLive ay = new AyBlogLive();                ay.Name = "Ay的WPF博客" + i;                ay.Content = "Ay的WPF是window上最好的桌面技术,真的很好" + i;                ay.CreateTime = dt.AddMonths(monthRandom.Next(1, 12)).AddDays(dayRandom.Next(-30, 60)).AddMinutes(minRandom.Next(-60, 120));                bl.Add(ay);            }
  lvDemo1.ItemsSource = bl; } }}

接下来,我们指定ListView.View    这里我们使用GridView,如果学过 asp.net就好理解了。标题列名Header属性,DisplayMemberBinding前面见过了,绑定对象中的属性

  <ListView x:Name="lvDemo1">                    <ListView.View>                        <GridView>                            <GridView.Columns>                                <GridViewColumn Header="文章名" DisplayMemberBinding="{Binding Name}"></GridViewColumn>                                <GridViewColumn Header="发布日期" DisplayMemberBinding="{Binding CreateTime,StringFormat={}{0:yyyy-MM-dd HH:mm:ss}}"></GridViewColumn>                            </GridView.Columns>                        </GridView>                    </ListView.View>                </ListView>

效果图:这里没有指定列宽,你可以自己指定。wpf会适应最大的宽度去调节

技术分享

指定宽度:

   <GridViewColumn Header="文章名" DisplayMemberBinding="{Binding Name}" Width="315"></GridViewColumn>                                <GridViewColumn Header="发布日期" DisplayMemberBinding="{Binding CreateTime,StringFormat={}{0:yyyy-MM-dd HH:mm:ss}}" Width="150"></GridViewColumn>

效果图:GridView默认列可以拖拽,列宽可以调节,双击列的右侧边缘,可以让列自动调整到可以看见内容的宽度。GridView没有类似MaxWidth或者MinWidth的属性

技术分享

指定列的单元格模板,在列中指定列,这列数据每个单元格怎么显示

  <ListView.View>                        <GridView>                            <GridView.Columns>                                <!--<GridViewColumn Header="文章名" DisplayMemberBinding="{Binding Name}" Width="315"></GridViewColumn>-->                                <GridViewColumn Header="文章名" Width="315">                                    <GridViewColumn.CellTemplate>                                        <DataTemplate>                                            <StackPanel Orientation="Horizontal">                                                <TextBlock>[<Hyperlink NavigateUri="http://www.ayjs.net">下载</Hyperlink>]</TextBlock>                                                <TextBlock Text="{Binding Path=Name}" Margin="1,0,0,0" Foreground="CadetBlue"></TextBlock>                                            </StackPanel>                                                        </DataTemplate>                                    </GridViewColumn.CellTemplate>                                </GridViewColumn>                                <GridViewColumn Header="发布日期" DisplayMemberBinding="{Binding CreateTime,StringFormat={}{0:yyyy-MM-dd HH:mm:ss}}" Width="150"></GridViewColumn>                            </GridView.Columns>                        </GridView>                    </ListView.View>

效果图:当然这只是简单的例子,比如这里我们可以显示图片,使用图片地址换器,单元格就可以显示一张图片了。

技术分享

同样地,列标题Header也是有模板的,GridViewColumn.HeaderTemplate,同样我们也发现了HeaderTemplateSelector,所以每个列可以展示的不一样

技术分享

  <GridViewColumn.HeaderTemplate>                                        <DataTemplate>                                            <Border Width="312" CornerRadius="5" BorderBrush="Yellow" BorderThickness="1" Background="YellowGreen">                                                <Grid>                                                    <Rectangle HorizontalAlignment="Stretch">                                                    </Rectangle>                                                    <TextBlock Text="文章标题" Foreground="White" HorizontalAlignment="Center"></TextBlock>                                                </Grid>                                            </Border>                                        </DataTemplate>                                    </GridViewColumn.HeaderTemplate>

效果预览:

技术分享

慢着,别以为就这样结束了,在写代码的过程中,我还发现了,单元格模板选择器,所以每格的显示效果可以不一样的

技术分享

下面有个地址转换成Image控件的Source的转换器,自己写的以前的转换器学习博文:查看

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

我还发现树状数据显示模板,感兴趣的自己可以去拓展

技术分享

同样的模板,我们也可以在GridView下面一级定义

技术分享

当让ColumnHeaderContainerStyle用于全局修改每一个列的头部样式,GridViewColumn中的是单独修改。同样也有全局模板选择器

 

  =============潇洒的版权线==========www.ayjs.net===== Aaronyang ========= AY =========== 安徽 六安 杨洋 ==========   未经允许不许转载 ========= 

 

 

在做第2个demo时候,有必要介绍下ComponentResourceKey,主要为从外部程序集加载的资源定义和引用键。这使得资源查找功能可以在程序集内指定目标类型,而不是在程序集内指定显式的资源字典。主要应用:定义共享的资源在一个程序集里,使用ComponentResourceKey,可以找到他,覆盖样式。详细请参考:查看

 MSDN地址说明查看

XAML 属性用法(设置键,精简版)
<object x:Key="{ComponentResourceKey {x:Type targetTypeName}, targetID}" .../>
XAML 属性用法(设置键,详细版)
<object x:Key="{ComponentResourceKey TypeInTargetAssembly={x:Type targetTypeName}, ResourceID=targetID}" .../>
XAML 属性用法(请求资源,精简版)
<object property="{DynamicResource {ComponentResourceKey {x:Type targetTypeName}, targetID}}" .../>
XAML 属性用法(请求资源,详细版)
<object property="{DynamicResource {ComponentResourceKey TypeInTargetAssembly={x:Type targetTypeName}, ResourceID=targetID}}" .../>

 

ay总结:一句话,控件提供者,给用户提供了 约定资源,相当于资源接口,你可以通过ComponentResourceKey找到该位置,并填写它,覆盖它。如果控件提供者给了你控件 约定资源 Key的方法,或者重写Key名字的方法,那就更好了。


 

DEMO2   简单的模拟我的电脑的 盘符显示方式

   <DockPanel Grid.Column="0" Grid.Row="0" Background="#FFF7F6F6" LastChildFill="True" x:Name="demo2">            <Grid DockPanel.Dock="Top" Background="YellowGreen" Height="28">                <TextBlock VerticalAlignment="Center" HorizontalAlignment="Center">DEMO2 ListView自定义视图</TextBlock>            </Grid>            <Grid DockPanel.Dock="Bottom" Height="28"  HorizontalAlignment="Stretch"  Background="YellowGreen" >                <StackPanel Orientation="Horizontal" HorizontalAlignment="Center" >                    <Button Width="60" Margin="2" x:Name="btnDetail" Click="">详细信息</Button>                    <Button Width="60" Margin="2" x:Name="btnContent" Click="">内容</Button>                    <Button Width="60" Margin="2" x:Name="btnNext" Click="">平铺</Button>                    <Button Width="60" Margin="2" x:Name="btnSmallImage" Click="">小图标</Button>                    <Button Width="60" Margin="2" x:Name="btnNormalImage" Click="">中图标</Button>                </StackPanel>            </Grid>            <Grid>                <ListView Name="lstAyDisk">                </ListView>            </Grid>        </DockPanel>

我们先完成数据源,WPF提供的GridView的展示方式,这里的切换放到 详细信息 按钮的 事件里

       //demo2  创建数据源            ObservableCollection<DiskInfo> disk = new ObservableCollection<DiskInfo> {                 new DiskInfo{DiskChar=C,DiskIcon="1.png",DiskType="NTFS",AllMemory=50,UsedMemory=39,DiskName="本地磁盘"},                new DiskInfo{DiskChar=D,DiskIcon="2.png",DiskType="NTFS",AllMemory=120,UsedMemory=21,DiskName="本地磁盘"},                new DiskInfo{DiskChar=E,DiskIcon="3.png",DiskType="NTFS",AllMemory=20,UsedMemory=10,DiskName="软件"},                new DiskInfo{DiskChar=F,DiskIcon="4.png",DiskType="NTFS",AllMemory=250,UsedMemory=212.5,DiskName="游戏"},                new DiskInfo{DiskChar=G,DiskIcon="5.png",DiskType="NTFS",AllMemory=100,UsedMemory=40,DiskName="资料备份"}            };            lstAyDisk.ItemsSource = disk;                                }        private void btnDetail_Click(object sender, RoutedEventArgs e)        {        }        private void btnContent_Click(object sender, RoutedEventArgs e)        {        }        private void btnTile_Click(object sender, RoutedEventArgs e)        {        }        private void btnSmallImage_Click(object sender, RoutedEventArgs e)        {        }        private void btnNormalImage_Click(object sender, RoutedEventArgs e)        {        }    }    public class DiskInfo    {        public char DiskChar { get; set; }        public string DiskName { get; set; }        public string DiskType { get; set; }        public double UsedMemory { get; set; }        public double AllMemory { get; set; }        public string DiskIcon { get; set; }    }

接下来,我们把listview的view全部写到Window.Resources里面,我们先在项目里面引入几张测试图片,我的文件夹叫imageDisk,先写wpf自带的gridView

技术分享

列表要显示图片需要图片地址变成图片文件流,就需要转换器

using System;using System.Collections.Generic;using System.Text;using System.Windows.Data;using System.Windows.Media.Imaging;using System.IO;namespace TemplateDemo{    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 p = parameter as string;            string imagePath = "";            if (p != null)            {                imagePath = Path.Combine(ImageDirectory, p+(string)value);            }            else {                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("等待实现..");        }    }}

接下来,windows.Resources先完成最基本的GridView的View,因为ListView要显示东西只认View,这个View要继承ViewBase类,所以过会我们自己定义个ViewBase的子类

 <Window.Resources>        <local:ImagePathConverter x:Key="ImagePathConverter"></local:ImagePathConverter>        <GridView x:Key="GridView">            <GridView.Columns>                <GridViewColumn Header="名称">                    <GridViewColumn.CellTemplate>                        <DataTemplate>                            <StackPanel Orientation="Horizontal">                                <Viewbox Width="20">                                    <Image Source="{Binding DiskIcon,Converter={StaticResource ImagePathConverter},ConverterParameter=‘imageDisk/‘}"/>                                </Viewbox>                                <TextBlock Text="{Binding Path=DiskName}" Margin="1,0,0,0" Foreground="Black"></TextBlock>                                <TextBlock Text="{Binding Path=DiskChar,StringFormat=‘({0}:)‘}" Margin="1,0,0,0" Foreground="Black"></TextBlock>                            </StackPanel>                        </DataTemplate>                    </GridViewColumn.CellTemplate>                </GridViewColumn>                <GridViewColumn Header="类型" DisplayMemberBinding="{Binding Path=DiskType}" />                <GridViewColumn Header="总大小" DisplayMemberBinding="{Binding Path=AllMemory, StringFormat={}{0} GB}" />                <GridViewColumn Header="已用空间" DisplayMemberBinding="{Binding Path=UsedMemory, StringFormat={}{0} GB}" />            </GridView.Columns>        </GridView>    </Window.Resources>   

指定ListView的默认View

    <ListView x:Name="lstAyDisk" View="{StaticResource GridView}">    </ListView>

运行后,效果图

技术分享

接下来,我们自定义个继承ViewBase的AView,表示笑了

using System;using System.Collections.Generic;using System.Text;using System.Windows;using System.Windows.Controls;namespace TemplateDemo{    public class AView:ViewBase    {        private DataTemplate itemTemplate;        public DataTemplate ItemTemplate        {            get { return itemTemplate; }            set { itemTemplate = value; }        }            protected override object DefaultStyleKey        {            get { return new ComponentResourceKey(GetType(), "AView"); }        }        protected override object ItemContainerDefaultStyleKey        {            get { return new ComponentResourceKey(GetType(), "AViewItem"); }        }           }}

我们公开了一个DataTemplate方便使用者定义Item的样子,并准备重写ViewBase控件的DefaultStyleKey和ItemContainerDefaultStyleKey

ComponentResourceKey封装了拥有样式的类的类型,以及一个资源标识ResourceId。通过上一篇三巴掌系列的学习,我们知道了ItemContainerStyle是控制例如ListBoxItem,容器中Item的样式的。默认的Style控制整个的Style的。

技术分享

这里我们已经自己定义了2个样式资源名字,AView还有AViewItem

接下来,我们使用ComponentResourceKey找到这两个资源,并填充内容。

因为我们重写ViewBase的样式,ViewBase属于外部程序集,为了确保WPF能找到希望使用的样式,确保能够自动获得样式,我们使用ComponentResourceKey

我们新建Themes,然后新建generic.xaml文件,添加下面的样式,在AView.cs中查找ResourceId为AView的,即DefaultStyleKey对象,使用TargetType指定这个样式使用者是ListView,继承ListBox的样式

           xmlns:local="clr-namespace:TemplateDemo"
    <Style x:Key="{ComponentResourceKey TypeInTargetAssembly={x:Type local:AView}, ResourceId=AView}" TargetType="{x:Type ListView}" BasedOn="{StaticResource {x:Type ListBox}}">        <Setter Property="BorderBrush" Value="Gold"></Setter>        <Setter Property="BorderThickness" Value="5"></Setter>    </Style>

接下来我们在界面上创建一个空的AView,并指定ListView的View指定AView

   <local:AView x:Key="ImageNormalView">        </local:AView>
   <ListView x:Name="lstAyDisk" View="{StaticResource ImageNormalView}">                </ListView>

运行后效果图:由于没有指定模板,且样式继承了ListBox所以一列展示了,且基本的View的边框和颜色已经生效了。所以可以Style的作用范围是AViewItem外面一层的View

技术分享

接下来,我们自定义一个模板,让显示成ImageButton类型,上方图片,下方文字加盘符

 <local:AView x:Key="ImageNormalView">            <local:AView.ItemTemplate>                <DataTemplate>                    <StackPanel>                        <Viewbox Width="64" HorizontalAlignment="Center">                            <Image Source="{Binding DiskIcon,Converter={StaticResource ImagePathConverter},ConverterParameter=‘imageDisk/‘}"/>                        </Viewbox>                        <TextBlock HorizontalAlignment="Center"  Margin="1,0,0,0" Foreground="Black">                            <TextBlock.Text>                                <MultiBinding StringFormat="{}{0}({1}:)">                                    <Binding Path="DiskName"></Binding>                                    <Binding Path="DiskChar"></Binding>                                </MultiBinding>                            </TextBlock.Text>                        </TextBlock>                    </StackPanel>                </DataTemplate>            </local:AView.ItemTemplate>        </local:AView>

但是加上以后,运行后还是没有效果,而且还是Listbox的样式,我们上篇博客讲到了只要修改ItemPanel就可以改变布局了,我们试着在View的样式里指定ItemPanel

  <Style x:Key="{ComponentResourceKey TypeInTargetAssembly={x:Type local:AView}, ResourceId=AView}" TargetType="{x:Type ListView}" BasedOn="{StaticResource {x:Type ListBox}}">        <Setter Property="BorderBrush" Value="Gold"></Setter>        <Setter Property="BorderThickness" Value="5"></Setter>        <Setter Property="ItemsPanel">            <Setter.Value>                <ItemsPanelTemplate>                    <WrapPanel></WrapPanel>                </ItemsPanelTemplate>            </Setter.Value>        </Setter>    </Style>

但是上篇博客也说到了,例如Listbox如果横向的滚动条不禁止掉,wrap就不会换行了,我们给ListView增加ScrollViewer.HorizontalScrollBarVisibility="Disabled"

                <ListView x:Name="lstAyDisk" View="{StaticResource ImageNormalView}" ScrollViewer.HorizontalScrollBarVisibility="Disabled">

运行代码效果如下:虽然右侧还有点距离,但无妨。

技术分享

同样的效果,如果我们设置了WrapPanel的宽度,它也会自动换行的,Wrap的宽度等于父类的宽度,我们也可以这样设置,且使用者不需要加代码

   <WrapPanel Width="{Binding (FrameworkElement.ActualWidth), RelativeSource={RelativeSource   AncestorType=ScrollContentPresenter}}"></WrapPanel>

接下来,发现Item的还是显示对象的ToString()的数据,且DataTemplate也没生效,原因是我们没有指定使用者ListView.View.ItemTemplate

    <Style x:Key="{ComponentResourceKey TypeInTargetAssembly={x:Type local:AView},ResourceId=AViewItem}"         TargetType="{x:Type ListViewItem}" BasedOn="{StaticResource {x:Type ListBoxItem}}">        <Setter Property="ContentTemplate" Value="{Binding Path=View.ItemTemplate, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ListView}}}"></Setter>    </Style>

效果图:

技术分享

我们稍微调整边距和内容水平和垂直居中。

   <Style x:Key="{ComponentResourceKey TypeInTargetAssembly={x:Type local:AView},ResourceId=AViewItem}"         TargetType="{x:Type ListViewItem}" BasedOn="{StaticResource {x:Type ListBoxItem}}">        <Setter Property="ContentTemplate" Value="{Binding Path=View.ItemTemplate, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ListView}}}"></Setter>        <Setter Property="Padding" Value="5"/>        <Setter Property="Margin" Value="1.5"/>        <Setter Property="HorizontalContentAlignment" Value="Center"></Setter>        <Setter Property="VerticalContentAlignment" Value="Center"></Setter>    </Style>

技术分享

运行项目后,用户如果不会操作就会出现虚线框,所以我们需要定义Item的模板,我们在上面的样式上写。我打开曾经做过的一个模板预览窗口,查看了ListBoxItem的系统定义的样式,大致模仿思路改写下,建议你也可以在blend中去修改,修改好后就可以移到这边来了

技术分享

外方有个Border,然后中间放个内容显示控件,代表DataTemplate显示的内容。接着一些触发器触发各个状态的效果。

    <Style x:Key="{ComponentResourceKey TypeInTargetAssembly={x:Type local:AView},ResourceId=AViewItem}"         TargetType="{x:Type ListViewItem}" BasedOn="{StaticResource {x:Type ListBoxItem}}">        <Setter Property="ContentTemplate" Value="{Binding Path=View.ItemTemplate, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ListView}}}"></Setter>        <Setter Property="Padding" Value="5"/>        <Setter Property="Margin" Value="1.5"/>        <Setter Property="HorizontalContentAlignment" Value="Center"></Setter>        <Setter Property="VerticalContentAlignment" Value="Center"></Setter>        <Setter Property="Template">            <Setter.Value>                <ControlTemplate TargetType="{x:Type ListBoxItem}">                                    </ControlTemplate>            </Setter.Value>        </Setter>    </Style>

此时运行项目时候,界面已经没有任何东西了,因为内容控件没有增加,如果加上 <ContentPresenter /> 运行项目时候,已经出现了,但是所有的动作效果没了,这就要求我们写效果了。

  <Setter Property="Template">            <Setter.Value>                <ControlTemplate TargetType="{x:Type ListBoxItem}">                    <ContentPresenter />                </ControlTemplate>            </Setter.Value>        </Setter>

我们还是用自带的

  <Border x:Name="Bd" BorderThickness="{TemplateBinding Border.BorderThickness}" Padding="{TemplateBinding Control.Padding}" BorderBrush="{TemplateBinding Border.BorderBrush}" Background="{TemplateBinding Panel.Background}"  SnapsToDevicePixels="True">                        <ContentPresenter  Content="{TemplateBinding ContentControl.Content}" ContentTemplate="{TemplateBinding ContentControl.ContentTemplate}" ContentStringFormat="{TemplateBinding ContentControl.ContentStringFormat}" HorizontalAlignment="{TemplateBinding Control.HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding Control.VerticalContentAlignment}" SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}" />                    </Border>

打开blend,我自己随便拖了个Listbox瞎添加了几个ListBoxItem,然后编辑模板,将Tab切换到触发器

技术分享

正如上面定义的一样,Item具有4个状态,禁用,鼠标移上,激活时候选中,非激活时候选中。当然我们不需要这么复杂,简单几个就够了,我自己参考模板去写的

    <Style x:Key="{ComponentResourceKey TypeInTargetAssembly={x:Type local:AView},ResourceId=AViewItem}"         TargetType="{x:Type ListViewItem}" BasedOn="{StaticResource {x:Type ListBoxItem}}">        <Setter Property="ContentTemplate" Value="{Binding Path=View.ItemTemplate, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ListView}}}"></Setter>        <Setter Property="Padding" Value="5"/>        <Setter Property="Margin" Value="1.5"/>        <Setter Property="HorizontalContentAlignment" Value="Center"></Setter>        <Setter Property="VerticalContentAlignment" Value="Center"></Setter>        <Setter Property="Template">            <Setter.Value>                <ControlTemplate TargetType="{x:Type ListBoxItem}">                    <Border x:Name="Bd" BorderThickness="{TemplateBinding Border.BorderThickness}" Padding="{TemplateBinding Control.Padding}" BorderBrush="{TemplateBinding Border.BorderBrush}" Background="{TemplateBinding Panel.Background}"  SnapsToDevicePixels="True">                        <ContentPresenter  Content="{TemplateBinding ContentControl.Content}" ContentTemplate="{TemplateBinding ContentControl.ContentTemplate}" ContentStringFormat="{TemplateBinding ContentControl.ContentStringFormat}" HorizontalAlignment="{TemplateBinding Control.HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding Control.VerticalContentAlignment}" SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}" />                    </Border>                    <ControlTemplate.Triggers>                        <Trigger Property="IsSelected" Value="True">                            <Setter TargetName="Bd" Property="BorderBrush" Value="#66A7E8"></Setter>                            <Setter TargetName="Bd" Property="Background" Value="#D1E8FF"></Setter>                        </Trigger>                        <MultiTrigger>                            <MultiTrigger.Conditions>                                <Condition Property="UIElement.IsMouseOver" Value="True"></Condition>                                <Condition Property="IsSelected" Value="False"></Condition>                            </MultiTrigger.Conditions>                            <Setter TargetName="Bd" Property="BorderBrush" Value="#70C0E7"></Setter>                            <Setter TargetName="Bd" Property="Background" Value="#E5F3FB"></Setter>                        </MultiTrigger>                        <MultiTrigger>                            <MultiTrigger.Conditions>                                <Condition Property="Selector.IsSelectionActive" Value="False">                                </Condition>                                <Condition Property="Selector.IsSelected" Value="True"> </Condition>                            </MultiTrigger.Conditions>                            <Setter TargetName="Bd" Property="BorderBrush" Value="#3399FF"></Setter>                            <Setter TargetName="Bd" Property="Background" Value="#fff"></Setter>                        </MultiTrigger>                    </ControlTemplate.Triggers>                </ControlTemplate>            </Setter.Value>        </Setter>    </Style>

技术分享

去掉focus框

<Setter Property="FocusVisualStyle" Value="{x:Null}"/>

接下来,在DiskInfo类,增加可用空间字段

        private double canUseMemory;        public double CanUseMemory        {            get            {                return AllMemory - UsedMemory;            }            set { canUseMemory = value; }        }

接下来,增加悬浮提示:但是遇到了个TextBlock的Text多binding时候,StringFormat如何换行

方法一:&#x0a;

方法二:

<TextBlock Text="{Binding StringFormat=‘第一行{0}第二行{0}第三行‘, Source={x:Static s:Environment.NewLine}}" />

修改后简单效果图:

技术分享

接下来,我们要写平铺的排版,触发器都是共用的,我们只要稍微改下模板就OK了。关于下面的样式,我优先推荐使用Dock布局,在WPF开发中,布局是最基本的能力,你要能最快速度直到怎样搭建界面

        <local:AView x:Key="ImageTileView">            <local:AView.ItemTemplate>                <DataTemplate>                    <DockPanel LastChildFill="True">                        <DockPanel.ToolTip>                            <ToolTip Placement="Mouse">                                <StackPanel>                                    <TextBlock HorizontalAlignment="Center"  Margin="1,0,0,0" Foreground="Black">                                        <TextBlock.Text>                                            <MultiBinding StringFormat="可用空间: {0} GB&#x0a;总大小: {1} GB ">                                                <Binding Path="CanUseMemory"></Binding>                                                <Binding Path="AllMemory"></Binding>                                            </MultiBinding>                                        </TextBlock.Text>                                    </TextBlock>                                </StackPanel>                            </ToolTip>                        </DockPanel.ToolTip>                        <Grid  DockPanel.Dock="Left" >                            <Grid.ColumnDefinitions>                                <ColumnDefinition Width="50"/>                                <ColumnDefinition Width="*"/>                            </Grid.ColumnDefinitions>                            <Image  Source="{Binding DiskIcon,Converter={StaticResource ImagePathConverter},ConverterParameter=‘imageDisk/‘}" Grid.Column="0" Width="40"/>                            <StackPanel Grid.Column="1">                                <TextBlock  Margin="1,0,0,0" Foreground="Black">                                    <TextBlock.Text>                                        <MultiBinding StringFormat="{}{0}({1}:)">                                            <Binding Path="DiskName"></Binding>                                            <Binding Path="DiskChar"></Binding>                                        </MultiBinding>                                    </TextBlock.Text>                                </TextBlock>                                <ProgressBar Value="{Binding CanUseMemory}" Maximum="{Binding AllMemory}" Minimum="0" Height="13" Width="150" BorderBrush="#BCBCBC" Background="#E6E6E6" BorderThickness="1" Foreground="#26A0DA"></ProgressBar>                                <TextBlock  Margin="1,0,0,0" Foreground="Black">                                    <TextBlock.Text>                                        <MultiBinding StringFormat="{}{0} GB可用,共 {1} GB">                                            <Binding Path="CanUseMemory"></Binding>                                            <Binding Path="AllMemory"></Binding>                                        </MultiBinding>                                    </TextBlock.Text>                                </TextBlock>                            </StackPanel>                        </Grid>                    </DockPanel>                </DataTemplate>            </local:AView.ItemTemplate>        </local:AView>

我们指定这个ListView的View的模板,效果图如下;

技术分享

接下来,我们要做的是内容方式展示的View的数据模板。打开我的电脑,它的进度条是根据窗体变的,说白了,第一眼就想到了百分比布局的Grid

   <local:AView x:Key="ImageContentView">            <local:AView.ItemTemplate>                <DataTemplate>                    <DockPanel LastChildFill="True" Grid.Column="0"  Width="{Binding (FrameworkElement.ActualWidth), RelativeSource={RelativeSource   AncestorType=ScrollContentPresenter}}">                        <Grid  DockPanel.Dock="Left" >                            <Grid.ColumnDefinitions>                                <ColumnDefinition Width="40"/>                                <ColumnDefinition x:Name="colPro" Width="1.5*"/>                                <ColumnDefinition Width="1*"/>                            </Grid.ColumnDefinitions>                            <Image  Source="{Binding DiskIcon,Converter={StaticResource ImagePathConverter},ConverterParameter=‘imageDisk/‘}" Grid.Column="0" Width="30"  Margin="1,0,5,0"/>                            <StackPanel Grid.Column="1" >                                <TextBlock  Foreground="Black"  Margin="0,0,0,5">                                    <TextBlock.Text>                                        <MultiBinding StringFormat="{}{0}({1}:)">                                            <Binding Path="DiskName"></Binding>                                            <Binding Path="DiskChar"></Binding>                                        </MultiBinding>                                    </TextBlock.Text>                                </TextBlock>                                <ProgressBar Value="{Binding CanUseMemory}" Maximum="{Binding AllMemory}" Minimum="0" Height="13" Width="{Binding ElementName=colPro,Path=Width}"  BorderBrush="#BCBCBC" Background="#E6E6E6" BorderThickness="1" Foreground="#26A0DA"></ProgressBar>                            </StackPanel>                            <StackPanel Grid.Column="2"  Margin="10,0,0,0">                                <TextBlock  Foreground="Black" Text="{Binding DiskType}" Margin="0,0,0,5"/>                                <TextBlock  Foreground="Black">                                    <TextBlock.Text>                                        <MultiBinding StringFormat="{}{0} GB可用,共 {1} GB">                                            <Binding Path="CanUseMemory"></Binding>                                            <Binding Path="AllMemory"></Binding>                                        </MultiBinding>                                    </TextBlock.Text>                                </TextBlock>                            </StackPanel>                        </Grid>                    </DockPanel>                </DataTemplate>            </local:AView.ItemTemplate>        </local:AView>

效果图:

技术分享将窗体拉宽后,知识中间变了

技术分享

接下来,实现超大图标布局,这里我把小图标的版本按钮换成做大图标了,事件名称也改了VeryBig

 <local:AView x:Key="ImageVeryBigView">            <local:AView.ItemTemplate>                <DataTemplate>                    <StackPanel>                        <StackPanel.ToolTip>                            <StackPanel>                                <TextBlock HorizontalAlignment="Center"  Margin="1,0,0,0" Foreground="Black">                                    <TextBlock.Text>                                        <MultiBinding StringFormat="可用空间: {0} GB&#x0a;总大小: {1} GB ">                                            <Binding Path="CanUseMemory"></Binding>                                            <Binding Path="AllMemory"></Binding>                                        </MultiBinding>                                    </TextBlock.Text>                                </TextBlock>                            </StackPanel>                        </StackPanel.ToolTip>                        <Viewbox Width="200" HorizontalAlignment="Center">                            <Image Source="{Binding DiskIcon,Converter={StaticResource ImagePathConverter},ConverterParameter=‘imageDisk/‘}"/>                        </Viewbox>                        <TextBlock HorizontalAlignment="Center"  Margin="1,0,0,0" Foreground="Black">                            <TextBlock.Text>                                <MultiBinding StringFormat="{}{0}({1}:)">                                    <Binding Path="DiskName"></Binding>                                    <Binding Path="DiskChar"></Binding>                                </MultiBinding>                            </TextBlock.Text>                        </TextBlock>                    </StackPanel>                </DataTemplate>            </local:AView.ItemTemplate>        </local:AView>

效果图,这是窗口最大化截图的:

技术分享

最后,我们补充按钮的事件,切换就OK了

 private void btnDetail_Click(object sender, RoutedEventArgs e)        {            lstAyDisk.View = (ViewBase)this.FindResource("GridView");        }        private void btnContent_Click(object sender, RoutedEventArgs e)        {            lstAyDisk.View = (ViewBase)this.FindResource("ImageContentView");        }        private void btnTile_Click(object sender, RoutedEventArgs e)        {            lstAyDisk.View = (ViewBase)this.FindResource("ImageTileView");        }        private void btnNormalImage_Click(object sender, RoutedEventArgs e)        {            lstAyDisk.View = (ViewBase)this.FindResource("ImageNormalView");        }        private void btnVeryBigImage_Click(object sender, RoutedEventArgs e)        {            lstAyDisk.View = (ViewBase)this.FindResource("ImageVeryBigView");        }

快来看看效果图:

技术分享

 

  =============潇洒的版权线==========www.ayjs.net===== Aaronyang ========= AY =========== 安徽 六安 杨洋 ==========   未经允许不许转载 =========

小小的推荐,作者的肯定,读者的支持。推不推荐不重要,重要的是希望大家能把WPF推广出去,别让这么好的技术消失了,求求了,让我们为WPF技术做一份贡献。

 

[Aaronyang] 写给自己的WPF4.5 笔记9[复杂数据处理三步曲,数据展示ListView泪奔2/3]