上一章筆者介紹了關於WinForm環境。這一章筆者將繼續講WinForm。只不過更加的面向開發了。事實就是在學習工具箱裡面的控件。對於WinForm開發來講,企業對他的要求並沒有那麼高。但是如果是游戲相關的話,不好意思!筆者覺得你可能選錯語言了。C++可能更合適你。有一點希望讀者們明白。下列講到的內容是筆者在開發用的。只能輔助你學習,卻不能成為教材。上一章講到WinForm開發就是JAVA的Awt和Swing編程。筆者在做Awt和Swing已經過去四年多了。如果記得沒有錯的話,在設計窗體的時候筆者常常會用到關於FlowLayout類,BorderLayout類,JPanel類等布局類。所以先讓我們看一下關於WinForm的布局吧。
WinForm布局說到布局,筆者只能先從屬性窗體切入了。記得大部分的控件都是從Control類這裡繼承過來的。而Control類中有倆個屬性跟布局有一定有關系。一個是Anchor屬性,一個是Dock屬性。
Anchor屬性的作用就是把控件綁定到其容器的邊緣並確定控件隨其容器一起調整大小。但是是按什麼樣的比列調整筆者也不是很清楚。MSDN也沒有找到相關的說法。只是說明控件隨其容器一起調整大小。下面就是Anchor屬性的值。
public enum AnchorStyles
{
// 摘要:
// 該控件未錨定到其容器的任何邊緣。
None = 0,
//
// 摘要:
// 該控件錨定到其容器的上邊緣。
Top = 1,
//
// 摘要:
// 該控件錨定到其容器的下邊緣。
Bottom = 2,
//
// 摘要:
// 該控件錨定到其容器的左邊緣。
Left = 4,
//
// 摘要:
// 該控件錨定到其容器的右邊緣。
Right = 8,
}
如果你設置Right的話,當你把容器往右邊拉的話,設置的控件大小也往右邊變。還是讓筆者舉一個例子吧。

從圖上可以看出筆者設置了四個值——上下左右。現在筆者就拖動一下看看他們的變化吧。

按扭是放在一個Panel類的容器裡面的。現在筆者拖動了Panel就是發現按扭跟著一起發現變化。
Dock屬性這個可以說是跟JAVA的BorderLayout類是一個樣子的。把容器分為上、下、左、右、中五大區。所以對應的Dock屬性的值也有五種。
1 public enum DockStyle
2 {
3 // 摘要:
4 // 該控件未停靠。
5 None = 0,
6 //
7 // 摘要:
8 // 該控件的上邊緣停靠在其包含控件的頂端。
9 Top = 1,
10 //
11 // 摘要:
12 // 該控件的下邊緣停靠在其包含控件的底部。
13 Bottom = 2,
14 //
15 // 摘要:
16 // 該控件的左邊緣停靠在其包含控件的左邊緣。
17 Left = 3,
18 //
19 // 摘要:
20 // 該控件的右邊緣停靠在其包含控件的右邊緣。
21 Right = 4,
22 //
23 // 摘要:
24 // 控件的各個邊緣分別停靠在其包含控件的各個邊緣,並且適當調整大小。
25 Fill = 5,
26 }
你們可以自己動手試一下。就是設置對應的值。就是可以零距離停靠所設置的區。算了。筆者設置一下TOP吧。看一下樣子吧。

上面倆個屬性的功能大概多了解了。接下就來講一下關於FlowLayoutPanel控件吧。JAVA的FlowLayout有一點類似。意為流動布局。而C#的這個叫流式布局面板。是一個容器。看一下下面的圖片吧。

我們可以看到,如果不夠位置放一個按扭的時候,會自動換行存放。FlowLayoutPanel控件裡面有一個叫FlowDirection屬性來設置流動的方向。是從左到右,是從上到下。這個嗎讀者們自行查看。
用戶定義控件在開發過程中,我們會發現官方提供的控件有時候並不能滿足我們自己的業務需求。這個時候怎麼辦呢?其實開發人員完全可以自己組裝一個控件。即是用戶定義控件。選中項目右擊》添加》用戶控件。


填寫對應的用戶控件的名稱之後,點擊添加按扭。visual studio就會切換到用戶控件的設計窗體界面,同時項目裡面會多出一個文件。如下


上面的項目結構的圖片了吧。我們可以看到多出一個叫ATextBox.cs的文件。文件前面的圖標跟窗體的圖標一點也不一樣子。可是你卻會發現操作卻跟窗體操作是一樣子。沒有錯。你可以把這個界面當作對窗體的操作就可以了。相信筆者不用多說了。該拖控件就拖吧。
如果你用心的話,可以點開他。這個時候你會發現真的有一點像呢?
筆者拖一個TextBox到ATextBox.cs裡面吧。然後在拖動改變一下ATextBox.cs的大小吧。如圖下。

好了。就這樣子吧。那麼要什麼用呢?雙擊Form1.cs窗體。這個時候我們會發現對應的工具箱裡面會出現一些細微的變化。

看到這裡。不用筆者多說拉到對應的窗體就可以了使用了。這裡要注意一點:如果你的用戶控件發生了代碼上的修改。就必須重新生成。這個時候拉出來才有可能是最新的。為了區別筆者把ATextBox.cs的背影色改為紅色。讓我們看一下吧。

讓我們切到後面的代碼吧。筆者在代碼裡面加了一個叫AText的屬性。如下
1 using System;
2 using System.Collections.Generic;
3 using System.ComponentModel;
4 using System.Drawing;
5 using System.Data;
6 using System.Linq;
7 using System.Text;
8 using System.Windows.Forms;
9
10 namespace WinFormExample
11 {
12 public partial class ATextBox : UserControl
13 {
14 public ATextBox()
15 {
16 InitializeComponent();
17 }
18 public string AText
19 {
20 set { this.textBox1.Text = value; }
21 get { return this.textBox1.Text; }
22 }
23 }
24 }
這個時候我們來看一下。對應他的屬性窗體裡面就會出現相應的屬性了。這個樣子就可以修改他的值了。

相信讀者們能明白筆者要表達的是什麼意思了吧。沒有錯。如果你寫了相關的事件。也會在他的屬性窗體裡面出現。那麼有時候如果不想讓他出現在屬性窗體裡面怎麼辦呢?這個時候我們就要看一個類了。UserControl類就是最好的學習了。按F12相看UserControl類。我們會發現一些注解。如Browsable、EditorBrowsable等。沒有錯。這些都是用於表示對應屬性在屬性窗體的行為。筆者就不用多說了吧。
[Browsable(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
[EditorBrowsable(EditorBrowsableState.Always)]
public override bool AutoSize { get; set; }
關於用戶控件筆者就寫到這裡。這部分的內容筆者在開發過程中也很少用到。但是如果是大型PC端的軟件開發。那一般都會用到。請讀者們自己查閱。
應用程序配置文件應用程序配置文件就是App.config。這是.NET自己創造一個以.config為擴展名的配置文件(事實上就是一個XML)。所以在讀取上可以不用XmlDocument類。當然你要是覺得不爽也可以用XmlDocument了。這並沒有多大影響。但是為了專業精神有時候筆者還是會用C#的讀取方式。那App.config是什麼呢?在開發WinForm一般都會用到他。你可以把對應的數據庫連接字符串放在App.config。也可以存放一些其他的信息。沒有錯。就是用於存放應用程序的配置信息。那麼C#自己提了一個叫ConfigurationManager類。這個類就是用於讀取App.config。不多說讓筆者舉例子吧。
ConfigurationManager類是在System.Configuration的dll裡面的。如果項目的引用裡面沒用的話。不好意思。請先引了一下。關於這一點相信《Java進擊C#——項目開發環境》足夠讓你了解到。然後,就可以創建一個App.config了。選項目右擊》添加》新建項。這個相信大家都明白了吧。

在常規裡面就可以找到對應的“應用程序配置文件”了。選擇“添加”。項目裡面就會出現App.config.內容如下。
<?xml version="1.0" encoding="utf-8" ?> <configuration> </configuration>
好了。我們要做的就是在App.config裡面增加相關的信息了。
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings>
<add key="Aomi" value="i am aomi"/>
</appSettings>
</configuration>
這個時候筆者就要去讀取相關的信息了。筆者在窗體裡面拖了一個Label控件和一個按扭控件。點擊按扭就把配置信息的值讀取出來。賦值給Label控件。

後面代碼
1 using System;
2 using System.Collections.Generic;
3 using System.ComponentModel;
4 using System.Data;
5 using System.Drawing;
6 using System.Linq;
7 using System.Text;
8 using System.Windows.Forms;
9 using System.Configuration;
10
11 namespace WinFormExample
12 {
13 public partial class Form1 : Form
14 {
15 public Form1()
16 {
17 InitializeComponent();
18 }
19
20 private void button1_Click(object sender, EventArgs e)
21 {
22 this.label1.Text = ConfigurationManager.AppSettings["Aomi"];
23 }
24 }
25 }
看到代碼了吧。就一句很簡單的話就可以了。讓我們看一下運行的情況吧。

關於App.config不只是筆者講的這些。還可以用於設置開發軟件的一些系統設置。這可就多了。是寫不完的。請筆者們自行查閱。
BackgroundWorker類這個類你可以理解為後台線程。在開發過程中,程序有很多不需用戶看到的工作。這些工作一般都可以交給後面線程去工作。而BackgroundWorker類就是最好的代表了。對於BackgroundWorker類本來筆者應該在多線程那章節一起講的。可是筆者想一下覺得放在這裡比較合適。WinForm的界面執行都是在一個叫UI線程裡面做的。那麼UI線程有一個特色就是後台線程是訪問不了UI線程裡面的控件。一般的訪都要用到一個叫Invoke方法。他是在Control類裡面的。所以不管是控件還是窗體都有這個方法。看一下下面的代碼吧。
1 using System;
2 using System.Collections.Generic;
3 using System.ComponentModel;
4 using System.Data;
5 using System.Drawing;
6 using System.Linq;
7 using System.Text;
8 using System.Windows.Forms;
9 using System.Configuration;
10 using System.Threading;
11
12 namespace WinFormExample
13 {
14 public partial class Form1 : Form
15 {
16 public Form1()
17 {
18 InitializeComponent();
19 }
20
21 private void button1_Click(object sender, EventArgs e)
22 {
23 this.label1.Text = ConfigurationManager.AppSettings["Aomi"];
24 }
25
26 private void Form1_Load(object sender, EventArgs e)
27 {
28 Thread myThread = new Thread(this.Trace);
29 myThread.Start();
30 }
31
32 public void Trace()
33 {
34 this.Invoke(new Action(() =>
35 {
36 this.label1.Text = "這是一個 Trace";
37 }));
38 }
39 }
40 }
如果把它去掉的話,就會發生異常。如下

筆者舉一個列子來說明一下BackgroundWorker類的用法吧。
1 using System;
2 using System.Collections.Generic;
3 using System.ComponentModel;
4 using System.Data;
5 using System.Drawing;
6 using System.Linq;
7 using System.Text;
8 using System.Windows.Forms;
9 using System.Configuration;
10 using System.Threading;
11
12 namespace WinFormExample
13 {
14 public partial class Form1 : Form
15 {
16 private BackgroundWorker _backgroudWorker;
17 public Form1()
18 {
19 InitializeComponent();
20 }
21
22 private void button1_Click(object sender, EventArgs e)
23 {
24 if (this._backgroudWorker == null)
25 {
26 this._backgroudWorker = new BackgroundWorker();
27 this._backgroudWorker.WorkerSupportsCancellation = true;
28 this._backgroudWorker.WorkerReportsProgress = true;
29 this._backgroudWorker.DoWork += BackgroudWorker_DoWork;
30 this._backgroudWorker.ProgressChanged += BackgroudWorker_ProgressChanged;
31 this._backgroudWorker.RunWorkerCompleted += BackgroudWorker_RunWorkerCompleted;
32 }
33
34 this._backgroudWorker.RunWorkerAsync();
35
36 }
37
38 private void BackgroudWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
39 {
40 this.progressBar1.Value = e.ProgressPercentage;
41 }
42
43 private void BackgroudWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
44 {
45 this.label1.Text = e.Result.ToString();
46 }
47
48 private void BackgroudWorker_DoWork(object sender, DoWorkEventArgs e)
49 {
50 string value = ConfigurationManager.AppSettings["Aomi"];
51 e.Result = value;
52
53 for (int i = 0; i < 100; i++)
54 {
55 this._backgroudWorker.ReportProgress(i);
56 Thread.Sleep(200);
57 }
58
59 }
60
61 }
62 }
運行結果

BackgroundWorker類把後台和前台分開。並且又可以在後台執行過程中通知前台。我們可以看到BackgroundWorker類還是很強大的。同時也可以不用Invoke方法。
WorkerSupportsCancellation屬性:用於表示是否可以在運行中取消工作。
WorkerReportsProgress屬性:用於表示是否可以在運行中通知前台。
DoWork事件:後台線程
ProgressChanged事件:前台UI線程。用於後台線程中通知的時候執行。
RunWorkerCompleted事件:前台UI線程。用於後台線程結束之後執行。
RunWorkerAsync方法:開始異步運行。
本章總結本章主要是講到關於筆者在開發過程中用到的幾個知識點。也是筆者認為學習WinForm必須要懂得。關於WinForm筆者就簡單的介紹到這裡。下一章我們要去看一下關於Linq和EF的知識點。