UserControl 类 是C# winform 中自定义控件的基类
所有自定义控件都要继承UserControl
自定义组件结构分为
组件属性 组件事件 和 组件方法
一般来说 在无参构造方法中 调用 InitializeComponent后 就可以执行 自己的初始化(InitializeComponent 后可认为组件已经渲染完毕)
之后一般是执行属性设置和事件绑定
UserControl 类 是C# winform 中自定义控件的基类
所有自定义控件都要继承UserControl
自定义组件结构分为
组件属性 组件事件 和 组件方法
一般来说 在无参构造方法中 调用 InitializeComponent后 就可以执行 自己的初始化(InitializeComponent 后可认为组件已经渲染完毕)
之后一般是执行属性设置和事件绑定
Point screenPoint = 控件.PointToScreen(new Point()); Rectangle rect = new Rectangle(screenPoint, chartCellLine.Size); Image img = new Bitmap(rect.Width, rect.Height); Graphics g = Graphics.FromImage(img); g.CopyFromScreen(rect.X - 1, rect.Y - 1, 0, 0, rect.Size); img.Save(strPicPath, System.Drawing.Imaging.ImageFormat.Jpeg);
C# 异步编程语法 加入async await 关键字
标记了 async 的方法会返回 Task
task 就是一个任务异步执行的对象
如果 想解耦task 获取task的内容 通过关键字 await 可以直接获取内容 但是加了await关键字 方法也要标记为async
另一种方法在同步方法内等待 异步执行 结果可以通过 Task.GetAwaiter().GetResult() 同步等待结果
详解Task 方法
异步执行 并返回Task对象(会创建线程)
Task.Run(()=>{})
并向执行 同步等待 (等待所有Task执行结束 且返回一个数组结果)
Task.wennAll(Task[])
下面我演示一下使用数据绑定的情况会如何
Form2的界面和Form1的界面一致,你可以重复你在Form1中所做的事情,看看会怎么样。注意一点,当你修改了控件中的数据后请将鼠标移动到其他的控件上去,以便数据绑定机制可以帮你做数据同步的事情。
Form2的代码:
1using System;
2using System.Collections.Generic;
3using System.ComponentModel;
4using System.Data;
5using System.Drawing;
6using System.Text;
7using System.Windows.Forms;
8
9namespace SimplyDataBinding
10{
11 public partial class Form2 : Form
12 {
13 Person person;
14 public Form2()
15 {
16 InitializeComponent();
17 person = new Person(“Cai”, “Peng”, 32);
18 this.txtFirstname1.DataBindings.Add(“Text”, person, “Firstname”);
19 this.txtLastname1.DataBindings.Add(“Text”, person, “Lastname”);
20 this.txtAge1.DataBindings.Add(“Text”, person, “Age”);
21 this.txtFirstname2.DataBindings.Add(“Text”, person, “Firstname”);
22 this.txtLastname2.DataBindings.Add(“Text”, person, “Lastname”);
23 this.txtAge2.DataBindings.Add(“Text”, person, “Age”);
24
25 }
26 }
27}
你们应该可以发现这段代码和Form1中的代码的区别了吧。以上的代码是以数据绑定的方式完成的。后面我将详细解释这些代码。
你一定发现了,如果你做上述相同的操作的时候两个相应的TextBox始终会保持一致的数据。而这一切的发生完全是自动的,这要得意于数据绑定了。
该解释一下了。
this.txtFirstname1.DataBindings.Add(“Text”, person, “Firstname”);
仔细看看,你能发现,”Text” 是不是就是TextBox的Text属性。person 就是数据源,而”Firstname” 是person对象的属性。从这里就可以看的出来,以上的代码是想把Text属性和person中的Firstname属性关联在一起。这种关联就会导致数据的自动呈现和自动接受更改。本质上而言,数据绑定基本的思路就将数据源中的某个属性和控件中的某个属性关联在一起,使得数据绑定引擎能为你去处理多余的事情。
下面说点复杂点东西。数据源的属性和控件中的属性关联其实是由一个类来管理的,那个类就是Binding类。上面的代码也可以变成一下的形式
Binding binding = new Binding(“Text”, person, “FirstName”);
this.txtFirstname1.DataBindings.Add(binding);
Binding代表某对象属性值和某控件属性值之间的简单绑定。
第一参数是:控件的某个属性的名称表示。
第二参数是:对象
第三参数是:表示绑定的对象的属性的数据绑定表达式。通常情况下是属性的名称。
上一篇中,我介绍了大致介绍了一下数据绑定实现的原理和见识了一下简单绑定。继续介绍,简单绑定还没完全说清楚。Binding对象是简单绑定中比较重要的一个类。Binding创建和维护某控件的属性与某对象的属性或对象列表中当前对象的属性之间的简单绑定。这是MSDN上的描述。上一篇中你们认识到了,绑定其实就是控件属性和对象数据之间的关联。在MSDN的描述中也说明了这一点。
我们可以建立一个Binding对象来表示这个关联,一下的代码你们已经见到过了.Binding binding = new Binding(“Text”, person, “FirstName”);
this.txtFirstname1.DataBindings.Add(binding);
我已经大概解释了Binding的构造函数中每个参数的含义。 现在具体来说明一下。
第一参数:你要指定一个你控件的属性(如:Text, Background, ForeColor等).
第二参数:你要指定任意一个类的实例最为数据源。可能的数据源包括:
任意对象和一下列举的对象。
说明 | C# 示例 |
---|---|
实现 IBindingList 或 ITypedList 的任何类。包括:DataSet、DataTable、DataView 或 DataViewManager。 | DataSet ds = new DataSet(“myDataSet”); |
实现 IList 以创建对象索引集合的任意类。必须在创建 Binding 之前创建和填充该集合。列表中的所有对象必须为同一类型;否则将引发异常。 | ArrayList ar1 = new ArrayList; Customer1 cust1 = new Customer(“Louis”); ar1.Add(cust1); |
强类型对象的强类型 IList。 | Customer [] custList = new Customer[3]; |
以上是MSDN中列举的。
第三参数:需要指定的绑定表达式。(在前一文章中的例子中就是一个简单的属性的名称)。但并不是总是这样。我们可以考虑这样的一个例子。
比如我们又一个Employee类并且它拥有个一属性是City(表示所在的城市),而这个属性是一个City类型。City类中有一个属性是Name表示城市的名称。
在这种情况中。如果你想把Employee类的某个实例的City属性的城市名称绑定到一个TextBox中的话。我们就需要这样做:
1//其中employee表示Employee类的实例,而“City.Name”表示控件现在绑定的是employee的City属性的Name属性的值。
2this.textBox1.DataBindings.Add(“Text”,employee, “City.Name”); 下面我建立一个例子来说更多的关于数据绑定表达式的用法:(下面的例子只会讲解简单绑定中涉及到的内容)
1.建立City类
1using System;
2using System.Collections.Generic;
3using System.Text;
4
5namespace SimplyDataBinding2
6{
7 public class City
8 {
9 private string _postalcode;
10 /// <summary>
11 /// 获取或设置邮政编码。
12 /// </summary>
13 public string Postalcode
14 {
15 get { return _postalcode; }
16 set { _postalcode = value; }
17 }
18 private string _name;
19 /// <summary>
20 /// 获取或设置城市名称。
21 /// </summary>
22 public string Name
23 {
24 get { return _name; }
25 set { _name = value; }
26 }
27 public City() { }
28 public City(string postalcode, string name)
29 {
30 this.Postalcode = postalcode;
31 this.Name = name;
32 }
33 }
34}
35
2.建立Employee类
1using System;
2using System.Collections.Generic;
3using System.Text;
4
5namespace SimplyDataBinding2
6{
7 public class Employee
8 {
9 private string _name;
10 /// <summary>
11 /// 获取或设置姓名。
12 /// </summary>
13 public string Name
14 {
15 get { return _name; }
16 set { _name = value; }
17 }
18 private string _title;
19 /// <summary>
20 /// 获取或设置称呼。
21 /// </summary>
22 public string Title
23 {
24 get { return _title; }
25 set { _title = value; }
26 }
27 private City _city;
28 /// <summary>
29 /// 获取或设置所在城市。
30 /// </summary>
31 public City City
32 {
33 get { return _city; }
34 set { _city = value; }
35 }
36 public Employee() { }
37 public Employee(string name, string title, City city)
38 {
39 this.Name = name;
40 this.Title = title;
41 this.City = city;
42 }
43 }
44}
45
请注意31行,我声明了一个City属性,它是一个City类型。
Form1.CS
1using System;
2using System.Collections.Generic;
3using System.ComponentModel;
4using System.Data;
5using System.Drawing;
6using System.Text;
7using System.Windows.Forms;
8
9namespace SimplyDataBinding2
10{
11 public partial class Form1 : Form
12 {
13 public Form1()
14 {
15 InitializeComponent();
16 Employee employee = new Employee(“Caipeng”, “Mr”, new City(“430071”, “Wuhan”));
17 this.textBox1.DataBindings.Add(“Text”, employee, “Name”, true);
18 this.textBox2.DataBindings.Add(“Text”, employee, “Title”, true);
19 //注意这里的数据绑定表达式。City属性本身是指向City类的一个实例的。所以它没有办法被解析为能
20 //直接显示在Text中的数据。一下的表达式所要表达的是我需要那个City实例的Name属性。
21 this.textBox3.DataBindings.Add(“Text”, employee, “City.Name”, true);
22 }
23 }
24}
我已经在代码做了说明。你可以自己看看。上面我重点的简介了一下数据绑定表达是的作用。接下来我们去更深入的认识一下Binding的作用。
如果你们留意了的话,你会发现你如果你的属性即使不是字符串类型一样可以很好的绑定到TextBox的Text。数据绑定在这里又发功,它负责将类型进行了转换。不过只有一些基本类型能够进行转换。如果你需要将二进制数据转换成Image类型,那你就的处理Binding类中的两个事件。Format和Parse事件。我们通常使用这两个事件来进行数据的转换。
Format 事件:当数据被绑定到控件的时候触发。就是在数据要显示到控件中的时候。
Parse 事件:当控件的属性发生改变而导致要更新数据源的时候触发。
这两个事件是相对的,分别管理这从数据源到控件和控件到数据源的两个过程的数据格式转换的问题。
看例子:
我把上面的例子做个修改:
1using System;
2using System.Collections.Generic;
3using System.ComponentModel;
4using System.Data;
5using System.Drawing;
6using System.Text;
7using System.Windows.Forms;
8
9namespace SimplyDataBinding2
10{
11 public partial class Form2 : Form
12 {
13 Employee employee;
14 public Form2()
15 {
16 InitializeComponent();
17 employee = new Employee(“Caipeng”, “Mr”, new City(“430071”, “Wuhan”));
18 this.textBox1.DataBindings.Add(“Text”, employee, “Name”, true);
19 this.textBox2.DataBindings.Add(“Text”, employee, “Title”, true);
20 //注意这里的数据绑定表达式是”City”。它是可以能够直接显示在Text属性但绝对不是你想要的值。
21 Binding cityBinding = new Binding(“Text”, employee, “City”);
22 //你可以屏蔽一下的两句话来看看Text中到底会显示什么。
23 cityBinding.Format += new ConvertEventHandler(cityBinding_Format);
24 cityBinding.Parse += new ConvertEventHandler(cityBinding_Parse);
25 this.textBox3.DataBindings.Add(cityBinding);
26 }
27
28 string tempPostalcode = null;
29 /// <summary>
30 /// 当控件更新数据源的时候触发。
31 /// </summary>
32 /// <param name=”sender”></param>
33 /// <param name=”e”></param>
34 void cityBinding_Parse(object sender, ConvertEventArgs e)
35 {
36 //e.value包含了控件中相应属性的值。
37 City city = new City(tempPostalcode, e.Value.ToString());
38 e.Value = city;
39 }
40
41 /// <summary>
42 /// 当数据被绑定到控件上的时候触发。
43 /// </summary>
44 /// <param name=”sender”></param>
45 /// <param name=”e”></param>
46 void cityBinding_Format(object sender, ConvertEventArgs e)
47 {
48 //我将City类型转换成我想要的城市信息。
49 //e.Value 包含的是数据源中相应属性的值。这里是一个City类型的实例。
50 City temp = (City)e.Value;
51 tempPostalcode = temp.Postalcode;
52 e.Value = temp.Name;
53 }
54
55 private void button1_Click(object sender, EventArgs e)
56 {
57 //利用它来查看转换的结果是否正确。
58 MessageBox.Show(employee.City.Postalcode + ” ” + employee.City.Name);
59 }
60 }
61}
从表现上看,和第一个例子没有什么区别,但实际实现的方式是不同的。请注意第21行和第一个例子中的21行是不同。
第一个例子
this.textBox3.DataBindings.Add(“Text”, employee, “City.Name”, true);
第二个例子
Binding cityBinding = new Binding(“Text”, employee, “City”);
//你可以屏蔽一下的两句话来看看Text中到底会显示什么。
cityBinding.Format += new ConvertEventHandler(cityBinding_Format);
cityBinding.Parse += new ConvertEventHandler(cityBinding_Parse);
this.textBox3.DataBindings.Add(cityBinding); 各位应该可以看的出区别了。
到上一篇文章结束,你应该知道简单绑定的原理了及Binding对象在简单绑定起到的作用。如果你留心的话,你会发现我在上一篇中留下了一个列表。其中列举了能够被数据绑定支持的“数据源”都有那些。其中主要包含单一对象、集合及集合中包含集合这样的三大类别的对象。其中在简单绑定中,我已经描述了如何绑定对象到控件中。而绑定到集合和集合中的集合就需要了解复杂绑定了。
复杂绑定可以简单的理解为如何将集合或集合的集合绑定到控件的过程。在这个过程中,你对发现有某些地方和简单绑定很相似,但实际上当控件绑定到集合类的对象的时候和我们认识的简单绑定的过程是有区别的。复杂绑定是依赖与简单绑定过程的。先来看看下面的例子:
项目包含两个部分:Person类和Form类。
Person类的代码:
1using System;
2using System.Collections.Generic;
3using System.Text;
4
5namespace ComplexBinding
6{
7 //将要用于集合中的项对象。
8 class Person
9 {
10 private string _name;
11
12 public string Name
13 {
14 get { return _name; }
15 set { _name = value; }
16 }
17 private int _age;
18
19 public int Age
20 {
21 get { return _age; }
22 set { _age = value; }
23 }
24 //请保留这个构造函数。
25 public Person() { }
26
27 public Person(string name, int age)
28 {
29 this.Name = name;
30 this.Age = age;
31 }
32 }
33}
34
Form1的界面:
Form1.cs代码
1using System;
2using System.Collections.Generic;
3using System.ComponentModel;
4using System.Data;
5using System.Drawing;
6using System.Text;
7using System.Windows.Forms;
8
9namespace ComplexBinding
10{
11 public partial class Form1 : Form
12 {
13 public Form1()
14 {
15 InitializeComponent();
16 //建立一个集合,让它维持多个Person对象。
17 List<Person> list = new List<Person>();
18 list.Add(new Person(“Caipeng”, 32));
19 list.Add(new Person(“WangLiLi”, 30));
20 list.Add(new Person(“Colin.Cai”, 0));
21 this.txtName.DataBindings.Add(“Text”, list, “Name”);
22 this.txtAge.DataBindings.Add(“Text”, list, “Age”);
23 }
24
25
26 }
27}
看看上面的代码中的21行和22行。你会发现两个TextBox中指定的数据源都是list(集合),在前面我讲过了,在此之后的那个参数一般是对象的属性名称。可list哪来的Name和Age属性呢?我在这里指定的”Name” 和 “Age”确实是属性,不过是Person对象中的属性。是不是满是疑问。其中可能的两个疑问是
1.TextBox怎么能显示一个集合呢,是不是数据绑定老人家搞错了?
2.list中没有Name和Age属性呀!!!就算指定的是list中维持的某个对象的属性,那应该关联的是第一个还是第二个呢??
要确定的第一件事情,集合确实无法显示在TextBox这样的控件中。它可以很好的显示在类似DataGridView这样的控件中。其次要确定的事情是,两个TextBox中显示的数据确实来自list,但准确的说是来自list中包含的某一个Person对象。如果你现在就运行这个程序的话,你会发现两个TextBox中显示的数据是list中的第一个Person对象的Name和Age的值。
我要解释一下,当数据源是一个集合的时候,到底发生了什么。
首先,当数据绑定机制发现你绑定的数据源是一个集合,那么它会采取一种不同简单绑定的方式来对待这个数据源。如果是绑定到的是TextBox这样的控件话,它会先从那个集合中猎取一项出来。在这个例子里就是第一个person对象。
接下来,数据绑定机制会将这个对象中的属性和控件中的属性关联起来(和简单绑定中所描述的一样。)
再接下来,我不能欺骗大家没有“再接下来”了。上面的两个步骤就已经完成了一个复杂绑定。
谁负责处理以上的过程呢?是BindingContext类,它管理着这个过程。(如果我的理解能力没有问题的话)
没有你们想象中的复杂吧!简单到你想骂我。别骂,之所以这样简单只因为我还没有讲到难的地方。不管怎样,你已经认识了复杂绑定了。
接下来我们将上面的例子做进一步的修改。看看下面的代码:
Form1界面:
Form1的代码:
1using System;
2using System.Collections.Generic;
3using System.ComponentModel;
4using System.Data;
5using System.Drawing;
6using System.Text;
7using System.Windows.Forms;
8
9namespace ComplexBinding
10{
11 public partial class Form1 : Form
12 {
13 CurrencyManager cm;
14 public Form1()
15 {
16 InitializeComponent();
17 //建立一个集合,让它维持多个Person对象。
18 List<Person> list = new List<Person>();
19 list.Add(new Person(“Caipeng”, 32));
20 list.Add(new Person(“WangLiLi”, 30));
21 list.Add(new Person(“Colin.Cai”, 0));
22 this.txtName.DataBindings.Add(“Text”, list, “Name”);
23 this.txtAge.DataBindings.Add(“Text”, list, “Age”);
24 //增加以下的语句,获得管理集合的管理对象.
25 //下面的两个button演示cm如果管理集合的简单方法.
26 cm = (CurrencyManager)this.BindingContext[list];
27 }
28
29 private void button2_Click(object sender, EventArgs e)
30 {
31 //将当前的位置++
32 cm.Position++;
33 }
34
35 private void button1_Click(object sender, EventArgs e)
36 {
37 //将当前的位置–
38 cm.Position–;
39 }
40
41
42 }
43}
可先看看上面的例子的效果。你会发现当你点击Button2或Button1的时候,两个控件中的数据会发生相应的变化。我想从这个例子证明我以上描述复杂绑定的过程是正确的并且也想从这个例子衍生出另一个我们值得关注的问题。那就是你不得不去认识的一个BindingContext类,如果你想深入的了解数据绑定机制的话。
下一篇中我将进一步对数据绑定进行解释。
前面章节中,对简单绑定和复杂绑定做了简单的描述。在这章中,我们将所有的这些东西串在一起看看数据绑定的全貌。
Binding对象:代表某对象属性值和某控件属性值之间的简单绑定。其主要负责将控件的属性和对象的属性进行关联。
BindingManagerBase:管理绑定到相同数据源和数据成员的所有 Binding 对象。 这个对象在前面的章节中没有涉及,但实际上不管是简单绑定还是复杂绑定中都使用到了这个对象的相应的派生类。
BindingContext对象: 负责管理从Control类继承的任意对象的 BindingManagerBase对象集合只要发生数据绑定,那在一个FORM中就一定存在一个BindingContext对象。我们可以通过Form对象BindingContext属性获得一个BindingContext对象。
这三个对象掌管着数据绑定的主要功能。我们先来看看其关系:
1. Binding对象负责将控件的属性和对象的属性关联起来。对象的属性值会被自动传递个控件的属性,而控件的属性值更改后也会直接传回对象的属性(双向绑定)。
2. 在一个WinForm界面中总是会存在多个控件。所以,一般都会有一组Binding对象来管理不同控件中的属性和相同数据源中属性的关联关系。为了能方便的管理这样的一组Binding对象,我们使用继承至BindingManagerBase的子对象进行管理工作。BindingManagerBase有两个子类:PropertyManager和CurrencyManager.
其中:PropertyManager : 维护对象的属性与数据绑定控件属性之间的 Binding。(见简单绑定的描述)
CurrencyManager : 管理 Binding 对象的列表。管理列表或集合类型的数据源对象。(见复杂绑定的描述)
无论是PropertyManager还是CurrencyManager总是和一个数据源对象的对应的。也就是说,一个特定的数据源对象(无论是单一对象还是集合类对象)都会有一个对应的BindingManagerBase的子对象。
3.对同一窗体而言,通常都会面对多个数据源而不是一个。所以,也就会产生多个PropertyManager或CurrencyManager对象。BindingContext就主要负责管理多个BindingManagerBase的子对象。BindingContext可以通过窗体的BindingContext属性获得。它是一个字典的集合。
为了更好的说明这三类对象之间的关系,请查看下图。
上面图例表明了一下几件事情:
1.当你的窗体中绑定了3个不同的数据源,数据绑定机制就会自动产生三个对应的BindingManagerBase的子对象与数据源对象对应。其实更为准确的说法是,如果你的窗体绑定了三个不同的对象,那么就会产生三个独立的BindingManagerBase的子对象与其对应。至于是产生PropertyManager还是CurrencyManager就要取决与你绑定的数据源是单一对象还是列表(集合)对象了。上图说明了这一个点,如果是绑定的是单一对象就会产生PorpertyManager,而如果是列表(集合)对象就会产生一个CurrencyManager。
2. PropertyManager主要管理一组相关的Binding对象,而CurrencyManager主要管理着相应的对象集合(列表对象)。两个对象管理的侧重点不同,一个主要管理数据绑定的基础对象Binding,而一个主要管理数据绑定的后端数据源对象集合。
比如CurrencyManager可以每次从集合对象中猎取一个对象然后将其绑定的到窗体控件中去,它也可以在集合对象中进行导航。或也可以新增新的对象集合中,等等。
3.因为对于同一窗体而言,可能绑定到多个数据源也就会产生多个“管理者”,而每一个数据源都会对应一个独立的“管理者”。所以我们可以通过窗体的BindingContext对象获得某个特定数据源对应的“管理者”。
比如:窗体中的数据源是HumanList(集合),那么当窗体和这个集合发生绑定后,我们就可以通过一下的方式获得一个CurrencyManager对象。Code
其注意以上的第9行的语句this.BindingContext[list]。其中窗体有一个BindingContext属性,它在发生数据绑定后会自动维持一个key/value的集合,这个集合是一数据源对象作为KEY的,我们可以通过指定特定的数据源对象找到相应的“管理者”。注意前面我已经说过对于每个数据源而言都会有一个对应的“管理者”。上面的代码中应为数据源是集合类型所以会产生一个CurrencyManager对象。
现在可以做个小结:BindingContext类维持一组可能BindingManagerBase对象,而这些BindingManagerBase对象和数据源对象又是一一对应的。
复杂的讲解到此,下面我就做一个实际的例子来体会一下上面的描述。
1.首先:我们创建一个自定义的对象:Person类
using System;
using System.Collections.Generic;
using System.Text;
namespace AppBindingContext
{
public class Person
{
private string _lastName;
public string LastName
{
get { return _lastName; }
set { _lastName = value; }
}
private string _firstName;
public string FirstName
{
get { return _firstName; }
set { _firstName = value; }
}
private int _age;
public int Age
{
get { return _age; }
set { _age = value; }
}
public Person() { }
public Person(string lastName, string firstName, int age)
{
this.LastName = lastName;
this.FirstName = firstName;
this.Age = age;
}
}
}
2. 我们建立一个Winform界面,然后使用List<T>作为集合来保存多个Person对象。
1 using System;
2 using System.Collections.Generic;
3 using System.ComponentModel;
4 using System.Data;
5 using System.Drawing;
6 using System.Text;
7 using System.Windows.Forms;
8
9 namespace AppBindingContext
10 {
11 public partial class Form1 : Form
12 {
13 //声明一个管理者
14 private CurrencyManager cm;
15 public Form1()
16 {
17 InitializeComponent();
18 //建立数据源集合.
19 List<Person> list = new List<Person>();
20 list.Add(new Person(“LastName1”, “FirstName1”, 30));
21 list.Add(new Person(“LastName2”, “FirstName2”, 31));
22 list.Add(new Person(“LastName3”, “FirstName3”, 32));
23 list.Add(new Person(“LastName4”, “FirstName4”, 33));
24 //数据绑定
25 this.textBox1.DataBindings.Add(“Text”, list, “LastName”);
26 this.textBox2.DataBindings.Add(“Text”, list, “FirstName”);
27 this.textBox3.DataBindings.Add(“Text”, list, “Age”);
28 this.dataGridView1.DataSource = list;
29 //通过指定的数据源对象获得相应的CurrencyManager对象.
30 cm = (CurrencyManager)this.BindingContext[list];
31 }
32 //点击此按钮可以导航到上一条记录
33 private void button1_Click(object sender, EventArgs e)
34 {
35 cm.Position–;
36 }
37 //点击此按钮可以导航到下一条记录
38 private void button2_Click(object sender, EventArgs e)
39 {
40 cm.Position++;
41 }
42 }
43 }
先下载上面的代码看看,体会一下CurrencyManager的作用.你也可以看看CurrencyManager的其他的方法和属性,其实大致就可以明白这个对象是做什么用.
它公布出来的很多方法其实都是用于操作集合用的.然而这种"操作"是依赖数据绑定完成的,而不是单纯的为了操作集合而操作.
前面章节中,对简单绑定和复杂绑定做了简单的描述。在这章中,我们将所有的这些东西串在一起看看数据绑定的全貌。
Binding对象:代表某对象属性值和某控件属性值之间的简单绑定。其主要负责将控件的属性和对象的属性进行关联。
BindingManagerBase:管理绑定到相同数据源和数据成员的所有 Binding 对象。 这个对象在前面的章节中没有涉及,但实际上不管是简单绑定还是复杂绑定中都使用到了这个对象的相应的派生类。
BindingContext对象: 负责管理从Control类继承的任意对象的 BindingManagerBase对象集合只要发生数据绑定,那在一个FORM中就一定存在一个BindingContext对象。我们可以通过Form对象BindingContext属性获得一个BindingContext对象。
这三个对象掌管着数据绑定的主要功能。我们先来看看其关系:
1. Binding对象负责将控件的属性和对象的属性关联起来。对象的属性值会被自动传递个控件的属性,而控件的属性值更改后也会直接传回对象的属性(双向绑定)。
2. 在一个WinForm界面中总是会存在多个控件。所以,一般都会有一组Binding对象来管理不同控件中的属性和相同数据源中属性的关联关系。为了能方便的管理这样的一组Binding对象,我们使用继承至BindingManagerBase的子对象进行管理工作。BindingManagerBase有两个子类:PropertyManager和CurrencyManager.
其中:PropertyManager : 维护对象的属性与数据绑定控件属性之间的 Binding。(见简单绑定的描述)
CurrencyManager : 管理 Binding 对象的列表。管理列表或集合类型的数据源对象。(见复杂绑定的描述)
无论是PropertyManager还是CurrencyManager总是和一个数据源对象的对应的。也就是说,一个特定的数据源对象(无论是单一对象还是集合类对象)都会有一个对应的BindingManagerBase的子对象。
3.对同一窗体而言,通常都会面对多个数据源而不是一个。所以,也就会产生多个PropertyManager或CurrencyManager对象。BindingContext就主要负责管理多个BindingManagerBase的子对象。BindingContext可以通过窗体的BindingContext属性获得。它是一个字典的集合。
为了更好的说明这三类对象之间的关系,请查看下图。
上面图例表明了一下几件事情:
1.当你的窗体中绑定了3个不同的数据源,数据绑定机制就会自动产生三个对应的BindingManagerBase的子对象与数据源对象对应。其实更为准确的说法是,如果你的窗体绑定了三个不同的对象,那么就会产生三个独立的BindingManagerBase的子对象与其对应。至于是产生PropertyManager还是CurrencyManager就要取决与你绑定的数据源是单一对象还是列表(集合)对象了。上图说明了这一个点,如果是绑定的是单一对象就会产生PorpertyManager,而如果是列表(集合)对象就会产生一个CurrencyManager。
2. PropertyManager主要管理一组相关的Binding对象,而CurrencyManager主要管理着相应的对象集合(列表对象)。两个对象管理的侧重点不同,一个主要管理数据绑定的基础对象Binding,而一个主要管理数据绑定的后端数据源对象集合。
比如CurrencyManager可以每次从集合对象中猎取一个对象然后将其绑定的到窗体控件中去,它也可以在集合对象中进行导航。或也可以新增新的对象集合中,等等。
3.因为对于同一窗体而言,可能绑定到多个数据源也就会产生多个“管理者”,而每一个数据源都会对应一个独立的“管理者”。所以我们可以通过窗体的BindingContext对象获得某个特定数据源对应的“管理者”。
比如:窗体中的数据源是HumanList(集合),那么当窗体和这个集合发生绑定后,我们就可以通过一下的方式获得一个CurrencyManager对象。Code
其注意以上的第9行的语句this.BindingContext[list]。其中窗体有一个BindingContext属性,它在发生数据绑定后会自动维持一个key/value的集合,这个集合是一数据源对象作为KEY的,我们可以通过指定特定的数据源对象找到相应的“管理者”。注意前面我已经说过对于每个数据源而言都会有一个对应的“管理者”。上面的代码中应为数据源是集合类型所以会产生一个CurrencyManager对象。
现在可以做个小结:BindingContext类维持一组可能BindingManagerBase对象,而这些BindingManagerBase对象和数据源对象又是一一对应的。
复杂的讲解到此,下面我就做一个实际的例子来体会一下上面的描述。
1.首先:我们创建一个自定义的对象:Person类
using System;
using System.Collections.Generic;
using System.Text;
namespace AppBindingContext
{
public class Person
{
private string _lastName;
public string LastName
{
get { return _lastName; }
set { _lastName = value; }
}
private string _firstName;
public string FirstName
{
get { return _firstName; }
set { _firstName = value; }
}
private int _age;
public int Age
{
get { return _age; }
set { _age = value; }
}
public Person() { }
public Person(string lastName, string firstName, int age)
{
this.LastName = lastName;
this.FirstName = firstName;
this.Age = age;
}
}
}
2. 我们建立一个Winform界面,然后使用List<T>作为集合来保存多个Person对象。
1 using System;
2 using System.Collections.Generic;
3 using System.ComponentModel;
4 using System.Data;
5 using System.Drawing;
6 using System.Text;
7 using System.Windows.Forms;
8
9 namespace AppBindingContext
10 {
11 public partial class Form1 : Form
12 {
13 //声明一个管理者
14 private CurrencyManager cm;
15 public Form1()
16 {
17 InitializeComponent();
18 //建立数据源集合.
19 List<Person> list = new List<Person>();
20 list.Add(new Person(“LastName1”, “FirstName1”, 30));
21 list.Add(new Person(“LastName2”, “FirstName2”, 31));
22 list.Add(new Person(“LastName3”, “FirstName3”, 32));
23 list.Add(new Person(“LastName4”, “FirstName4”, 33));
24 //数据绑定
25 this.textBox1.DataBindings.Add(“Text”, list, “LastName”);
26 this.textBox2.DataBindings.Add(“Text”, list, “FirstName”);
27 this.textBox3.DataBindings.Add(“Text”, list, “Age”);
28 this.dataGridView1.DataSource = list;
29 //通过指定的数据源对象获得相应的CurrencyManager对象.
30 cm = (CurrencyManager)this.BindingContext[list];
31 }
32 //点击此按钮可以导航到上一条记录
33 private void button1_Click(object sender, EventArgs e)
34 {
35 cm.Position–;
36 }
37 //点击此按钮可以导航到下一条记录
38 private void button2_Click(object sender, EventArgs e)
39 {
40 cm.Position++;
41 }
42 }
43 }
先下载上面的代码看看,体会一下CurrencyManager的作用.你也可以看看CurrencyManager的其他的方法和属性,其实大致就可以明白这个对象是做什么用.
它公布出来的很多方法其实都是用于操作集合用的.然而这种"操作"是依赖数据绑定完成的,而不是单纯的为了操作集合而操作.
Program 项目入口 且保存全局属性
Form loginForm 登录功能
Form MainFrame 主页面
opencv 用 opencvsharp
OLEDB 管理access数据库
CV.EqualizeHist 直方图均衡化 提高图片质量 重新分配颜色比例
所有C# 自定义windows form 控件继承自 UserControl 在design中设计界面 在partial class中定义自定义属性和行为
注解解析
[Category("Text"),
Browsable(true),
DefaultValue(typeof(Image), null),
Description("图标")]
public Image Icon { get; set; }
Category 对应分类
Browsable 对应是否能看到
然后还有默认值和描述
这个注解使代码和 设计界面能连通
组件生命周期
form 打开:
• Control.HandleCreated
• Control.BindingContextChanged
• Form.Load
• Control.VisibleChanged
• Form.Activated
• Form.Shown
from 关闭:
• Form.Closing
• Form.FormClosing
• Form.Closed
• Form.FormClosed
• Form.Deactivate
control ,change focus:(使用 tab ,shift + tab等等,或是调用Select,SelectNextControl,或是使用当前form的ActiveControl)
• Enter
• GotFocus
• Leave
• Validating
• Validated
• LostFocus
control ,change focus:(使用鼠标,或是通过调用Focus方法)
• Enter
• GotFocus
• LostFocus
• Leave
• Validating
• Validated
特别是load 事件 可以再InitializeComponent 绑定加载事件 然后 添加渲染后回调操作
添加自定义事件
public delegate void PageChangeDelegate();//
/// <summary>
/// 当前页改变时发生的事件
/// </summary>
[Description("当前页改变时发生的事件"), Category("分页设置")]
public event PageChangeDelegate PageChanged;
在具体的业务流程中调用
private void btnFirst_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{
//设置当前页
CurrentPage = 1;
//设置上一页、下一页是否可用以及当前页按钮字体颜色
SetBtnPrePageAndBtnNextPage();
//调用注册事件
if (PageChanged != null) PageChanged();
}
应用程序安装Newtonsoft.Json
序列化
JsonConvert.SerializeObject(o);
反序列化
JsonConvert.DeserializeObject<T>(jsonstr) ;
T为指定的泛型
反序列化为JObject
JObject.Parse(jsonstr)
C# 可以用dynamic作为动态类型的声明 然后程序可以访问其动态属性
dynamic keyAndValue = new T();
Console.WriteLine(keyAndValue.key)
其中key为动态属性 程序不知道keyAndValue对象是否有key属性
泛型类
using System;
using System.Collections.Generic;
namespace GenericApplication
{
public class MyGenericArray<T>
{
private T[] array;
public MyGenericArray(int size)
{
array = new T[size + 1];
}
public T getItem(int index)
{
return array[index];
}
public void setItem(int index, T value)
{
array[index] = value;
}
}
class Tester
{
static void Main(string[] args)
{
// 声明一个整型数组
MyGenericArray<int> intArray = new MyGenericArray<int>(5);
// 设置值
for (int c = 0; c < 5; c++)
{
intArray.setItem(c, c*5);
}
// 获取值
for (int c = 0; c < 5; c++)
{
Console.Write(intArray.getItem(c) + " ");
}
Console.WriteLine();
// 声明一个字符数组
MyGenericArray<char> charArray = new MyGenericArray<char>(5);
// 设置值
for (int c = 0; c < 5; c++)
{
charArray.setItem(c, (char)(c+97));
}
// 获取值
for (int c = 0; c < 5; c++)
{
Console.Write(charArray.getItem(c) + " ");
}
Console.WriteLine();
Console.ReadKey();
}
}
}
泛型方法
using System;
using System.Collections.Generic;
namespace GenericMethodAppl
{
class Program
{
static void Swap<T>(ref T lhs, ref T rhs)
{
T temp;
temp = lhs;
lhs = rhs;
rhs = temp;
}
static void Main(string[] args)
{
int a, b;
char c, d;
a = 10;
b = 20;
c = 'I';
d = 'V';
// 在交换之前显示值
Console.WriteLine("Int values before calling swap:");
Console.WriteLine("a = {0}, b = {1}", a, b);
Console.WriteLine("Char values before calling swap:");
Console.WriteLine("c = {0}, d = {1}", c, d);
// 调用 swap
Swap<int>(ref a, ref b);
Swap<char>(ref c, ref d);
// 在交换之后显示值
Console.WriteLine("Int values after calling swap:");
Console.WriteLine("a = {0}, b = {1}", a, b);
Console.WriteLine("Char values after calling swap:");
Console.WriteLine("c = {0}, d = {1}", c, d);
Console.ReadKey();
}
}
}