[Aaronyang] 写给自己的WPF4.5 笔记7[三巴掌-ItemsControl数据绑定详解与binding二次处理 3/3]

我要做回自己--Aaronyang的博客(www.ayjs.net)

博客摘要:

  1. 全方位的讲解了转换器的使用,单值,多值转换器,条件转换器,StringFormat等方式
  2. 详细的实践地讲解了ItemsControl中的知识

    一:ItemsSource,DisplayMemberPath,ItemStringFormat,ItemContainerStyle

    二:ItemContainerStyle下修改显示模板

    三:AlternationCount例讲

    四:StyleSelector样式选择器

    五:DataTemplateSelector模板选择器,以及数据模板深入

    六:ItemsPanel

    七:细节分析

  3. ComboBox的简单原理套用与讲解,以及自动搜索TextSearch.TextPath等

 


 

新建一个WPF项目TemplateDemo

1. binding的数据二次处理方式

  1.1 自带StringFormat,只简单列举几个

    写DEMO创建窗口时候,默认Grid,改成Canvas做演示最好了

<Window x:Class="TemplateDemo.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:TemplateDemo"
        Title="MainWindow" Height="700" Width="1200">
    <Window.Resources>
        <ObjectDataProvider x:Key="nowTime" MethodName="GetDate" IsAsynchronous="True" ObjectType="{x:Type local:DataDemo}"/>
    </Window.Resources>
    
    <Canvas>
        <Label>时间:{}{0:时间格式}</Label>
        <TextBox Height="23" Canvas.Left="137" TextWrapping="Wrap" Text="{Binding Source={StaticResource nowTime},Path=MyDateTime,StringFormat={}{0:yyyy年MM月dd日}}" Canvas.Top="4" Width="120"/>

    </Canvas>
</Window>

后台,本课最基本的样子了:

 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.Navigation;
using System.Windows.Shapes;

namespace TemplateDemo
{
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }


    }

    public class DataDemo {

        public DataObject GetDate()
        {
            DataObject d = new DataObject();
            d.MyDateTime=DateTime.Now;
            return d;
        }
    }
    public class DataObject {
        public DateTime MyDateTime { get; set; }
    }
}

运行效果:文本框显示当前日期  2015年1月27日

  1.2 金额

   public class DataDemo {

        public DataObject GetDate()
        {
            DataObject d = new DataObject();
            d.MyDateTime=DateTime.Now;
            d.MyMoney = 520.58M;
            return d;
        }
    }
    public class DataObject {
        public DateTime MyDateTime { get; set; }
        public decimal MyMoney { get; set; }
    }
      <TextBox Height="23" Canvas.Left="137" TextWrapping="Wrap" Text="{Binding Source={StaticResource nowTime},Path=MyMoney,StringFormat=我的Money:{0:C}}" Canvas.Top="24" Width="163"/>

  1.3其他

   {0:P} 百分数,{0:E}科学计数法 {0:F?},例如{0:F0}就是不保留小数,F1就是保留1位

[Aaronyang] 写给自己的WPF4.5 笔记7[三巴掌-ItemsControl数据绑定详解与binding二次处理 3/3]

当例如 {0:P}位于StringFormat的开头字符需要使用{}转义,所以你看到时间是{}{0:yyyy年}   关于时间常见 yyyyMMddHHmmssaa 其中aa是AM或者PM

  1.4值转换器     以前写过文章使用用法,挺详细: 点击查看

在三巴掌系列里面,前面写过了简单的转换器了,当然还有条件转换器,在转换器中加入几个属性,声明资源时候可以赋值。

补充说明:

多值转换器:继承IMultiValueConverter,本来第一个object value是一个值得,现在是object[]了,那么前台怎么传递的

我们继续现在这个例子,MyDate代表消费日期,MyMoney代表消费金额吧,展示个数据

第一种普通方式:

 <Label Canvas.Top="78" Content="多值转换器:"/>
        <TextBlock Canvas.Top="83" Canvas.Left="87">
            <TextBlock.Text>
                <MultiBinding StringFormat="{}{0:yyyy年MM月dd日},{1:C}">
                    <Binding Path="MyDateTime" Source="{StaticResource nowTime}"></Binding>
                    <Binding Path="MyMoney" Source="{StaticResource nowTime}"></Binding>
                </MultiBinding>
            </TextBlock.Text>

效果:

[Aaronyang] 写给自己的WPF4.5 笔记7[三巴掌-ItemsControl数据绑定详解与binding二次处理 3/3]

假如我现在还想知道星期几消费的怎么办,或者更复杂的,关联到保密表,表示这个消费是否是加密的,怎么办?那就需要转换器了

现在我们简单做个demo,如果消费金额大于500与就不显示金额,只显示保密。

新增一个SpendHistory.cs类,实现IMultiValueConverter

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Data;

namespace TemplateDemo
{

    public class SpendHistory:IMultiValueConverter
    {
        public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            var d1 =values[0];
            string d1string = "";
            if (d1 is DateTime) {
                 d1string = ((DateTime)d1).ToString("yyyy年MM月dd日:");
            }
           
            decimal d = (decimal)values[1];
            if (d > 500)
            {
                return d1string + "保密";
            }
            else {
                return d1string +d.ToString("0:C");
            }

        }

        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }

    }
}

OK,补充说明下面的ConvertBack一般当你的绑定是TwoWay的,或者OneWayToSource就需要需实现了

在运行项目前,你需要将顶部声明的ObjectDataProvider的IsAsynchronous改成false,否则下面的绑定会报错。关于ConverterParameter的转换器参数不讲了,通过bingding指定ConverterParameter后,就可以再转换器的parameter参数中取得该值


 

整体代码如下:

<Window x:Class="TemplateDemo.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:TemplateDemo"
        Title="MainWindow" Height="700" Width="1200">
    <Window.Resources>
        <ObjectDataProvider x:Key="nowTime" MethodName="GetDate" IsAsynchronous="False" ObjectType="{x:Type local:DataDemo}"/>
        <local:SpendHistory x:Key="spendConvertor"></local:SpendHistory>
    </Window.Resources>

    <Canvas>
        <Label>时间:{}{0:时间格式}</Label>
        <TextBox Height="23" Canvas.Left="137" TextWrapping="Wrap" Text="{Binding Source={StaticResource nowTime},Path=MyDateTime,StringFormat={}{0:yyyy年MM月dd日}}" Canvas.Top="4" Width="163"/>
        <Label Canvas.Top="22">货币:{0:C}</Label>
        <TextBox Height="23" Canvas.Left="137" TextWrapping="Wrap" Text="{Binding Source={StaticResource nowTime},Path=MyMoney,StringFormat=我的Money:{0:C}}" Canvas.Top="24" Width="163"/>
        <Label Canvas.Top="48">1位小数:{0:F1}</Label>
        <TextBox Height="23" Canvas.Left="137" TextWrapping="Wrap" Text="{Binding MyMoney, Source={StaticResource nowTime}, StringFormat=我的1位小数:{0:F1}}" Canvas.Top="52" Width="163"/>
        <Label Canvas.Top="78" Content="多值转换器1:"/>
        <TextBlock Canvas.Top="83" Canvas.Left="87">
            <TextBlock.Text>
                <MultiBinding StringFormat="{}{0:yyyy年MM月dd日},{1:C}">
                    <Binding Path="MyDateTime" Source="{StaticResource nowTime}"></Binding>
                    <Binding Path="MyMoney" Source="{StaticResource nowTime}"></Binding>
                </MultiBinding>
            </TextBlock.Text>
        </TextBlock>
        <Label Canvas.Top="98" Content="多值转换器2:"/>
        <TextBlock Canvas.Top="103" Canvas.Left="87" x:Name="tbtest">
            <TextBlock.Text>
                <MultiBinding  Converter="{StaticResource spendConvertor}" >
                    <Binding Path="MyDateTime" Source="{StaticResource nowTime}"></Binding>
                    <Binding Path="MyMoney" Source="{StaticResource nowTime}"></Binding>
                </MultiBinding>
            </TextBlock.Text>
        </TextBlock>

    </Canvas>
</Window>

展示效果:

[Aaronyang] 写给自己的WPF4.5 笔记7[三巴掌-ItemsControl数据绑定详解与binding二次处理 3/3]

 

 

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

 

接下来比较复杂的 ItemsControl 

[Aaronyang] 写给自己的WPF4.5 笔记7[三巴掌-ItemsControl数据绑定详解与binding二次处理 3/3]

在WPF中有很多集合数据源的控件,重复的显示每一项,我们可以控制每一项(Item)的显示样式和模板,然后样式选择器ItemContainerStyleSelector,模板选择器ItemTemplateSelector,接着每一项重复几次AlternationCount,一个数据的来源ItemsSouce,所有项外面都有层Panel,这个Panel使用哪种,怎么控制,就操作ItemPanel,像Grid那种有的可以分组,所以每组样式怎么控制,就有了GroupStyle,当然组不止一个,为了让组显示不同的样式,所以又有了GroupStyleSelector。同理还有简单的指定DisplayMemberPath,ItemStringFormat等

关于Selector类,就拥有了 SelectedItem(选中的数据对象),SelectedIndex(选中项索引),SelectedValue(选中对象的Value属性,通过SelectedValuePath指定),ListBox的多选是Listbox类自己提供的,SelectionMode和SelectedItems。

1. ItemsSource,DisplayMemberPath,ItemStringFormat,ItemContainerStyle

其实不太想偷懒方式修改模板或者样式(blend),在vs中也可以简单使用,使用文档大纲

[Aaronyang] 写给自己的WPF4.5 笔记7[三巴掌-ItemsControl数据绑定详解与binding二次处理 3/3]      [Aaronyang] 写给自己的WPF4.5 笔记7[三巴掌-ItemsControl数据绑定详解与binding二次处理 3/3]

生成代码:

   <ListBox x:Name="lbDemo1" Height="100" Canvas.Left="377.868" Canvas.Top="10" Width="376.337" ItemContainerStyle="{DynamicResource ListBoxItemStyle1}">
            <ListBox.Resources>
                <Style x:Key="ListBoxItemStyle1" TargetType="{x:Type ListBoxItem}">
                    <Setter Property="Template">
                        <Setter.Value>
                            <ControlTemplate TargetType="{x:Type ListBoxItem}">
                                <Grid/>
                            </ControlTemplate>
                        </Setter.Value>
                    </Setter>
                </Style>
            </ListBox.Resources>
        </ListBox>

默认是以资源方式,嵌入进来的,下面的定位为止,如果是window窗口,就是Window.Resources中定义了,其实这样挺冗余的,复杂效果的时候用resource方式,简单的话,就直接Listbox点出来吧

先创建一批假数据吧,方便测试

   public class DataDemo
    {
        public DataObject GetDate()
        {
            DataObject d = new DataObject();
            d.MyDateTime = DateTime.Now;
            d.MyMoney = 520.58M;
            List<Blog> blog = new List<Blog> { 
                new Blog{BlogName="wpf文章1",BlogContent="wpf内容1"},
                new Blog{BlogName="wpf文章2",BlogContent="wpf内容2"},
                new Blog{BlogName="wpf文章3",BlogContent="wpf内容3"},
                new Blog{BlogName="wpf文章4",BlogContent="wpf内容4"},
                new Blog{BlogName="wpf文章5",BlogContent="wpf内容5"},
                new Blog{BlogName="wpf文章6",BlogContent="wpf内容6"},
                new Blog{BlogName="wpf文章7",BlogContent="wpf内容7"},
                new Blog{BlogName="wpf文章8",BlogContent="wpf内容8"},
                new Blog{BlogName="wpf文章9",BlogContent="wpf内容9"},
            };
            d.MyBlogs = blog;
            return d;
        }
    }
    public class DataObject
    {
        public DateTime MyDateTime { get; set; }
        public decimal MyMoney { get; set; }
        public List<Blog> MyBlogs { get; set; }
    }

    public class Blog {
        public string BlogName { get; set; }
        public string BlogContent { get; set; }
    }

前台先手动写代码吧,增加熟练度:

   <ListBox x:Name="lbDemo1" Height="100" Canvas.Left="377.868" Canvas.Top="10" Width="376.337" ItemsSource="{Binding Path=MyBlogs,Source={StaticResource nowTime}}" DisplayMemberPath="BlogName"  ItemStringFormat="Aaronyang-《{0}》">
            <ListBox.ItemContainerStyle>
                <Style>
                    
                </Style>
            </ListBox.ItemContainerStyle>
        </ListBox>

简单使用了ItemsSource,DisplayMemberPath,ItemStringFormat,知道原理,就很容易使用了。指定子项的数据,每一项显示哪个字段,每项怎么格式化

效果图:

[Aaronyang] 写给自己的WPF4.5 笔记7[三巴掌-ItemsControl数据绑定详解与binding二次处理 3/3]

接下来稍微改变每项的样式

<ListBox.ItemContainerStyle>
                <Style>
                    <Setter Property="ListBoxItem.Background" Value="#A5E0F1"></Setter>
                    <Style.Triggers>
                        <Trigger Property="ListBoxItem.IsSelected" Value="True">
                            <Setter Property="ListBoxItem.Background" Value="#44BCDF"></Setter>
                            <Setter Property="ListBoxItem.Foreground" Value="#fff"></Setter>
                        </Trigger>
                    </Style.Triggers>
                </Style>
            </ListBox.ItemContainerStyle>

效果图:

[Aaronyang] 写给自己的WPF4.5 笔记7[三巴掌-ItemsControl数据绑定详解与binding二次处理 3/3]

选中后,字体变白,背景其他颜色,这种效果被一一重复的影响到每一项Item,更复杂的效果当然就要看你的Style怎么写了

 

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

 

2.ItemContainerStyle下修改显示模板,所以ControlTemplate属性了

接下来,我们在每一项前面加上一个checkbox,然后文章后面加上 下载 的超链接,在listbox上方加上,下载的按钮,我们在Style中修改Template属性,注意这里的Style要加上 TargetType,不然Template属性出不来。

我们设置SelectionMode=Muliple多选

  <Button x:Name="downBlog" Width="66" Height="18" Content="下载" Canvas.Left="378" Canvas.Top="22" Click="downBlog_Click"/>
        <ListBox x:Name="lbDemo1" Height="100" Canvas.Left="378" Canvas.Top="42" Width="376" ItemsSource="{Binding Path=MyBlogs,Source={StaticResource nowTime}}" DisplayMemberPath="BlogName" SelectionMode="Multiple">
            <ListBox.ItemContainerStyle>
                <Style TargetType="ListBoxItem">
                    <Setter Property="ListBoxItem.Background" Value="#A5E0F1"></Setter>
                    <Setter Property="Margin" Value="2"></Setter>
                    <Setter Property="Template">
                        <Setter.Value>
                            <ControlTemplate>
                                <CheckBox Focusable="False" IsChecked="{Binding RelativeSource={RelativeSource TemplatedParent},Path=IsSelected,Mode=TwoWay}">
                                    <StackPanel Orientation="Horizontal">
                                        <ContentPresenter Content="{Binding Path=BlogName}"/>
                                        <TextBlock Margin="15,0,0,0"><Hyperlink>下载</Hyperlink></TextBlock>
                                    </StackPanel>
                                </CheckBox>
                            </ControlTemplate>
                        </Setter.Value>
                    </Setter>

                    <Style.Triggers>
                        <Trigger Property="ListBoxItem.IsSelected" Value="True">
                            <Setter Property="ListBoxItem.Background" Value="#44BCDF"></Setter>
                            <Setter Property="ListBoxItem.Foreground" Value="#fff"></Setter>
                        </Trigger>
                    </Style.Triggers>
                </Style>
            </ListBox.ItemContainerStyle>
        </ListBox>

单击下载后,拿到选择的值

   private void downBlog_Click(object sender, RoutedEventArgs e)
        {
            var a=lbDemo1.SelectedItems;
            if(a.Count==0){
                MessageBox.Show("你没有选择任何选项");
                return;
            }
            StringBuilder sb=new StringBuilder();
            foreach (var item in a)
            {
                sb.AppendLine(((Blog)item).BlogName);
            }
            MessageBox.Show(sb.ToString());
        }

效果图:

[Aaronyang] 写给自己的WPF4.5 笔记7[三巴掌-ItemsControl数据绑定详解与binding二次处理 3/3]

ContentPresenter控件,你理解为内容显示控件,在这里他就是Item中绑定的要显示的字和一个整体的位置。那么关于radioButton的版本,你应该也会写了。

注意:这里修改了模板,样式的触发器没效果了。但是你可以在模板触发器中编写

 

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

 

3.AlternationCount

我们在Listbox上增加AlternationCount属性AlternationCount=3,表示自动会给Item项增加属性    AlternationIndex=0,然后1,2 ,就是0,1,2这样循环

我们去掉模板的代码,给不同的AlternationIndex添加不同的背景颜色

 <ListBox x:Name="lbDemo1" AlternationCount="3" Height="100" Canvas.Left="378" Canvas.Top="42" Width="376" ItemsSource="{Binding Path=MyBlogs,Source={StaticResource nowTime}}" DisplayMemberPath="BlogName" SelectionMode="Multiple">
            <ListBox.ItemContainerStyle>
                <Style TargetType="ListBoxItem">
                    <Setter Property="ListBoxItem.Background" Value="#A5E0F1"></Setter>

                    <!--<Setter Property="Template">
                        <Setter.Value>
                            <ControlTemplate>
                                <CheckBox Focusable="False" IsChecked="{Binding RelativeSource={RelativeSource TemplatedParent},Path=IsSelected,Mode=TwoWay}" x:Name="ck">
                                    <StackPanel Orientation="Horizontal">
                                        <ContentPresenter Content="{Binding Path=BlogName}"/>
                                        <TextBlock Margin="15,0,0,0"><Hyperlink>下载</Hyperlink></TextBlock>
                                    </StackPanel>
                                </CheckBox>
                                <ControlTemplate.Triggers>
                                    
                                </ControlTemplate.Triggers>
                            </ControlTemplate>
                            
                        </Setter.Value>
                    </Setter>-->

                    <Style.Triggers>
                        <Trigger Property="ItemsControl.AlternationIndex" Value="0">
                            <Setter Property="ListBoxItem.Background" Value="#64CD4F"></Setter>
                            <Setter Property="ListBoxItem.Foreground" Value="#fff"></Setter>
                        </Trigger>
                        <Trigger Property="ItemsControl.AlternationIndex" Value="1">
                            <Setter Property="ListBoxItem.Background" Value="#FF0000"></Setter>
                            <Setter Property="ListBoxItem.Foreground" Value="#fff"></Setter>
                        </Trigger>
                        <Trigger Property="ItemsControl.AlternationIndex" Value="2">
                            <Setter Property="ListBoxItem.Background" Value="#FFC90E"></Setter>
                            <Setter Property="ListBoxItem.Foreground" Value="#fff"></Setter>
                        </Trigger>
                        <Trigger Property="ListBoxItem.IsSelected" Value="True">
                            <Setter Property="ListBoxItem.Background" Value="#44BCDF"></Setter>
                            <Setter Property="ListBoxItem.Foreground" Value="#fff"></Setter>
                        </Trigger>
                    </Style.Triggers>
                </Style>
            </ListBox.ItemContainerStyle>
        </ListBox>

效果图:

[Aaronyang] 写给自己的WPF4.5 笔记7[三巴掌-ItemsControl数据绑定详解与binding二次处理 3/3]

 

4.样式选择器

OK,现在我们根据 是否 博客是否置顶属性,给置顶的文章额外的样式,增加 IsTop属性

public class DataDemo
    {
        public DataObject GetDate()
        {
            DataObject d = new DataObject();
            d.MyDateTime = DateTime.Now;
            d.MyMoney = 520.58M;
            List<Blog> blog = new List<Blog> { 
                new Blog{BlogName="wpf文章1",BlogContent="wpf内容1"},
                new Blog{BlogName="wpf文章2",BlogContent="wpf内容2",IsTop=true},
                new Blog{BlogName="wpf文章3",BlogContent="wpf内容3"},
                new Blog{BlogName="wpf文章4",BlogContent="wpf内容4",IsTop=true},
                new Blog{BlogName="wpf文章5",BlogContent="wpf内容5"},
                new Blog{BlogName="wpf文章6",BlogContent="wpf内容6"},
                new Blog{BlogName="wpf文章7",BlogContent="wpf内容7"},
                new Blog{BlogName="wpf文章8",BlogContent="wpf内容8"},
                new Blog{BlogName="wpf文章9",BlogContent="wpf内容9"}
            };
            d.MyBlogs = blog;
            return d;
        }
    }
    public class Blog
    {
        public string BlogName { get; set; }
        public string BlogContent { get; set; }

        private bool isTop = false;
        public bool IsTop
        {
            get { return isTop; }
            set { isTop = value; }
        }
    }

接下来,我们在前台的window.Resources增加2个样式,置顶的 样式描红,加粗,字体大点。其他的正常显示,有淡灰色背景

 <Window.Resources>
        <ObjectDataProvider x:Key="nowTime" MethodName="GetDate" IsAsynchronous="False" ObjectType="{x:Type local:DataDemo}"/>
        <local:SpendHistory x:Key="spendConvertor"></local:SpendHistory>

        <Style TargetType="ListBoxItem" x:Key="normalBlog">
            <Setter Property="ListBoxItem.Background" Value="#E3E3E3"></Setter>
            <Setter Property="ListBoxItem.Foreground" Value="#9E9D9D"></Setter>
        </Style>
        <Style TargetType="ListBoxItem" x:Key="topBlog">
            <Setter Property="ListBoxItem.Background" Value="#E3E3E3"></Setter>
            <Setter Property="ListBoxItem.Foreground" Value="#FF0000"></Setter>
            <Setter Property="ListBoxItem.FontSize" Value="18"></Setter>
            <Setter Property="ListBoxItem.FontWeight" Value="Bold"></Setter>
        </Style>
    </Window.Resources>

新建一个类,继承样式选择器类

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Windows;
using System.Windows.Controls;

namespace TemplateDemo
{
    public class BlogSelector : StyleSelector
    {
        public Style NormalStyle { get; set; }
        public Style TopStyle { get; set; }


        /// <summary>
        /// 根据对象的什么属性
        /// </summary>
        public string PropertyPath { get; set; }
        /// <summary>
        /// 根据对象的属性,参考判断的值
        /// </summary>
        public string PropertyValue { get; set; }



        public override Style SelectStyle(object item, DependencyObject container)
        {
            Blog blog = (Blog)item;

            Type type = blog.GetType();
            PropertyInfo pi = type.GetProperty(PropertyPath);//获得这个blog对象的指定属性
            var pi_value = pi.GetValue(blog,null);
            if (pi_value.ToString() == PropertyValue)
            {
                return TopStyle;
            }
            else {
                return NormalStyle;
            }
         
        }
    }
}

修改Listbox

<ListBox x:Name="lbDemo1" Height="100" Canvas.Left="378" Canvas.Top="42" Width="376" ItemsSource="{Binding Path=MyBlogs,Source={StaticResource nowTime}}" DisplayMemberPath="BlogName" SelectionMode="Multiple">
            <ListBox.ItemContainerStyleSelector>
                <local:BlogSelector NormalStyle="{StaticResource normalBlog}" TopStyle="{StaticResource topBlog}" PropertyPath="IsTop" PropertyValue="True"/>
            </ListBox.ItemContainerStyleSelector>
        </ListBox>

效果图:

[Aaronyang] 写给自己的WPF4.5 笔记7[三巴掌-ItemsControl数据绑定详解与binding二次处理 3/3]

5.数据模板

当我们设置ItemsSource时候,ListBoxItem.Content的属性被设置为恰当的Blog对象了。我们可以在ItemTemplate自定义组合控件,然后显示数据了

  <ListBox x:Name="lbDemo1" Height="344" Canvas.Left="378" Canvas.Top="42" Width="340" ItemsSource="{Binding Path=MyBlogs,Source={StaticResource nowTime}}"  SelectionMode="Multiple">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <Border BorderBrush="#008000" BorderThickness="1" Margin="2" CornerRadius="2" Background="#fff" Width="305">
                        <Grid>
                            <Grid.RowDefinitions>
                                <RowDefinition Height="30"/>
                                <RowDefinition Height="60"/>
                            </Grid.RowDefinitions>
                            <TextBlock Grid.Row="0" Text="{Binding BlogName}" VerticalAlignment="Center" Background="#F1F0F0"></TextBlock>
                            <TextBlock Grid.Row="1" Text="{Binding BlogContent}" TextTrimming="WordEllipsis" TextWrapping="Wrap" Padding="3"></TextBlock>
                        </Grid>
                    </Border>
                </DataTemplate>
            </ListBox.ItemTemplate>
            <ListBox.ItemContainerStyleSelector>
                <local:BlogSelector NormalStyle="{StaticResource normalBlog}" TopStyle="{StaticResource topBlog}" PropertyPath="IsTop" PropertyValue="True"/>
            </ListBox.ItemContainerStyleSelector>
        </ListBox>

这里我去掉了 DisplayMemberPath,因为不显示单个数据了,使用DataTemplate时候也必须去掉DisplayMemberPath

效果图:

[Aaronyang] 写给自己的WPF4.5 笔记7[三巴掌-ItemsControl数据绑定详解与binding二次处理 3/3]

接下来,我们把数据模板移到资源中去,使用key的方式使用

  <DataTemplate x:Key="blogdt">
            <Border BorderBrush="#008000" BorderThickness="1" Margin="2" CornerRadius="2" Background="#fff" Width="305">
                <Grid>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="30"/>
                        <RowDefinition Height="60"/>
                    </Grid.RowDefinitions>
                    <TextBlock Grid.Row="0" Text="{Binding BlogName}" VerticalAlignment="Center" Background="#F1F0F0"></TextBlock>
                    <TextBlock Grid.Row="1" Text="{Binding BlogContent}" TextTrimming="WordEllipsis" TextWrapping="Wrap" Padding="3"></TextBlock>
                </Grid>
            </Border>
        </DataTemplate>

使用:

      <ListBox x:Name="lbDemo1" ItemTemplate="{StaticResource blogdt}" Height="344" Canvas.Left="378" Canvas.Top="42" Width="340" ItemsSource="{Binding Path=MyBlogs,Source={StaticResource nowTime}}"  SelectionMode="Multiple">
            <ListBox.ItemContainerStyleSelector>
                <local:BlogSelector NormalStyle="{StaticResource normalBlog}" TopStyle="{StaticResource topBlog}" PropertyPath="IsTop" PropertyValue="True"/>
            </ListBox.ItemContainerStyleSelector>
        </ListBox>

效果是一样的。

接下来,我们可以定义 数据类型DataType 绑定 数据模板

删掉资源中,x:key的代码,换上DataType

    <DataTemplate DataType="{x:Type local:Blog}">
            <Border BorderBrush="#008000" BorderThickness="1" Margin="2" CornerRadius="2" Background="#fff" Width="305">
                <Grid>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="30"/>
                        <RowDefinition Height="60"/>
                    </Grid.RowDefinitions>
                    <TextBlock Grid.Row="0" Text="{Binding BlogName}" VerticalAlignment="Center" Background="#F1F0F0"></TextBlock>
                    <TextBlock Grid.Row="1" Text="{Binding BlogContent}" TextTrimming="WordEllipsis" TextWrapping="Wrap" Padding="3"></TextBlock>
                </Grid>
            </Border>
        </DataTemplate>

那么该窗口中,所有数据类型是Blog的展示,都会自动应该该数据模板

那么下面的Listbox就不需要指定ItemTemplate了,也可以看到刚刚的展示效果

提示1:

  但是如果Listbox还指定了ItemTemplate,那么还是以指定的数据模板展示。

  WPF的数据模板中,例如有按钮,则可以像在窗口中写代码一样,可以添加事件,事件中可以拿到数据源,你就理解它就好像是Item中的窗口的代码,而不是单独的,绑定时候时候使用转换器等。

 

接下来,在DataTemplate中还有触发器,叫做DataTrigger,跟其他触发器一样的,就是值是绑定的。OK,现在我们去掉ListBox的样式选择器。把置顶的文章背景变成黄色

  <DataTemplate x:Key="blogdt">
            <Border BorderBrush="#f00" BorderThickness="1" Margin="2" CornerRadius="2" Background="#fff" Width="305" Name="BlogItemBorder">
                <Grid>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="30"/>
                        <RowDefinition Height="60"/>
                    </Grid.RowDefinitions>
                    <TextBlock Grid.Row="0" Text="{Binding BlogName}" VerticalAlignment="Center" Background="#F1F0F0"></TextBlock>
                    <TextBlock Grid.Row="1" Text="{Binding BlogContent}" TextTrimming="WordEllipsis" TextWrapping="Wrap" Padding="3"></TextBlock>
                </Grid>
            </Border>
            <DataTemplate.Triggers>
                <DataTrigger Binding="{Binding Path=IsTop}" Value="True">
                    <Setter TargetName="BlogItemBorder" Property="Background" Value="Yellow"/>
                </DataTrigger>
            </DataTemplate.Triggers>
        </DataTemplate>

效果图:

[Aaronyang] 写给自己的WPF4.5 笔记7[三巴掌-ItemsControl数据绑定详解与binding二次处理 3/3]

提示2:

数据模板绑定的对象如果实现了INotifyPropertyChanged接口,那么后台改变数据,模板中的数据也会变

 

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

 

6.模板选择器

新建一个模板选择器,BlogDataTemplateSelector,方式和样式选择器很像

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;

namespace TemplateDemo
{
    public class BlogDataTemplateSelector : DataTemplateSelector
    {
        public DataTemplate NormalDataTemplate { get; set; }
        public DataTemplate TopDataTemplate { get; set; }


        /// <summary>
        /// 根据对象的什么属性
        /// </summary>
        public string PropertyPath { get; set; }
        /// <summary>
        /// 根据对象的属性,参考判断的值
        /// </summary>
        public string PropertyValue { get; set; }



        public override DataTemplate SelectTemplate(object item, DependencyObject container)
        {
            Blog blog = (Blog)item;

            Type type = blog.GetType();
            PropertyInfo pi = type.GetProperty(PropertyPath);//获得这个blog对象的指定属性
            var pi_value = pi.GetValue(blog, null);
            if (pi_value.ToString() == PropertyValue)
            {
                return TopDataTemplate;
            }
            else
            {
                return NormalDataTemplate;
            }

        }

    }
}

接下来,前台2个数据模板,然后在容器中指定模板选择器

新增2个DataTemplate,一个横着显示,一个竖着显示

   <DataTemplate x:Key="normalData">
            <Border BorderBrush="#008000" BorderThickness="1" Margin="2" CornerRadius="2" Background="#fff" Width="305">
                <Grid>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="30"/>
                        <RowDefinition Height="60"/>
                    </Grid.RowDefinitions>
                    <TextBlock Grid.Row="0" Text="{Binding BlogName}" VerticalAlignment="Center" Background="#F1F0F0"></TextBlock>
                    <TextBlock Grid.Row="1" Text="{Binding BlogContent}" TextTrimming="WordEllipsis" TextWrapping="Wrap" Padding="3"></TextBlock>
                </Grid>
            </Border>
        </DataTemplate>
        <DataTemplate x:Key="TopData">
            <Border BorderBrush="#008000" BorderThickness="1" Margin="2" CornerRadius="2" Background="#fff" Width="305">
                <Grid>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="50"/>
                        <ColumnDefinition Width="280"/>
                    </Grid.ColumnDefinitions>
                    <TextBlock Grid.Column="0" Text="{Binding BlogName}" VerticalAlignment="Center" Background="#F1F0F0"  Foreground="#f00"></TextBlock>
                    <TextBlock Grid.Column="1" Text="{Binding BlogContent}" TextTrimming="WordEllipsis" TextWrapping="Wrap" Padding="3"></TextBlock>
                </Grid>
            </Border>
        </DataTemplate>

ListBox使用中ItemTemplateSelector

  <ListBox x:Name="lbDemo1"  Height="344" Canvas.Left="378" Canvas.Top="42" Width="340" ItemsSource="{Binding Path=MyBlogs,Source={StaticResource nowTime}}"  SelectionMode="Multiple">
            <ListBox.ItemTemplateSelector>
                <local:BlogDataTemplateSelector NormalDataTemplate="{StaticResource normalData}" TopDataTemplate="{StaticResource TopData}" PropertyPath="IsTop" PropertyValue="True"/>
            </ListBox.ItemTemplateSelector>
        </ListBox>

效果图:isTop等于true的使用了第二个模板,false的使用了第一个模板

[Aaronyang] 写给自己的WPF4.5 笔记7[三巴掌-ItemsControl数据绑定详解与binding二次处理 3/3]

到目前,选中SelectedItem的时候,背景颜色都不出来,因为设置了数据模板中,border的背景颜色被设置了白色, 我们在ItemContainerStyle中设置了Trigger也没用,我们把TopData这个数据模板中的border的background属性绑定到父节点的ListBoxItem的背景颜色上,就有效果了。

 <DataTemplate x:Key="TopData">
            <Border BorderBrush="#008000" BorderThickness="1" CornerRadius="2" Background="{Binding RelativeSource={RelativeSource AncestorType={x:Type ListBoxItem},Mode=FindAncestor},Path=Background}"  Width="316">
                <Grid>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="50"/>
                        <ColumnDefinition Width="280"/>
                    </Grid.ColumnDefinitions>
                    <TextBlock Grid.Column="0" Text="{Binding BlogName}" VerticalAlignment="Center" Background="#F1F0F0" Foreground="#f00"></TextBlock>
                    <TextBlock Grid.Column="1" Text="{Binding BlogContent}" TextTrimming="WordEllipsis" TextWrapping="Wrap" Padding="3"></TextBlock>
                </Grid>
            </Border>
        </DataTemplate>

然后ListBox

   <ListBox x:Name="lbDemo1"  Height="344" Canvas.Left="378" Canvas.Top="42" Width="340" ItemsSource="{Binding Path=MyBlogs,Source={StaticResource nowTime}}"  SelectionMode="Multiple" HorizontalAlignment="Stretch">
            <ListBox.ItemTemplateSelector>
                <local:BlogDataTemplateSelector NormalDataTemplate="{StaticResource normalData}" TopDataTemplate="{StaticResource TopData}" PropertyPath="IsTop" PropertyValue="True"/>
            </ListBox.ItemTemplateSelector>
            <ListBox.ItemContainerStyle>
                <Style>
                    <Setter Property="Control.Padding" Value="0"/>
                    <Style.Triggers>
                        <Trigger Property="ListBoxItem.IsSelected" Value="True">
                            <Setter Property="ListBoxItem.Background" Value="Red"></Setter>
                        </Trigger>
                    </Style.Triggers>
                </Style>
            </ListBox.ItemContainerStyle>
        </ListBox>

由于模板中,Border的背景绑定了父节点的背景,我们只需要将Border最大化占满Item,就可以完全替代Item了。由于使用模板选择器,所以很明显看到ListboxItem的这个问题,怎么解决的。默认即使容器

有触发器,也不会修改背景的颜色(我们去掉normalData中的背景白色 和 Margin,请自己简单调整吧),这是由于模板包含了使用不同颜色的元素,这些元素在原来的颜色上显示,给遮住了。

这里使用了元素绑定的知识 Binding RelativeSource={RelativeSource AncestorType={x:Type ListBoxItem},Mode=FindAncestor},Path=Background

相对源,我们向上找节点FindAncestor,找到第一个ListBoxItem的元素,然后它的属性Background

效果图:

[Aaronyang] 写给自己的WPF4.5 笔记7[三巴掌-ItemsControl数据绑定详解与binding二次处理 3/3]

 

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

 

7.容器Panel,改变布局

到现在你看到的都是竖着摆放ListBoxItem的,因为Listbox容器中是StackPanel,我们更改其容器即可。我们把容器改成WrapPanel

为了更好的演示效果,我们把每个Item的宽度改小点,下面代码改了Border的宽度。我们修改下Listbox的宽度,方便查看效果

 <DataTemplate x:Key="normalData">
            <Border BorderBrush="#008000" BorderThickness="1"  CornerRadius="2"  Width="200">
                <Grid>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="30"/>
                        <RowDefinition Height="60"/>
                    </Grid.RowDefinitions>
                    <TextBlock Grid.Row="0" Text="{Binding BlogName}" VerticalAlignment="Center" Background="#F1F0F0"></TextBlock>
                    <TextBlock Grid.Row="1" Text="{Binding BlogContent}" TextTrimming="WordEllipsis" TextWrapping="Wrap" Padding="3"></TextBlock>
                </Grid>
            </Border>
        </DataTemplate>
        <DataTemplate x:Key="TopData">
            <Border BorderBrush="#008000" BorderThickness="1" CornerRadius="2" Background="{Binding RelativeSource={RelativeSource AncestorType={x:Type ListBoxItem},Mode=FindAncestor},Path=Background}"  Width="200">
                <Grid>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="50"/>
                        <ColumnDefinition Width="280"/>
                    </Grid.ColumnDefinitions>
                    <TextBlock Grid.Column="0" Text="{Binding BlogName}" VerticalAlignment="Center" Background="#F1F0F0" Foreground="#f00"></TextBlock>
                    <TextBlock Grid.Column="1" Text="{Binding BlogContent}" TextTrimming="WordEllipsis" TextWrapping="Wrap" Padding="3"></TextBlock>
                </Grid>
            </Border>
        </DataTemplate>

Listbox代码如下:我们指定了ItemsPanel的值使用了WrapPanel

  <ListBox ScrollViewer.HorizontalScrollBarVisibility="Disabled" x:Name="lbDemo1"  Height="344" Canvas.Left="56" Canvas.Top="128" Width="1000" ItemsSource="{Binding Path=MyBlogs,Source={StaticResource nowTime}}"  SelectionMode="Multiple">
            <ListBox.ItemTemplateSelector>
                <local:BlogDataTemplateSelector NormalDataTemplate="{StaticResource normalData}" TopDataTemplate="{StaticResource TopData}" PropertyPath="IsTop" PropertyValue="True"/>
            </ListBox.ItemTemplateSelector>

            <ListBox.ItemContainerStyle>
                <Style>
                    <Setter Property="Control.Padding" Value="0"/>
                    <Style.Triggers>
                        <Trigger Property="ListBoxItem.IsSelected" Value="True">
                            <Setter Property="ListBoxItem.Background" Value="Red"></Setter>
                        </Trigger>
                    </Style.Triggers>
                </Style>
            </ListBox.ItemContainerStyle>
            <ListBox.ItemsPanel>
                <ItemsPanelTemplate>
                    <WrapPanel/>
                </ItemsPanelTemplate>
            </ListBox.ItemsPanel>
        </ListBox>

效果图:

[Aaronyang] 写给自己的WPF4.5 笔记7[三巴掌-ItemsControl数据绑定详解与binding二次处理 3/3]

这里我们加了ScrollViewer.HorizontalScrollBarVisibility="Disabled"  如果启用横向滚动条,则所有的listboxItem都在一行了

[Aaronyang] 写给自己的WPF4.5 笔记7[三巴掌-ItemsControl数据绑定详解与binding二次处理 3/3]

提示3:

自定义指定容器是好,但是是在考虑item项不多的时候建议使用。默认的ListBox使用的是 VirtualizingStackPanel而非StackPanel。Virtualizing的用法不说了,上篇文章已经讲解过了

如果项不多,但是模板过于复杂,一下子加载200-300多条,也是很影响使用,建议自己估量着用。

 


 

同理,关于 ComboBox的使用也很简单了,我们直接添加个ComboBox,指定数据源,由于window.Resources中的下面资源,所有使用Blog对象的,都是如下显示

  <DataTemplate DataType="{x:Type local:Blog}">
            <Border BorderBrush="#008000" BorderThickness="1" Margin="2" CornerRadius="2" Background="#fff" Width="200">
                <Grid>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="30"/>
                        <RowDefinition Height="60"/>
                    </Grid.RowDefinitions>
                    <TextBlock Grid.Row="0" Text="{Binding BlogName}" VerticalAlignment="Center" Background="#F1F0F0"></TextBlock>
                    <TextBlock Grid.Row="1" Text="{Binding BlogContent}" TextTrimming="WordEllipsis" TextWrapping="Wrap" Padding="3"></TextBlock>
                </Grid>
            </Border>
        </DataTemplate>

所以下方ComboBox指定数据源时候,立即就应用了数据模板

  <ComboBox Width="300" Canvas.Left="533" Canvas.Top="19" ItemsSource="{Binding Path=MyBlogs,Source={StaticResource nowTime}}" >
            
        </ComboBox>

效果图:

[Aaronyang] 写给自己的WPF4.5 笔记7[三巴掌-ItemsControl数据绑定详解与binding二次处理 3/3]

如果:IsEditable="True"

[Aaronyang] 写给自己的WPF4.5 笔记7[三巴掌-ItemsControl数据绑定详解与binding二次处理 3/3]

默认显示的是对象的ToString()我们现在添加TextSearch.TextPath="BlogName"

     <ComboBox Width="300" Canvas.Left="533" Canvas.Top="19" ItemsSource="{Binding Path=MyBlogs,Source={StaticResource nowTime}}" IsEditable="True" TextSearch.TextPath="BlogName">

这样选中后,就是BlogName了,并且支持搜索

方式1:

[Aaronyang] 写给自己的WPF4.5 笔记7[三巴掌-ItemsControl数据绑定详解与binding二次处理 3/3]

   不展开下拉框,输入wpf文章7,然后点击展开,默认滚动条滑到可看到BlogName等于wpf文章7的下拉项

方式2:

[Aaronyang] 写给自己的WPF4.5 笔记7[三巴掌-ItemsControl数据绑定详解与binding二次处理 3/3]

展开下拉框,输入 wpf文章5,下拉框就开始一个一个字开始匹配了,并且滚动条在移动


OK,三巴掌的数据绑定就写到这里了

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

 

[Aaronyang] 写给自己的WPF4.5 笔记7[三巴掌-ItemsControl数据绑定详解与binding二次处理 3/3]

上一篇:论文阅读:Prediction With Multicross Component forFuture Video Coding


下一篇:5W1H聊开源之Who和How——谁、如何参与开源?