[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. 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如何换行

方法一:

方法二:

<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
总大小: {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
总大小: {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技术做一份贡献。

时间: 2024-10-19 17:34:13

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

[Aaronyang] 写给自己的WPF4.5 笔记8[复杂数据处理三步曲,数据视图精讲1/3]

真的好累了 ,笑了.做回自己吧       -------------      Aaronyang技术分享 www.ayjs.net 博文摘要: 详细介绍了WPF中视图的种类和开始学之前的准备工作 视图的 分页视图导航 DEMO1 详细讲解了 视图中xaml的声明方式,以及xaml的排序和分组 DEMO2 实例讲解了DataTable的BindingListCollectionView的类似操作 DEMO3 讲解了LINQ中的过滤 Predicate委托,以及过滤的几种方式 讲解了 视图中后台

[Aaronyang] 写给自己的WPF4.5 笔记11[自定义控件-AyImageButton篇 1/4]

我的文章一定要对读者负责-否则不是好文章  ----       www.ayjs.net  aaronyang技术分享 文章导航: 介绍vs2013 WPF开发,属性代码相关技巧 实战AyImageButton 1.0细用慢讲,学会用户控件,依赖属性,属性回调事件 诞生AyImageButton 1.1 支持 控件简单写法,支持自定义AyImageButton写法,提供详细的API 效果图: AyImageButton记录 源码下载:http://pan.baidu.com/s/1eQlHly

[Aaronyang] 写给自己的WPF4.5 笔记13[二维自定义控件技巧-可视化状态实战,自定义容器,注册类命令,用户控件补充]

 我的文章一定要做到对读者负责,否则就是失败的文章  ---------   www.ayjs.net    aaronyang技术分享 博文摘要:欢迎大家来支持我的<2013-2015 Aaronyang的又一总结,牧童遥指纳尼村>绝对好文章 关于<写给自己的WPF4.5 笔记14,已在官网发布> 1.讲解了自定义控件加入命令支持的两种手段,补充用户控件的客户定义模板 2.实战的方式讲解了无外观控件,可以让使用者定义模板,讲解模板PART,使用可视化状态组,动画的使用 效果演示:

[Aaronyang] 写给自己的WPF4.5 笔记[2依赖属性]

人生的意义不在于拿一手好牌,而在于打好一手坏牌 --Aaronyang的博客(www.ayjs.net)-www.8mi.me =============时隔两年后再看WPF========== 因为以前的经验,所以继承FrameworkElement,我就简写继承FWE ,继承UIElement就写继承UIE 后面重头戏就是blend中的开发,不想写的千篇一律.如果期待,左侧有关注按钮. 个人感觉,下面的这张图标比较重要,它或许有些帮助.我看东西只看分析出原理,你就可以拓三返一. Tip: 只

[Aaronyang] 写给自己的WPF4.5 笔记[1布局]

挫折时,要像大树一样,被砍了,还能再长:也要像杂草一样,虽让人践踏,但还能勇敢地活下去 --Aaronyang的博客(www.ayjs.net)-www.8mi.me =============时隔两年后再看WPF========== 因为以前的经验,所以继承FrameworkElement,我就简写继承FWE ,继承UIElement就写继承UIE 复习曲线记录-布局 1. 了解Panel,继承Panel的WPF提供的一些核心布局控件 StackPanel.WrapPanel.DockPane

[Aaronyang] 写给自己的WPF4.5 笔记12[自定义控件-AyImageButton的过程 2/4]

 我的文章一定要做到对读者负责,否则就是失败的文章  ---------   www.ayjs.net    aaronyang技术分享 博文摘要:点击前往文章正文 学会怎样给用户提供事件接口,本例子中通过监视Dock的变化事件,让用户可以在dock变化时候,自定义下一步操作 学会如何使用C# 符合枚举,以及如何在Xaml中使用 实战AyImageButton1.2的诞生,增加纯图标或者纯文本,是否开启提示 版本 难DEMO:在第三步的基础上,增加 Dock变化和RenderMode变化的混搭

[Aaronyang] 写给自己的WPF4.5 笔记6[三巴掌-大数据加载与WPF4.5 验证体系详解 2/3]

我要做回自己--Aaronyang的博客(www.ayjs.net) 博客摘要: Virtualizing虚拟化DEMO 和 大数据加载的思路及相关知识 WPF数据提供者的使用ObjectDataProvider 和 XmlDataProvider WPF验证 第一:使用自带的属性SET抛出异常,前台捕捉到异常,描红 第二:我们可以自定义验证规则,替代刚开始的异常捕捉验证 第三:我们可以使用INotifyDataErrorInfo方式,增加异常,并实现了验证通知和还原非法值 第四:我们使用了Er

[Aaronyang] 写给自己的WPF4.5 笔记[3MenuItem中的icon]

敢于尝试,就等于你已经向成功迈出了第一步 --Aaronyang的博客(www.ayjs.net)-www.8mi.me =============时隔两年后再看WPF========== 因为以前的经验,所以继承FrameworkElement,我就简写继承FWE ,继承UIElement就写继承UIE 后面重头戏就是blend中的开发,不想写的千篇一律.如果期待,左侧有关注按钮. 1. v1.0菜单 新建WPF项目,右键项目-属性-资源          导入几个项目用到的图标 第一种:带图

[Aaronyang] 写给自己的WPF4.5 笔记19[Visual类图文并茂讲解]

文章虽小,内容还好,且看且珍惜. 当界面上使用数千个矢量图形,例如实时统计图,粒子碰撞,比如超级玛丽游戏,图像一直在绘,过量的使用WPF的元素系统和Shape类会使用程序变慢,所以我们需要使用Visual类手动进行渲染. Visual类是很多WPF元素的父类.所以掌握它当然很重要了. Visual的开销小于Geometry小于Path Visual作为抽象类,有UIElement这个子类,也有Viewport3DVisual类(3D知识中的) 放置Visual的对象的容器:ContainerVi