[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位

当例如 {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>

效果:

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

现在我们简单做个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>

展示效果:

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

接下来比较复杂的 ItemsControl

在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中也可以简单使用,使用文档大纲

      

生成代码:

   <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,知道原理,就很容易使用了。指定子项的数据,每一项显示哪个字段,每项怎么格式化

效果图:

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

<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>

效果图:

选中后,字体变白,背景其他颜色,这种效果被一一重复的影响到每一项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());
        }

效果图:

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>

效果图:

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>

效果图:

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

效果图:

接下来,我们把数据模板移到资源中去,使用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>

效果图:

提示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的使用了第一个模板

到目前,选中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

效果图:

 =============潇洒的版权线==========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>

效果图:

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

提示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>

效果图:

如果:IsEditable="True"

默认显示的是对象的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:

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

方式2:

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



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

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

时间: 2024-11-06 22:35:34

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

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

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

[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 笔记9[复杂数据处理三步曲,数据展示ListView泪奔2/3]

 我的文章一定要做到对读者负责,否则就是失败的文章  ---------   www.ayjs.net    aaronyang技术分享 作者留言:        小小的推荐,作者的肯定,读者的支持.推不推荐不重要,重要的是希望大家能把WPF推广出去,别让这么好的技术消失了,求求了,让我们为WPF技术做一份贡献.其实写这篇文章时候已经哭了,最近几篇文章,在我个人看来都是wpf的宝藏文章.每天读者也就200-400多人,也说明了WPF的人越来也少了.但是我的Blend教程和WPF控件开发,3D W

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

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

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

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

[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

[Aaronyang] 写给自己的WPF4.5 笔记15[AyArc诞生-WPF版本绚丽的环状图,Ay制作,AyWindow强势预览]

 我的文章一定要做到对读者负责,否则就是失败的文章  ---------   www.ayjs.net    aaronyang技术分享 Ay.Framework.WPF-AyWindow强势预览,点击前往 1. AyArcChart效果图:DEMO下载 2. 使用用法: 新建一个WPF项目AyArcChartDemo,并拷贝AyArcChart用户控件xaml,由于我使用的是Arc控件,所以我们还需要引入Expression的部分dll 2.1 绝佳第一次使用 讲解几个ayarc重要的属性 A