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

生活总有意外,微笑对待每一件事,无需抱怨

博文摘要:数据库下载

  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 = 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,能够根据修改和设置值,能够很轻松的获得选中的值(已经写好,点击下载: 百度网盘-链接不存在)当然的我的这个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是必须要掌握的一个控件,后面也会单独美化它去讲解和使用。


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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值