Fork me on GitHub

14、数据绑定相关例子

1、把普通CLR类型单个对象指定为Source:包括.NET Framework自带类型的对象和用户自定义类型的对象。

如果类型实现了INotifyPropertyChanged接口,则可通过在属性的set语句里激发PropertyChanged事件来通知Binding数据已被更新。

using System.ComponentModel;

namespace _SampleDataBinding
{
    class Person:INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        private string _personName= string.Empty;

        public string PersonName 
        {
            get { return this._personName; }
            set { this._personName = value;
                if (PropertyChanged != null)
                {
                    PropertyChanged.Invoke(this,new PropertyChangedEventArgs(PersonName));
                }
            }
        }
    }
}
<StackPanel>
        <TextBox Name="TxtTest1" Text="{Binding Source={StaticResource MyPerson}, Path=PersonName}"></TextBox>
        <Button Name="BtnTest" Height="36" Click="BtnTest_OnClick">修改下试试</Button>
</StackPanel>
        private void BtnTest_OnClick(object sender, RoutedEventArgs e)
        {
            Person per=new Person();
            per.PersonName = "我修改名字了,叫麦克";
        }

2、把普通CLR集合类型对象指定为Source:包括数组、List<T>、ObservableCollection<T>等集合类型。

    class Student
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public int Age { get; set; }
    }
    <StackPanel Background="LightBlue">
        <TextBlock Text="学号:" FontWeight="Bold" Margin="5" FontSize="16"></TextBlock>
        <TextBox x:Name="TxtBoxId" Margin="5" Height="36"></TextBox>
        <TextBlock Text="学生集合:" FontSize="16" FontWeight="Bold" Margin="5"></TextBlock>
        <ListBox x:Name="ListBoxStudents" Height="195" Margin="5">
        </ListBox>
    </StackPanel>
            //准备数据源
            List<Student> stuList =new List<Student> ( )
            {
                new Student(){Id = 001,Name = "Apple",Age = 12},
                new Student(){Id = 002,Name = "Banana",Age = 13},
                new Student(){Id = 003,Name = "Cocoa",Age = 14},
                new Student(){Id = 004,Name = "Dog",Age = 15},
                new Student(){Id = 005,Name = "Func",Age = 16},
                new Student(){Id = 006,Name = "Great",Age = 17}
            };
            //为ListBox设置Binding
            this.ListBoxStudents.ItemsSource = stuList;
            this.ListBoxStudents.DisplayMemberPath = "Name";
            //为TextBox设置Binding
            Binding binding=new Binding("SelectedItem.Id"){Source = this.ListBoxStudents};
            this.TxtBoxId.SetBinding(TextBox.TextProperty, binding);

如果将绑定代码写在XAML中该怎么写呢?

<TextBox x:Name="TxtBoxId"  Text="{Binding ElementName=ListBoxStudents,Path=SelectedItem.Id}"></TextBox>

在写Path的时候SelectedItem.Id,其中.Id是不提示的,需要硬写上去。

 想试试直接在XAML中绑定,然后搞了半天没成功,请教了猥琐猫之后他给了个解决方案,下面是代码

    public class Student
    {
        public int StudentNum { get; set; }
        public string StudentName { get; set; }
        public int StudentAge { get; set; }
    }
    public class Data
    {
        private List<Student> _studentsList; 
        public List<Student> StudentsList
        {
            get { return _studentsList; } 
            set { _studentsList = value; }
        }
    }
    {
        public Data myData;
        private List<Student> studentsList; 
        public MainWindow ( )
        {
            InitializeComponent ( );
            myData=new Data();
            //准备数据源

            studentsList =new List<Student> ( )
            {
                new Student(){StudentNum = 001,StudentName  = "Apple",StudentAge = 12},
                new Student(){StudentNum = 002,StudentName = "Banana",StudentAge = 13},
                new Student(){StudentNum = 003,StudentName = "Cocoa",StudentAge = 14},
                new Student(){StudentNum = 004,StudentName = "Dog",StudentAge = 15},
                new Student(){StudentNum = 005,StudentName = "Func",StudentAge = 16},
                new Student(){StudentNum = 006,StudentName = "Great",StudentAge = 17}
            };
            myData.StudentsList = studentsList;
            this.DataContext = myData;
        }

 再写个例子试试

        private List<Student> stuList; 
        public MainWindow ( )
        {
            InitializeComponent ( );
            stuList=new List<Student>()
                {
                    new Student(){StudentId = 001,StudentName = "张三",StudentAge = 12},
                    new Student(){StudentId = 002,StudentName = "李四",StudentAge = 13},
                    new Student(){StudentId = 003,StudentName = "王五",StudentAge = 14},
                    new Student(){StudentId = 004,StudentName = "赵六",StudentAge = 15},
                    new Student(){StudentId = 005,StudentName = "陈七",StudentAge = 16}
                };
            this.listBoxStudents.ItemsSource = stuList;
        }
        <ListBox x:Name="listBoxStudents" Height="300" Margin="5" >
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <StackPanel Orientation="Horizontal">
                        <TextBlock Text="{Binding Path=StudentId}" Width="30"></TextBlock>
                        <TextBlock Text="{Binding Path=StudentName}" Width="60"></TextBlock>
                        <TextBlock Text="{Binding Path=StudentAge}" Width="30"></TextBlock>
                    </StackPanel>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>

如果上面这个例子中又来了几条数据咋办啊?WPF给出的解决方案是:在使用集合类型作为列表空间的ItemSource时一般会考虑使用ObservableCollection<T>

来替换List<T>,因为ObservableCollection<T>实现了INotifyCollectionChanged和INotifyPropertyChanged接口,能够把集合的变化立刻通知显示它的

列表控件,该百脑汇立刻显示出来。

            private ObservableCollection<Student> newStuList;

            newStuList=new ObservableCollection<Student>()
                {
                    new Student(){StudentId = 001,StudentName = "张三",StudentAge = 12},
                    new Student(){StudentId = 002,StudentName = "李四",StudentAge = 13},
                    new Student(){StudentId = 003,StudentName = "王五",StudentAge = 14},
                    new Student(){StudentId = 004,StudentName = "赵六",StudentAge = 15},
                    new Student(){StudentId = 005,StudentName = "陈七",StudentAge = 16}
                };

            this.listBoxStudents.ItemsSource = newStuList;


        private void BtnTest_OnClick(object sender, RoutedEventArgs e)
        {
            newStuList.Add(new Student(){StudentId = 006,StudentName = "新添加的",StudentAge = 17});

        }

3、将ElementName指定为Source

        <TextBox Name="TxtBox" BorderBrush="DeepSkyBlue" Height="35" Margin="5" 
          Text
="{Binding ElementName=SliderTest,Path=Value,Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"></TextBox> <Slider Name="SliderTest" Margin="5" BorderBrush="DimGray" Maximum="100" Minimum="0"></Slider>

 Mode表示数据流动的方向,其值TwoWay说明数据是双向流动的,Slider控件的值能够更新到TextBox,TextBox上的值也能够更新到Slider上,实现这个功能需要修改

UpdateSourceTrigger属性的值,因为TextBox的该属性值默认为Default也就是LostFocus,当失去焦点之后才发生变化。

4、没有Path的绑定

    <StackPanel>
        <StackPanel.Resources>
            <sys:String x:Key="myString">没有Path的绑定,静态资源</sys:String>
        </StackPanel.Resources>
        <TextBlock Name="TextBlockTest" Text="{Binding Source={StaticResource myString} }"></TextBlock>
    </StackPanel>

C#中的等效代码

            string a = "dafagagdfgghh";
            this.TextBoxTest.SetBinding(TextBox.TextProperty, new Binding(".") {Source = a});

前面的例子大多是把单个CLR类型对象指定为Binding的Source,方法有两种-把Banding.Source属性或把对象的Name赋值给Binding.ElementName。

5、没有Source的绑定-使用DataContext作为Binding的源

 DataContext属性被定义在FrameworkElement类里,这个类是WPF控件的基类,这意味着WPF空间(包括容器控件)都具备这种属性。如前所述,WPF的UI布局是树形结构,这棵树的每个节点都有DataContext。

当一个Binding只知道自己的Path而不知道自己的Source时,它会沿着UI元素树一路向树的根部找过去,每路过一个结点就要看看这个结点的DataContext是否具有Path所指定的属性。如果有就把这个对象作为自己的Source;如果没有,那就继续找下去;如果到了树的根部还没有找到,那么这个Binding就没有Source,因而也不会得到数据。

eg1:

    <Grid DataContext="你好">
        <Grid>
            <StackPanel>
                <Button x:Name="BtnTest" Content="点击试试" Click="BtnTest_OnClick"></Button>
            </StackPanel>
        </Grid>
    </Grid>
        private void BtnTest_OnClick(object sender, RoutedEventArgs e)
        {
            MessageBox.Show(BtnTest.DataContext.ToString());
        }

eg2:

    <Grid DataContext="你好">
        <Grid>
            <StackPanel>
                <Button x:Name="BtnTest" Content="点击试试" Click="BtnTest_OnClick"></Button>
                <TextBox x:Name="TextBoxtTest" Text="{Binding Path=.}"></TextBox>
            </StackPanel>
        </Grid>
    </Grid>

eg3:

    public partial class Student
    {
        public int StuId { get; set; }
        public string StuName { get; set; }
        public int StuAge { get; set; }
    }
    <StackPanel>
        <StackPanel.DataContext>
           <local:Student StuId="001" StuName="张三" StuAge="100"></local:Student>
        </StackPanel.DataContext>
        <TextBox Text="{Binding StuId}"></TextBox>
        <TextBox Text="{Binding StuName}"></TextBox>
        <TextBox Text="{Binding StuAge}"></TextBox>
    </StackPanel>

eg4:MVVM模式解决 小灰猫提供的解决方案

    class StudentViewModel
    {
        public Student stu { get; set; }

        public StudentViewModel()
        {
            stu=new Student()
                {
                    StuId = 2,
                    StuName = "张三",
                    StuAge = 100
                };
        }
    }
    public partial class MainWindow : Window
    {
        public MainWindow ( )
        {
            InitializeComponent ( );
            this.DataContext = new StudentViewModel();
        }
    }
    <StackPanel DataContext="{Binding stu}">
        <TextBox Text="{Binding StuId}"></TextBox>
        <TextBox Text="{Binding StuName}"></TextBox>
        <TextBox Text="{Binding StuAge}"></TextBox>
    </StackPanel>

 6、使用XML数据作为Binding的数据源

    <Window.Resources>
        <XmlDataProvider x:Key="xdp" XPath="FileSystem/Folder">
            <x:XData>
                <FileSystem xmlns="">
                    <Folder Name="A">
                        <Folder Name="BooksA">
                            <Folder Name="Programming">
                                <Folder Name="Windows">
                                    <Folder Name="WCF"/>
                                    <Folder Name="MFC"/>
                                    <Folder Name="Delphi"/>
                                </Folder>
                            </Folder>
                            <Folder Name="Tools">
                                <Folder Name="Development"/>
                                <Folder Name="Designment"/>
                                <Folder Name="Players"/>
                            </Folder>
                            <Folder Name="Tools">
                                <Folder Name="Development"/>
                                <Folder Name="Designment"/>
                                <Folder Name="Players"/>
                            </Folder>
                        </Folder>
                        <Folder Name="BooksB">
                            <Folder Name="Programming">
                                <Folder Name="Windows">
                                    <Folder Name="WCF"/>
                                    <Folder Name="MFC"/>
                                    <Folder Name="Delphi"/>
                                </Folder>
                            </Folder>
                            <Folder Name="Tools">
                                <Folder Name="Development"/>
                                <Folder Name="Designment"/>
                                <Folder Name="Players"/>
                            </Folder>
                            <Folder Name="Tools">
                                <Folder Name="Development"/>
                                <Folder Name="Designment"/>
                                <Folder Name="Players"/>
                            </Folder>
                        </Folder>
                    </Folder>
                </FileSystem>
            </x:XData>
        </XmlDataProvider>
    </Window.Resources>
    <Grid>
        <TreeView ItemsSource="{Binding Source={StaticResource xdp}}">
            <TreeView.ItemTemplate>
                <HierarchicalDataTemplate ItemsSource="{Binding XPath=Folder}">
                    <TextBlock Text="{Binding XPath=@Name}"></TextBlock>
                </HierarchicalDataTemplate>
            </TreeView.ItemTemplate>
        </TreeView>
    </Grid>

如果把XmlDataProvider直接写在XAML代码里,那么它的XML数据需要放在<x:Data>...</X:Data>标签里。

如果将XML文件单独写,该如何?

<?xml version="1.0" encoding="utf-8" ?>
<StudentList>
  <Student Id="1">
    <Name>Trim</Name>
  </Student>
  <Student Id="2">
    <Name>Tom</Name>
  </Student>
  <Student Id="3">
    <Name>Jim</Name>
  </Student>
  <Student Id="4">
    <Name>Aim</Name>
  </Student>
</StudentList>
    <StackPanel Background="DeepSkyBlue">
        <ListView x:Name="listViewTest" Height="200">
            <ListView.View>
                <GridView>
                    <GridViewColumn Header="Id" Width="80" DisplayMemberBinding="{Binding XPath=@Id}"></GridViewColumn>
                    <GridViewColumn Header="Name" Width="120" DisplayMemberBinding="{Binding XPath=Name}"></GridViewColumn>
                </GridView>
            </ListView.View>
        </ListView>
        <Button Height="36" Click="ButtonBase_OnClick">XML作为绑定源</Button>
        <Button Height="36" Margin="0,5,0,0" Name="BtnTest" Click="BtnTest_OnClick">新的写法</Button>
    </StackPanel>
        private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
        {
            XmlDocument doc=new XmlDocument();
            doc.Load(@"./StudentsData.xml");
            XmlDataProvider xdp=new XmlDataProvider();
            xdp.Document = doc;
            xdp.XPath = @"/StudentList/Student";

            this.listViewTest.DataContext = xdp;
            this.listViewTest.SetBinding(ListView.ItemsSourceProperty, new Binding());
        }

        private void BtnTest_OnClick(object sender, RoutedEventArgs e)
        {
            XmlDataProvider xdp=new XmlDataProvider();
            xdp.Source = new Uri ( @"E:\C#\WPF\数据绑定练习\08使用XML数据作为Binding的源\bin\Debug\StudentsData.xml" );
            xdp.XPath = @"/StudentList/Student";

            this.listViewTest.DataContext = xdp;
            this.listViewTest.SetBinding(ListView.ItemsSourceProperty, new Binding());
        }

两种不同的读取方式

7、使用LINQ检索结果作为Binding的源

        <ListView x:Name="listViewStudents" Height="143" Margin="5">
            <ListView.View>
                <GridView>
                    <GridViewColumn Header="Id" Width="60" DisplayMemberBinding="{Binding Id}"></GridViewColumn>
                    <GridViewColumn Header="Name" Width="100" DisplayMemberBinding="{Binding Name}"></GridViewColumn>
                    <GridViewColumn Header="Age" Width="80" DisplayMemberBinding="{Binding Age}"></GridViewColumn>
                </GridView>
            </ListView.View>
        </ListView>
        private void BtnTest_OnClick(object sender, RoutedEventArgs e)
        {
            List<Student> stuList=new List<Student>()
                {
                    new Student(){Id = 0,Name = "Tim",Age=10},
                    new Student(){Id = 1,Name = "Tom",Age = 11},
                    new Student(){Id = 2,Name = "Tony",Age = 12},
                    new Student(){Id = 3,Name = "Jim",Age = 13}
                };
            this.listViewStudents.ItemsSource = from student in stuList
                                                where student.Name.StartsWith("T")
                                                select student;

        }

 8、使用ObjectDataProvider对象作为BindingSource:就是将对象作为数据源提供给Binding

理想的情况下,上游程序员把类ishejihao、使用属性把数据暴露出来,下游程序员把这些类的实例作为Binding的Source、把属性作为Binding的Path来消费这些类。但是很难保证一个类idea所有数据都能使用属性来暴露,比如我们需要的数据可能是方法的返回值。而重新设计底层类的风险和成本会比较高,况且黑盒引用类库的情况下我们也不可能更改已经编译好的类,这时就要使用ObjectDataProvider来包装作为Binding的源数据对象了。 

    class Calculator
    {
        public string Add(string str1, string str2)
        {
            double x,y,z = 0;
            if (double.TryParse(str1, out x) && double.TryParse(str2, out y))
            {
                z = x + y;
                return z.ToString();
            }
            else
            {
                return "Inpurt Error!";
            }
        }
    }
        private void BtnTest_OnClick(object sender, RoutedEventArgs e)
        {
            ObjectDataProvider odp=new ObjectDataProvider();
            odp.ObjectInstance = new Calculator();
            odp.MethodName = "Add";
            odp.MethodParameters.Add("100");
            odp.MethodParameters.Add("200");
            MessageBox.Show(odp.Data.ToString());
        }

在XAML中实现

    public partial class MainWindow : Window
    {
        public MainWindow ( )
        {
            InitializeComponent ( );
            this.SetBinding();
        }

        private void SetBinding()
        {
            //创建并配置ObjectDataProvider对象
            ObjectDataProvider odp=new ObjectDataProvider();
            odp.ObjectInstance = new Calculator();
            odp.MethodName = "Add";
            odp.MethodParameters.Add("10");
            odp.MethodParameters.Add("10");

            //以ObjectDataProvider对象为Source创建Binding
            Binding bindingToStr1=new Binding("MethodParameters[0]")
                {
                    Source = odp,
                    BindsDirectlyToSource=true,
                    UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged
                };

            Binding bindingToStr2=new Binding("MethodParameters[1]")
                {
                    Source = odp,
                    BindsDirectlyToSource = true,
                    UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged
                };

            Binding bindingToResult=new Binding("."){Source = odp};

            //将Binding关联到UI元素上
            this.TextBoxStr1.SetBinding(TextBox.TextProperty, bindingToStr1);
            this.TextBoxStr2.SetBinding(TextBox.TextProperty, bindingToStr2);
            this.TextBoxResult.SetBinding(TextBox.TextProperty, bindingToResult);
        }

9、绑定对数据的转换和校验
Binding的作用就是在Source与Target之间建立桥梁,在这座桥上可以设置关卡对数据的有效性进行校验,当Binding两端要求使用不同的数据类型时,我们还可以为数据设置转换器。

Binding用于数据有效性校验的关卡

 

 

 

 

 

 

 

 

 

 

 

 

posted @ 2014-02-21 09:17  种花生的读书人  阅读(329)  评论(0编辑  收藏  举报

该博客仅作为记录笔记,转载随意