生活总有意外,微笑对待每一件事,无需抱怨--Aaronyang的博客(www.ayjs.net)
博文摘要:数据库下载
- 教你如何在vs2013中不安装Mssql数据库,使用了Sqlserver Compact,以及全部ef操作这个数据库。
- 教你从后台取数据,怎么绑定前台数据,并通过wpf的方式更新界面数据,ObservableCollection和属性通知
- 教你如何绑定集合的数据,教你如何关联的前台绑定集合数据
- 教你使用前台绑定radiobutton和简单的值转换器,字符格式化器
- 教你使用了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 = 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(); } } }
<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 ===== 学习共勉 ===