首页 > 代码库 > [Aaronyang] 写给自己的WPF4.5 笔记5[数据绑定三巴掌1/3]

[Aaronyang] 写给自己的WPF4.5 笔记5[数据绑定三巴掌1/3]

生活总有意外,微笑对待每一件事,无需抱怨--Aaronyang的博客(www.ayjs.net)

 

博文摘要:数据库下载

  1. 教你如何在vs2013中不安装Mssql数据库,使用了Sqlserver Compact,以及全部ef操作这个数据库。
  2. 教你从后台取数据,怎么绑定前台数据,并通过wpf的方式更新界面数据,ObservableCollection和属性通知
  3. 教你如何绑定集合的数据,教你如何关联的前台绑定集合数据
  4. 教你使用前台绑定radiobutton和简单的值转换器,字符格式化器
  5. 教你使用了AY自己亲自制作的AyRadiobuttonList,此控件还在拓展,性能已经最大化的优化了,暂不支持虚拟化加载大数据。但是我发誓这个控件潜力很大,还在封装AyImageButton的各种展示方式。动画效果效果,上下左右不同停靠不同的展现方式。

          技术分享


 

1. 首先我们需要一个数据源,作为wpf的前端人员,装个sqlserver的数据库太庞大了,所以我只装了vs2013,但是你还要一个SQL Server Compact 4.0 你还需要一个工具(4M左右):下载    原地址:下载

技术分享

现在,添加数据库SchoolDb

技术分享

创建简单的表

技术分享

添加一批数据

技术分享

接下来,数据操作

使用NuGet方式下载和安装EntityFramework.SqlServerCompact

 Install-Package EntityFramework.SqlServerCompact


 

映射实体

技术分享

我勾选了 Pluralize or singularize generated object names   个人理解:可能是 如果表有外键,可能封装了 根据外键ID,可以直接很方便的取到外键所对应的其他表的值,就是1对多的关系等吧

技术分享


 

个人感觉,还是使用Codefirst方便,可以控制实体。。但是算了,路都已经走了,没办法了。继续吧========aaronyang 杨洋 安徽 六安


 

第一步,画出基本界面

<Window x:Class="ControlStyleDemo.Window1"        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"        Title="教师信息查询" Height="400" Width="600">    <Grid>        <Grid.ColumnDefinitions>            <ColumnDefinition Width="*"/>        </Grid.ColumnDefinitions>        <Grid.RowDefinitions>            <RowDefinition Height="11*"/>            <RowDefinition Height="110*"/>        </Grid.RowDefinitions>        <StackPanel Orientation="Horizontal" VerticalAlignment="Center" Margin="15,0,0,0">            <TextBlock   TextWrapping="Wrap" Text="   教师ID:"  Height="15"/>            <TextBox Height="23" TextWrapping="Wrap" Text="" Width="120" x:Name="txtTeacherId"/>            <Button x:Name="btnSearch" Content="查询" Width="75" Height="23" Margin="5,0,0,0" Click="btnSearch_Click"/>        </StackPanel>        <Grid Margin="0,0,0,0" Grid.Row="1" Background="#FFEAEAEA">            <Grid.ColumnDefinitions>                <ColumnDefinition Width="*"/>            </Grid.ColumnDefinitions>            <Grid.RowDefinitions>                <RowDefinition Height="30"/>                <RowDefinition Height="30"/>            </Grid.RowDefinitions>            <StackPanel Orientation="Horizontal" VerticalAlignment="Center" Margin="15,0,0,0" Grid.Row="0">                <TextBlock  TextWrapping="Wrap" Text="教师姓名:"  Height="15" />                <TextBox Height="23" TextWrapping="Wrap" Text="" Width="140"/>                <TextBlock  TextWrapping="Wrap" Text="出生日期:"  Height="15" Margin="30,0,0,0"/>                <TextBox Height="23" TextWrapping="Wrap" Text="" Width="140"/>            </StackPanel>            <StackPanel Orientation="Horizontal" VerticalAlignment="Center" Margin="15,0,0,0" Grid.Row="1">                <TextBlock  TextWrapping="Wrap" Text="性       别:"  Height="15" />                <TextBox Height="23" TextWrapping="Wrap" Text="" Width="140"/>                <TextBlock  TextWrapping="Wrap" Text="   手机号:"  Height="15"  Margin="30,0,0,0"/>                <TextBox Height="23" TextWrapping="Wrap" Text="" Width="140"/>            </StackPanel>        </Grid>    </Grid></Window>

界面:

技术分享

绑定例子1:使用DataContext

每个控件几乎都有DataContext,理解为一个数据中心吧,元素绑定时候,会像上包括自己找最近的DataContext,找到后根据名字,绑定值,我们给下侧的form的grid父元素加上名字,方便后台给Grid的DataContext赋值,然后它里面的元素在绑定的时候,就会找到这个数据中心结点。从而绑定grid中的控件的指定属性值

<Grid Margin="0,0,0,0" Grid.Row="1" Background="#FFEAEAEA" x:Name="formData">

我们增加一个SchoolDomain用来封装操作,例如

using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;namespace ControlStyleDemo{    public class SchoolDomain    {        public static Teacher GetTeacherById(int id)        {            Teacher returnDTO = new Teacher();            using (SchoolDbEntities school = new SchoolDbEntities())            {               returnDTO= school.Teachers.FirstOrDefault<Teacher>(x => x.Id == id);            }            return returnDTO;        }    }}

接着给查询按钮增加单击事件,获得Teacher,然后绑定grid的DataContext

   private void btnSearch_Click(object sender, RoutedEventArgs e)        {            if (txtTeacherId.Text.Trim().Length == 0)            {                txtTeacherId.Focus(); return;            }            int ID;            if (Int32.TryParse(txtTeacherId.Text, out ID))            {                try                {                    formData.DataContext = SchoolDomain.GetTeacherById(ID);                }                catch                {                    MessageBox.Show("查询出错");                }            }            else            {                MessageBox.Show("教师ID是小于9999的正整数");            }        }

前台设定绑定

    <StackPanel Orientation="Horizontal" VerticalAlignment="Center" Margin="15,0,0,0" Grid.Row="0">                <TextBlock  TextWrapping="Wrap" Text="教师姓名:"  Height="15" />                <TextBox Height="23" TextWrapping="Wrap" Text="{Binding TeacherName}" Width="140" />                <TextBlock  TextWrapping="Wrap" Text="出生日期:"  Height="15" Margin="30,0,0,0"/>                <TextBox Height="23" TextWrapping="Wrap" Text="{Binding Path=BornDate}" Width="180"/>            </StackPanel>            <StackPanel Orientation="Horizontal" VerticalAlignment="Center" Margin="15,0,0,0" Grid.Row="1">                <TextBlock  TextWrapping="Wrap" Text="性       别:"  Height="15" />                <TextBox Height="23" TextWrapping="Wrap" Text="{Binding Gender}" Width="140"/>                <TextBlock  TextWrapping="Wrap" Text="   手机号:"  Height="15"  Margin="30,0,0,0"/>                <TextBox Height="23" TextWrapping="Wrap" Text="{Binding Phone}" Width="140"/>            </StackPanel>

OK,运行项目:

技术分享


 OK,发现出生日期不太好看怎么办?

 第一种采用领域模型的思路,自己建立一层DTO,比如TeacherDTO,增加一个,然后前台绑定这个BornDateString属性,但是每次取数据时候,你需要将Teacher的属性值一一赋值到TeacherDTO中,有点麻烦,但你也可以使用AutoMapping

    public string BornDateString        {            get            {                if (BornDate.HasValue)                {                    return BornDate.Value.ToString("yyyy年MM月dd日");                }                else {                    return "";                }           }         }

第二种:使用值转换器,OK,接下来,我们把性别改成RadioButton,把日期显示美化下

新增Converter文件夹,然后新建实现IValueConverter接口的DateConverter.cs,类名上标记 从什么类型转换成什么类型的特性

using System;using System.Collections.Generic;using System.Globalization;using System.Linq;using System.Text;using System.Windows;using System.Windows.Data;namespace ControlStyleDemo.Converter{    [ValueConversion(typeof(DateTime), typeof(String))]    public class DateConverter : IValueConverter    {        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)        {            DateTime date = (DateTime)value;            return date.ToString("yyyy年MM月dd日");        }        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)        {            string strValue = http://www.mamicode.com/value as string;            DateTime resultDateTime;            if (DateTime.TryParse(strValue, out resultDateTime))            {                return resultDateTime;            }            return DependencyProperty.UnsetValue;        }    }}

使用:①引入在xmal文件引用DateConverter类所在命名空间

②定义资源:

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

③使用:

       <TextBox Height="23" TextWrapping="Wrap" Text="{Binding Path=BornDate,Converter={StaticResource DateConverter}}" Width="180"/>

关于日期的转换器,wpf内置了StringFormat

 <TextBox Height="23" TextWrapping="Wrap" Text="{Binding Path=BornDate,StringFormat={}{0:yyyy年MM月dd日}}" Width="180"/>

接下来,我们来美化性别,增加一个转换器

  [ValueConversion(typeof(string), typeof(bool))]    public class RadioCheckedConverter : IValueConverter    {        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)        {            string v = value as string ?? "";            string p = parameter as string ?? "";            if (v.Trim() == p.Trim())            {                return true;            }            else            {                return false;            }        }        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)        {            return null;        }    }

前台使用

          <StackPanel Orientation="Horizontal" VerticalAlignment="Center">                    <RadioButton GroupName="gender" IsChecked="{Binding Path=Gender,Converter={StaticResource RadioCheckedConverter},ConverterParameter=‘男‘}"></RadioButton>                    <RadioButton GroupName="gender" IsChecked="{Binding Path=Gender,Converter={StaticResource RadioCheckedConverter},ConverterParameter=‘女‘}"></RadioButton>                </StackPanel>

运行时候,发现已经可以绑定了。但是实现的不优雅,怎么办?

思路1:自定义一个行为,因为行为可以拿到当前绑定对象,我们可以把值放在radio的容器的tag属性值,如果相同,设置IsChecked属性。

        试过在查询按钮绑定Click的trigger,但是参考值始终拿不到。

        试过在radio的容器加行为,也失败了,算了,行为的思路还是放弃吧。下面是我写的失败的代码。

技术分享
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.Interactivity;using System.Windows.Media;namespace ControlStyleDemo.Behavior{    /// <summary>    /// 可以让radiobutton组的控件自动决定是否选中    /// aaronyang    /// 2015年1月22日15:37:56    /// </summary>    public class RadioCheckedBehavior : TargetedTriggerAction<UIElement>    {        public string CheckedRadioTag        {            get { return (string)GetValue(CheckedRadioTagProperty); }            set { SetValue(CheckedRadioTagProperty, value); }        }        /// <summary>        /// 容器内的radio的选中性质是否由radio的文本决定        /// </summary>        public bool CheckedIsDeterminedText        {            get { return (bool)GetValue(CheckedIsDeterminedTextProperty); }            set { SetValue(CheckedIsDeterminedTextProperty, value); }        }        /// <summary>        /// 默认是否根据radio的Text判断,默认是的,false就是根据tag判断        /// </summary>        public static readonly DependencyProperty CheckedIsDeterminedTextProperty =            DependencyProperty.Register("CheckedIsDeterminedText", typeof(bool), typeof(RadioCheckedBehavior), new PropertyMetadata(true));        public static readonly DependencyProperty CheckedRadioTagProperty =            DependencyProperty.Register("CheckedRadioTag", typeof(string), typeof(RadioCheckedBehavior), new PropertyMetadata(""));        /// <summary>        /// 查找某种类型的子控件,并返回一个List集合        /// </summary>        /// <typeparam name="T"></typeparam>        /// <param name="obj"></param>        /// <param name="typename"></param>        /// <returns></returns>        public List<T> GetChildObjects<T>(DependencyObject obj, Type typename) where T : FrameworkElement        {            DependencyObject child = null;            List<T> childList = new List<T>();            for (int i = 0; i <= VisualTreeHelper.GetChildrenCount(obj) - 1; i++)            {                child = VisualTreeHelper.GetChild(obj, i);                if (child is T && (((T)child).GetType() == typename))                {                    childList.Add((T)child);                }                childList.AddRange(GetChildObjects<T>(child, typename));            }            return childList;        }        //选中容器内的radiobutton        public void CheckedRadio()        {            var a=(Panel)VisualTreeHelper.GetParent(this.Target);            List<RadioButton> rdolist = GetChildObjects<RadioButton>(a, typeof(RadioButton));            if (CheckedIsDeterminedText)            {                foreach (var item in rdolist)                {                    if (item.Content.ToString() == CheckedRadioTag)                    {                        item.IsChecked = true;                    }                    else                    {                        item.IsChecked = false;                    }                }            }            else            {                foreach (var item in rdolist)                {                    if (item.Tag.ToString() == CheckedRadioTag)                    {                        item.IsChecked = true;                    }                    else                    {                        item.IsChecked = false;                    }                }            }        }        protected override void Invoke(object parameter)        {            CheckedRadio();        }    }}
radio行为
 <Button x:Name="btnSearch" Content="查询" Width="75" Height="23" Margin="5,0,0,0" Click="btnSearch_Click">                <i:Interaction.Triggers>                    <i:EventTrigger EventName="Click">                        <b:RadioCheckedBehavior TargetName="skGender"  CheckedIsDeterminedText="true" CheckedRadioTag="{Binding ElementName=skGender,Path=Tag,Mode=TwoWay}"/>                    </i:EventTrigger>                </i:Interaction.Triggers>            </Button>

思路2:自定义Model,二次封装,增加个bool属性,get时候判断Gender决定是否返回true还是false,这个绝对可行,也干净。

思路3:自己写个radiobuttonlist控件

      满足的需求1:能够更简洁的使用,可以动态radio,能够根据修改和设置值,能够很轻松的获得选中的值(已经写好,点击下载: http://pan.baidu.com/s/1mgqU1Vq)当然的我的这个WPF库还在拓展,已经拓展的功能不止这一个功能。

      满足的需求2:能够适应AyImageButton,支持 图标/字体/Path + 文字 的显示方式,支持纯图片/字体/path 无损展示,支持图片文字竖排展示,支持纯文字展示,支持纯图标带提示文字的方式展示 V2.0 List (尚未写好)

    用法:窗体引入空间

    xmlns:lay="clr-namespace:Ay.Framework.WPF.Controls;assembly=Ay.Framework.WPF"

使用方式:

<lay:AyRadioList x:Name="ayPanel" CheckedRadioPath="Tag"  CheckedRadioValue="{Binding Gender}"  Width="120"  VerticalAlignment="Center">                    <RadioButton  Content="男" Tag="男"></RadioButton>                    <RadioButton  Content="女" Tag="女"></RadioButton>                </lay:AyRadioList>

我自定义了一个容器,拓展2个属性CheckedRadioPath,CheckedRadioValue,思路就是容器内的radio的哪个属性等于哪个值就选中。CheckedRadioValue不一定要是绑定的值,也可以直接是值,比如男,那么Tag等于男的radio就是选中的。后台拿值,只要ayPanel.CheckedRadioValue就可以了。(感谢王老大提供的拓展思路)

 

TargetNullValue的用法,指定元素为空时候,显示什么值

技术分享

后台可以将DataContext值转换成对象

技术分享

接下来,如果我们把存储 按钮加上  IsDefault="True" 属性,表示表单可以通过回车键提交。我们输入3,点击查询按钮,修改刘强东为刘强东4,然后按下回车,发现teacher对象的teachername没有变化

技术分享

我们只要在按钮后方,将焦点转移到按钮上就行了。  FocusManager.SetFocusedElement(this, (Button)sender);

  private void btnSaveTeacher_Click(object sender, RoutedEventArgs e)        {            FocusManager.SetFocusedElement(this, (Button)sender);            Teacher tea = (Teacher)this.formData.DataContext;            try            {                MessageBox.Show(tea.TeacherName);            }            catch (Exception ex)            {                MessageBox.Show(ex.ToString());            }        }

接下来是WPF的特点知识,属性通知

我们新建一个TeacherDTO,单独继承INotifyPropertyChanged

using System;using System.Collections.Generic;using System.ComponentModel;using System.Linq;using System.Text;namespace ControlStyleDemo{    public class TeacherDTO:INotifyPropertyChanged    {        public event PropertyChangedEventHandler PropertyChanged;    }}

拷贝那个自动生成的Teacher.cs类的属性

技术分享

using System;using System.Collections.Generic;using System.ComponentModel;using System.Linq;using System.Text;namespace ControlStyleDemo{    public class TeacherDTO : INotifyPropertyChanged    {        public event PropertyChangedEventHandler PropertyChanged;        public void OnPropertyChanged(PropertyChangedEventArgs e)        {            if (PropertyChanged != null)            {                PropertyChanged(this, e);            }        }        public int Id { get; set; }        private string teacherName;        public string TeacherName        {            get            {                return teacherName;            }            set            {                teacherName = value;                OnPropertyChanged(new PropertyChangedEventArgs("TeacherName"));            }        }        private string gender;        public string Gender        {            get { return gender; }            set            {                gender = value;                OnPropertyChanged(new PropertyChangedEventArgs("Gender"));            }        }        public Nullable<System.DateTime> BornDate { get; set; }        public string Phone { get; set; }    }}

我这里只对性别和名字进行了属性通知,所以我在后台修改他们的值,界面就会自动变了

但是点击查询的时候绑定的对象就要是 TeacherDTO了,如果属性太多,你可以使用AutoMapper工具,但是超过千条的AutoMapper,发现转换速度有点慢了

  private void btnSearch_Click(object sender, RoutedEventArgs e)        {            //if (txtTeacherId.Text.Trim().Length == 0)            //{            //    txtTeacherId.Focus(); return;            //}            //int ID;            //if (Int32.TryParse(txtTeacherId.Text, out ID))            //{            //    try            //    {            //        formData.DataContext = SchoolDomain.GetTeacherById(ID);            //    }            //    catch            //    {            //        MessageBox.Show("查询出错");            //    }            //}            //else            //{            //    MessageBox.Show("教师ID是小于9999的正整数");            //}            if (txtTeacherId.Text.Trim().Length == 0)            {                txtTeacherId.Focus(); return;            }            int ID;            if (Int32.TryParse(txtTeacherId.Text, out ID))            {                try                {                   var a = SchoolDomain.GetTeacherById(ID);                   TeacherDTO tdto = new TeacherDTO();                   tdto.TeacherName = a.TeacherName;                   tdto.Phone = a.Phone;                   tdto.Id = a.Id;                   tdto.Gender = a.Gender;                   tdto.BornDate = a.BornDate;                   formData.DataContext = tdto;                }                catch                {                    MessageBox.Show("查询出错");                }            }            else            {                MessageBox.Show("教师ID是小于9999的正整数");            }        }

设置值得时候,不是前台的某个文本框,然后它的text属性等于多少了,我们可以直接对象的属性的值,前台就会自动变了

   private void btnSetValue_Click(object sender, RoutedEventArgs e)        {            TeacherDTO tea = (TeacherDTO)this.formData.DataContext;            if (tea != null) {                tea.TeacherName = "aaronyang";                tea.Gender = "";            }        }

效果:

技术分享


接下里我们绑定该老师授课的班级列表,简单的使用ObservableCollection类,它也具有通知的功能,所以当是集合的数据的控件时候,在后台修改某条对象某个属性时候,前台界面就会自动变了,而不是查找某个控件然后设置属性了。

为了更好的控制Classroom,我们还是复制一个ClassroomDTO,然后增加ToString方法

using System;using System.Collections.Generic;using System.ComponentModel;using System.Linq;using System.Text;namespace ControlStyleDemo{    public class ClassroomDTO    {        public int Id { get; set; }        public string Grade { get; set; }        public int GradeNumber { get; set; }        public int Year { get; set; }        public override string ToString()        {            return String.Format("{0}届{1}({2})班", Year, Grade.Trim(), GradeNumber);        }    }}

数据库操作数据

using System;using System.Collections.Generic;using System.Collections.ObjectModel;using System.Linq;using System.Text;using System.Threading.Tasks;namespace ControlStyleDemo{    public class SchoolDomain    {        public static Teacher GetTeacherById(int id)        {            Teacher returnDTO = new Teacher();            using (SchoolDbEntities school = new SchoolDbEntities())            {                returnDTO = school.Teachers.FirstOrDefault<Teacher>(x => x.Id == id);            }            return returnDTO;        }        public static ICollection<ClassroomDTO> GetClassByTeacherId(int id)        {            using (SchoolDbEntities school = new SchoolDbEntities())            {                List<ClassroomDTO> list = (from o in school.Classrooms                                           where o.Master == id                                           select new ClassroomDTO                                           {                                               Id = o.Id,                                               Grade = o.Grade,                                               GradeNumber = o.GradeNumber,                                               Year = o.Year                                           }                                           ).ToList<ClassroomDTO>();                return new ObservableCollection<ClassroomDTO>(list);            }        }    }}

好了,一切该有的都有了,我们在界面上加上一个Listview

       <ListView Name="ayLv" HorizontalAlignment="Left" Height="227" Margin="13,53,0,-249" Grid.Row="1" VerticalAlignment="Top" Width="179">       </ListView>

点击查询时候,修改钮代码如下:

     var a = SchoolDomain.GetTeacherById(ID);                   TeacherDTO tdto = new TeacherDTO();                   tdto.TeacherName = a.TeacherName;                   tdto.Phone = a.Phone;                   tdto.Id = a.Id;                   tdto.Gender = a.Gender;                   tdto.BornDate = a.BornDate;                   formData.DataContext = tdto;                   // 查出班级                   var classroom = SchoolDomain.GetClassByTeacherId(tdto.Id);                   ayLv.ItemsSource = classroom;

绑定ItemsSource就行了,默认显示是调用对象的ToString方法,想要跟丰富的可以使用 模板知识中的数据模版知识。关于模板的知识,网上资料太多了,我也不会写文章去写了。要写也是基于数据模板的成品。

 


书上有资料说,绑定DataTable会有些知识点要讲,感觉有必要操作一下,提高自己的熟练度。

传统ado.net生成Datatable都是使用SqlDataAdapter(SqlCommand对象),例如adapter,然后创建DataSet,例如这里ds,类似数据库的一个库,然后一个adapter.Fill(ds,"表名"),然后取出生成好的ds[0],库中第一个表。DataTable的结构很像数据库的二维表。这里假设你已经用过DataTable.

这里我们使用的是精简版的SQLServer,不方便使用DataSet,所以我们还是利用EF生成DataTable,然后绑定DataTable的DataView

打开SchoolDomain.cs,添加新操作:选择班级,以DataTable的方式展示

 ① 创建数据获得的代码,根据班级号查找学生

  //选择班级,以DataTable的方式展示        public static DataTable GetStudentsByClassId_DataTable(int id)        {            //创建空表            DataTable dt = new DataTable("Student");            //创建列            dt.Columns.Add("Id", System.Type.GetType("System.Int32"));            dt.Columns.Add("StudentName", System.Type.GetType("System.String"));            dt.Columns.Add("ClassId", System.Type.GetType("System.Int32"));                      using (SchoolDbEntities school = new SchoolDbEntities())            {                //填充数据                List<Student> list = (from o in school.Students                                      where o.ClassId == id                                      select o).ToList<Student>();                foreach (var item in list)                {                    DataRow dr = dt.NewRow();                    dr["Id"] = item.Id;                    dr["StudentName"] = item.StudentName;                    dr["ClassId"] = item.ClassId;                    dt.Rows.Add(dr);                   }            }                      return dt;        }

拓展ClassroomDTO,过会有用

    public class ClassroomDTO    {        public int Id { get; set; }        public string Grade { get; set; }        public int GradeNumber { get; set; }        public int Year { get; set; }        public override string ToString()        {            return String.Format("{0}届{1}({2})班", Year, Grade.Trim(), GradeNumber);        }        //添加学生列表        public DataView Students { get; set; }    }

接下来,在界面上加上一个学生列表的listview

    <ListView Name="ayStu"  HorizontalAlignment="Left" Height="227" Margin="221,53,0,-249" Grid.Row="1" VerticalAlignment="Top" Width="289">                <ListView.View>                    <GridView>                        <GridView.Columns>                            <GridViewColumn Header="编号" DisplayMemberBinding="{Binding Path=Id}" Width="50"></GridViewColumn>                            <GridViewColumn Header="姓名" DisplayMemberBinding="{Binding Path=StudentName}" Width="140"></GridViewColumn>                            <GridViewColumn Header="班级编号" DisplayMemberBinding="{Binding Path=ClassId}" Width="60"></GridViewColumn>                        </GridView.Columns>                    </GridView>                </ListView.View>            </ListView>

技术分享

点击查询后,我们先使用后台赋值ItemsSource的方式

 // 添加该班级的学生                   DataView dv = SchoolDomain.GetStudentsByClassId_DataTable(1).DefaultView;                   ayStu.ItemsSource = dv;

运行项目:

技术分享

但是此时右侧的列表还不能根据左侧选择班级后自动更新,但至少证明了我们前端的代码绑定的没错,后面只要数据源对了,就显示了,OK我们在获取班级信息时候,绑定刚刚ClassRoomDTO的Students值

修改GetClassByTeacherId方法如下:

 public static ICollection<ClassroomDTO> GetClassByTeacherId(int id)        {            using (SchoolDbEntities school = new SchoolDbEntities())            {                List<ClassroomDTO> list = (from o in school.Classrooms                                           where o.Master == id                                           select new ClassroomDTO                                           {                                               Id = o.Id,                                               Grade = o.Grade,                                               GradeNumber = o.GradeNumber,                                               Year = o.Year                                           }                                           ).ToList<ClassroomDTO>();                foreach (var item in list)                {                    item.Students = GetStudentsByClassId_DataTable(item.Id).DefaultView;                }                return new ObservableCollection<ClassroomDTO>(list);            }        }

注释掉刚刚后台绑定ItemsSource的代码,修改前台界面ListView代码如下,此时运行,点击左侧的时候就会立即更新右侧的学生信息了

<ListView Name="ayStu" ItemsSource="{Binding ElementName=ayLv, Path=SelectedItem.Students}" 

技术分享

所以,你可以理解此时的SelectedItem对象就是一个Classroom对象,我们绑定了他的Students属性,所以在后台,我们是可以拿到单条你选中的班级信息或者是学生信息,但是在后台修改属性,UI是不会发生变化的,所以你可以改成ObservableCollection的方式。关于DataTable,LINQ,ListView控件暂时不讲了,但是ListView是必须要掌握的一个控件,后面也会单独美化它去讲解和使用。


文章到这里,第一篇已经写完了,希望大家能学到的还是能学到,知识太弱了,还请不要生气。下一课继续讲数据的绑定,不是前台界面的那种,是后台。

==============www.ayjs.net================== 安徽 六安 杨洋 ============ aaronyang =========== ay ===== 学习共勉 ===

 

[Aaronyang] 写给自己的WPF4.5 笔记5[数据绑定三巴掌1/3]