圖形介面程式設計(六) 分組容器和卡片容器(2)

NO IMAGE

點選開啟連結

2 卡片式容器

卡片容器(TabControl)是這樣一種容器:它本身包含多個容器(類似於Panel型別的面板容器),但同時只能顯示其中的一個,並隱藏其餘的容器。

卡片容器的用途很廣泛,目前流行的瀏覽器都使用卡片式方式在一個瀏覽器內可以同時開啟多個頁面,例如:

卡片式瀏覽的Firefox瀏覽器
圖1 卡片式瀏覽的Firefox瀏覽器

卡片式容器解決了一個這樣的問題:當我們需要在同一個窗體內放置為數眾多的控制元件時,如果不能讓使用者有選擇性的看到一些控制元件並隱藏其它控制元件,那麼使用者將會覺得很煩——一個介面上如果有超過10個以上的控制元件時,使用者將變得無所適從。

.net的卡片容器由TabPage(卡片頁)組成,卡片頁具有一個卡片標籤,它類似一個按鈕,顯示卡片頁的標題。點選某個卡片標籤,將在卡片容器內顯示對應的卡片頁。每個卡片頁像一個面板,可以容納其它的控制元件。

整個卡片容器就像我們在文具店隨處可見的帶標籤的記事本。可以通過點選某個卡片頁的標籤,顯示所需要的一部分控制元件,隱藏的其它控制元件。

卡片頁的Text屬性用來訪問卡片頁的標籤文字,例如:

  1. // 例項化一個卡片頁容器物件  
  2. TabPage page = new TabPage();  
  3. // 設定這個卡片頁容器的標籤文字  
  4. page.Text = "動畫播放";  

通過TabControl容器的TabPages屬性,我們可以訪問到卡片容器內所有的TabPage容器,TabPages屬性型別是一個TabPage的集合,所以可以有如下程式碼:

  1. // 訪問卡片容器內第一個卡片頁  
  2. TabPage page = this.tabControl.TabPages[0];  
  3. // 訪問卡片容器內第二個卡片頁  
  4. page = this.tabControl.TabPages[1];  
  5. // 遍歷所有的卡片頁  
  6. foreach (TabPage tp in this.tabControl.TabPages) {  
  7.     // 遍歷所有的卡片頁物件  
  8. }  

上例中介紹瞭如何訪問卡片容器中的卡片頁。這裡假設我們的類裡有一個TabControl型別的欄位tabControl。

通過如下程式碼,可以為卡片容器增加一個新的卡片頁:

  1. // 例項化一個標籤文字為"動畫播放"的卡片頁物件,這裡同時設定了卡片頁標籤文字  
  2. TabPage page = new TabPage("動畫播放");  
  3. // 將卡片頁物件加入到卡片容器中  
  4. this.tabControl.TabPages.Add(page);  

上述程式碼為卡片容器增加了一個標籤上顯示“動畫播放”的卡片頁。

可以使用簡化的方式,即TabPages屬性Add方法的過載:

  1. this.tabControl.TabPages.Add("動畫播放");  

上述程式碼為卡片容器增加了一個標籤為“動畫播放”的卡片頁,其後可以使用TabPages屬性來訪問它。即在Add方法內部例項化了TabPage物件並設定了其Text屬性。

  1. this.tabControl.TabPages.Add("play", "動畫播放");  

上述程式碼同樣是在Add方法內部例項化了TabPage物件並設定了其Text屬性,並且還設定了TabPage物件的Name屬性。通過Name屬性,可以更方便的查詢到這個TabPage物件。例如:

 

  1. // 通過索引器根據TabPage物件的名稱查詢  
  2. if (this.tabControl.TabPages.ContainsKey("play")) {  
  3.     TabPage page = this.tabControl.TabPages["play"];  
  4.     // 通過卡片頁的名稱刪除卡片頁  
  5.     this.tabControl.TabPages.RemoveByKey("play");  
  6. }  
  7. // 通過TabPage在卡片容器內的編號刪除卡片頁  
  8. this.tabControl.TabPages.RemoveAt(0);  

也可以通過如下程式碼批量增加卡片頁:

  1. // 例項化一個卡片頁物件陣列  
  2. TabPage[] pages = {  
  3.     new TabPage("動畫播放"),  
  4.     new TabPage("選項"),  
  5. };  
  6.    
  7. // 將卡片頁物件的陣列加入卡片容器內  
  8. this.tabControl.TabPages.AddRange(pages);  

卡片頁就可以看做一個普通的容器物件。卡片頁完全充滿卡片容器除了卡片標籤外的剩餘空間,並且不具備佈局功能,可以像操作一個Panel容器一樣來操作卡片頁,例如:

  1. // 為卡片容器內第1個卡片頁的Resize事件增加委託方法  
  2. this.tabControl.TabPages[0].Resize += new EventHandler(TabControlPage0Resize);  
  3.    
  4. // 為卡片容器內第2個卡片頁設定容器與內容之間的空白  
  5. this.tabControl.TabPages[1].Padding = new Padding(50);  
  6.    
  7. // 將表格佈局管理容器加入到卡片容器的第2個卡片頁中  
  8. this.tabControl.TabPages[1].Controls.Add(this.tablePaneInPage1);  

上述程式碼為卡片容器中的第1個卡片頁設定了尺寸變化時的事件委託方法,併為第2個卡片頁設定了容器和其內容直接的空白,最後將一個表格佈局管理面板容器加入到了第2個卡片頁中(假設表格佈局面板為tablePaneInPage1欄位)。這些事件、屬性和方法都是一個容器物件所特有的標誌。

可以看到,卡片容器的卡片頁即可以使用TabPages屬性的索引來訪問,也可以通過一個字串來訪問。

卡片容器的Alignment屬性可以設定卡片標籤的位置,屬性值是一個TabAlignment列舉的列舉項,可以取值Top(標籤位於卡片容器頂端,預設值)、Bottom(標籤位於卡片容器底部)、Left(標籤位於卡片容器左側)和Right(標籤位於卡片容器右側)。例如:

  1. // 設定卡片容器的卡片標籤位於容器頂端  
  2. this.tabControl.Alignment = TabAlignment.Top;  

上述程式碼設定卡片標籤在卡片容器的頂端。

當我們通過卡片標籤選擇新的卡片頁時,卡片容器會得到一個事件通知,可以使用一個委託方法處理這個事件,例如:

  1. // 為選中新的卡片標籤增加事件的委託方法  
  2. this.tabControl.SelectedIndexChanged += new EventHandler(TabControlTabIndexChanged);  

則事件處理方法為:

  1. private void TabControlTabIndexChanged(object sender, EventArgs e) {  
  2.     // 獲取被選中的卡片標籤標題文字, 將文字顯示在文字標籤控制元件中  
  3.     this.textLabel.Text = string.Format("卡片/"{0}/"被選中",   
  4.         this.tabControl.SelectedTab.Text);  
  5. }  

這裡,通過SelectedTab屬性獲取到了被選中的卡片標籤。另外,我們也可以使用SelectedIndex屬性獲取被選中卡片頁在卡片容器內的編號。

好了,基本就這麼多了,TabControl的使用非常簡單,這裡我們做一個稍微複雜的練習,來綜合使用前面學過的一部分容器和控制元件:

卡片頁“動畫播放”示意圖  
圖2 卡片頁“動畫播放”示意圖

卡片頁“選項”示意圖  
圖3 卡片頁“選項”示意圖

這次我們做一個簡單的動畫播放器,用到卡片容器:第一個卡片頁顯示動畫播放,第二個卡片頁顯示控制動畫的選項。

這次還是用到了Timer類(定時器),用於播放動畫和控制動畫播放的速度。

這次還使用到了“資源”。資源後面課程會有詳細介紹,這裡先熟悉一下基本用法。

整個窗體上有一個居中錨定的表格佈局面板(TableLayoutPanel),分為2行1列。第一行內是一個居中錨定的文字標籤控制元件(Label),第二行內是一個卡片容器(TabControl)。

卡片容器分為2頁,第1頁中包含一個圖片顯示框控制元件(PictrueBox),用於顯示動畫;第2頁中包含一個居中錨定的表格佈局面板,分為5行2列(注:圖中標為4行2列是一個錯誤,懶的改圖了,這裡說明一下),第1列中全部為文字標籤,顯示提示資訊,第2列中放置了一些控制動畫播放的控制元件。

用滑鼠右鍵開啟專案選單,選擇“屬性”選單項:

選擇專案屬性 
圖4 選擇專案屬性

再開啟的介面中選擇“資源”:

專案屬性-資源 
圖5 專案屬性-資源

選擇“新增資源”->“新增現有檔案”:

新增資原始檔 
圖6 新增資原始檔

再“開啟檔案”對話方塊中,在左邊選擇正確的路徑及資料夾,在右邊選擇要新增的圖片檔案,點選“開啟”按鈕:

新增圖片檔案 
圖7 新增圖片檔案

選擇“資源”選單->“影象”,確認資源已新增正確:

檢視已新增資源 
圖8 檢視已新增資源

注:以上小狗圖片均來自Sun公司Java Swing教程,特此說明。

至此,所有圖片資源都已經新增完畢,我們可以在程式中訪問這些資源。

程式碼如下:

Program.cs

  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Drawing;  
  4. using System.Resources;  
  5. using System.Windows.Forms;  
  6.    
  7. // 通過該名稱空間引入Resources類, 該類程式碼由Visual Studio自動生成  
  8. using Edu.Study.Graphics.TabControlContainer.Properties;  
  9.    
  10.    
  11. namespace Edu.Study.Graphics.TabControlContainer {  
  12.    
  13.     /// <summary>  
  14.     /// 主窗體類  
  15.     /// </summary>  
  16.     class MyForm : Form {  
  17.    
  18.         // 主窗體上的表格佈局面板, 2行1列  
  19.         private TableLayoutPanel mainTablePane;  
  20.    
  21.         // mainTablePane面板第1行文字標籤控制元件  
  22.         private Label textLabel;  
  23.    
  24.         // mainTablePane面板第2行卡片容器  
  25.         private TabControl tabControl;  
  26.    
  27.         // 卡片第1頁中的圖片框控制元件  
  28.         private PictureBox pictrueBox;  
  29.    
  30.         // 圖片框中圖片陣列, Image類表示一個圖片  
  31.         private Image[] pictrues;  
  32.    
  33.         // 用於切換圖片的定時器物件  
  34.         private Timer timer;  
  35.    
  36.         // 表示當前顯示的圖片在pictrues陣列中的索引  
  37.         private int pictrueIndex = 0;  
  38.    
  39.         // 第2個卡片中的表格佈局面板, 5行2列  
  40.         private TableLayoutPanel tablePaneInPage1;  
  41.    
  42.         // 用於控制動畫速度的數字滑塊  
  43.         private TrackBar speedTrackBar;  
  44.    
  45.         // 用於控制播放順序的核取方塊  
  46.         private CheckBox playBackCheckBox;  
  47.    
  48.         // 用於暫停動畫的核取方塊  
  49.         private CheckBox pauseCheckBox;  
  50.    
  51.         // 用於排列單選按鈕控制元件的流式面板  
  52.         private FlowLayoutPanel purFlowPane;  
  53.    
  54.         // 流式面板上的單選按鈕控制元件組, 用於顯示播放進度  
  55.         private RadioButton[] purRadioButton;  
  56.    
  57.         /// <summary>  
  58.         /// 構造器  
  59.         /// </summary>  
  60.         public MyForm() {  
  61.             this.Text = "卡片容器測試";  
  62.    
  63.             /***** 初始化最外層表格面板 *****/  
  64.             this.mainTablePane = new TableLayoutPanel();  
  65.             // 中央錨定  
  66.             this.mainTablePane.Dock = DockStyle.Fill;  
  67.             // 2行1列  
  68.             this.mainTablePane.RowCount = 2;  
  69.             this.mainTablePane.ColumnCount = 1;  
  70.             // 設定第1行的樣式, 絕對高度, 70單位  
  71.             this.mainTablePane.RowStyles.Add(new RowStyle(SizeType.Absolute, 70.0F));  
  72.             // 設定第2行樣式, 自動調整高度  
  73.             this.mainTablePane.RowStyles.Add(new RowStyle(SizeType.AutoSize));  
  74.             // 設定列樣式, 自動調整寬度  
  75.             this.mainTablePane.ColumnStyles.Add(new ColumnStyle(SizeType.AutoSize));  
  76.             // 加入到窗體  
  77.             this.Controls.Add(this.mainTablePane);  
  78.    
  79.             /***** 初始化mainTablePane第1行文字標籤控制元件 *****/  
  80.             this.textLabel = new Label();  
  81.             this.textLabel.AutoSize = true;  
  82.             this.textLabel.Dock = DockStyle.Fill;  
  83.             // 顯示一個實線邊框  
  84.             this.textLabel.BorderStyle = BorderStyle.FixedSingle;  
  85.             // 文字對齊方式: 上下居中, 左右居中  
  86.             this.textLabel.TextAlign = ContentAlignment.MiddleCenter;  
  87.             // 文字顏色為藍色  
  88.             this.textLabel.ForeColor = Color.Blue;  
  89.             // 字型設定為23號字  
  90.             this.textLabel.Font = new Font(this.Font.FontFamily, 23.0F);  
  91.             // 加入mainTablePane第1行  
  92.             this.mainTablePane.Controls.Add(this.textLabel, 0, 0);  
  93.    
  94.             /***** 初始化mainTablePane第2行卡片容器 *****/  
  95.             this.tabControl = new TabControl();  
  96.             this.tabControl.Dock = DockStyle.Fill;  
  97.             // 例項化一個卡片頁物件陣列  
  98.             TabPage[] pages = {  
  99.                 new TabPage("動畫播放"),  
  100.                 new TabPage("選項"),  
  101.             };  
  102.             // 將卡片頁物件的陣列加入卡片容器內  
  103.             this.tabControl.TabPages.AddRange(pages);  
  104.             // 為選中新的卡片標籤增加事件的委託方法  
  105.             this.tabControl.SelectedIndexChanged += new EventHandler(TabControlTabIndexChanged);  
  106.             // 為卡片容器內卡片第1頁的Resize事件增加委託方法  
  107.             this.tabControl.TabPages[0].Resize += new EventHandler(TabControlPage0Resize);  
  108.             // 為卡片容器內卡片第2頁設定容器與內容之間的空白  
  109.             this.tabControl.TabPages[1].Padding = new Padding(50);  
  110.             // 設定卡片容器的卡片標籤位於容器頂端  
  111.             this.tabControl.Alignment = TabAlignment.Top;  
  112.             // 加入mainTablePane第2行  
  113.             this.mainTablePane.Controls.Add(this.tabControl, 0, 1);  
  114.    
  115.             /***** 初始化tabControl第1頁圖片框控制元件 *****/  
  116.             this.pictrueBox = new PictureBox();  
  117.             // 設定圖片框尺寸和要顯示圖片尺寸一致.   
  118.             // Resources靜態屬性T0為名稱為T0的圖片資源, 為Image型別物件  
  119.             this.pictrueBox.Size = Resources.T0.Size;  
  120.             // 設定邊框為3D效果  
  121.             this.pictrueBox.BorderStyle = BorderStyle.Fixed3D;  
  122.             // 加入卡片容器第1頁  
  123.             this.tabControl.TabPages[0].Controls.Add(this.pictrueBox);  
  124.    
  125.             /***** 從資源中載入圖片 *****/  
  126.             // 例項化一個圖片向量集合  
  127.             List<Image> imagesList = new List<Image>();  
  128.             // 獲取資源管理器物件引用  
  129.             ResourceManager resMgr = Resources.ResourceManager;  
  130.             int index = 1;  
  131.             Image img = null;  
  132.             do {  
  133.                 // 使用資源管理器獲取名為Tn(n為從0開始數字)的資源  
  134.                 img = (Image)resMgr.GetObject("T" + index++);  
  135.                 if (img != null) {  
  136.                     // 如果獲取資源成功, 儲存該圖片物件  
  137.                     imagesList.Add(img);  
  138.                 }  
  139.             } while (img != null);  // 如果獲取資源失敗, 停止迴圈  
  140.             // 初始化圖片物件陣列  
  141.             this.pictrues = new Image[imagesList.Count];  
  142.             // 將集合內元素複製到陣列中  
  143.             imagesList.CopyTo(this.pictrues);  
  144.    
  145.             /***** 初始化定時器 *****/  
  146.             this.timer = new Timer();  
  147.             // 激發時間間隔50毫秒  
  148.             this.timer.Interval = 50;  
  149.             // 定時器激發事件委託方法  
  150.             this.timer.Tick += new EventHandler(TimerTick);  
  151.    
  152.             // 第2個卡片中文字標籤文字陣列  
  153.             string[] labelText = {  
  154.                 "速度:",  
  155.                 "倒放:",  
  156.                 "暫停:",  
  157.                 "幀列表:"  
  158.             };  
  159.    
  160.             /***** 第2個卡片中表格佈局面板 *****/  
  161.             this.tablePaneInPage1 = new TableLayoutPanel();  
  162.             this.tablePaneInPage1.Dock = DockStyle.Fill;  
  163.             // 5行2列. 第5行不放置控制元件, 僅僅為了填滿容器剩餘的空間, 將其它4行保持在足夠的高度上  
  164.             this.tablePaneInPage1.ColumnCount = 2;  
  165.             this.tablePaneInPage1.RowCount = labelText.Length + 1;  
  166.             // 設定第1列樣式, 絕對寬度, 110單位  
  167.             this.tablePaneInPage1.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 110.0F));  
  168.             // 設定第2列樣式, 自動調整  
  169.             this.tablePaneInPage1.ColumnStyles.Add(new ColumnStyle(SizeType.AutoSize));  
  170.             // 將表格佈局管理容器加入到卡片容器的第2個卡片頁中  
  171.             this.tabControl.TabPages[1].Controls.Add(this.tablePaneInPage1);  
  172.    
  173.             /***** 設定tablePaneInPage1容器第1列 *****/  
  174.             // 表示行數的整數  
  175.             int rows = 0;  
  176.             // 遍歷labelText陣列  
  177.             foreach (string s in labelText) {  
  178.                 // 例項化文字標籤控制元件  
  179.                 Label lb = new Label();  
  180.                 lb.Dock = DockStyle.Fill;  
  181.                 // 設定文字靠頂部居右對齊  
  182.                 lb.TextAlign = ContentAlignment.TopRight;  
  183.                 // 設定四周空白, 上邊預留8個單位空白  
  184.                 lb.Margin = new Padding(0, 8, 0, 0);  
  185.                 // 設定顯示文字  
  186.                 lb.Text = s;  
  187.                 // 控制元件置於tablePaneInPage1第row行第1列  
  188.                 this.tablePaneInPage1.Controls.Add(lb, 0, rows++);  
  189.                 // 設定該行樣式, 絕對高度, 高度為文字標籤高度2倍  
  190.                 this.tablePaneInPage1.RowStyles.Add(  
  191.                     new RowStyle(SizeType.Absolute, (lb.Height + lb.Margin.Vertical) * 2)  
  192.                 );  
  193.             }  
  194.             // 設定最末行樣式, 自動高度  
  195.             this.tablePaneInPage1.RowStyles.Add(new RowStyle(SizeType.AutoSize));  
  196.    
  197.             /***** 設定數字滑塊控制元件 *****/  
  198.             this.speedTrackBar = new TrackBar();  
  199.             this.speedTrackBar.Dock = DockStyle.Fill;  
  200.             // 定義控制元件四周空白, 右邊空白100單位  
  201.             this.speedTrackBar.Margin = new Padding(3, 5, 100, 3);  
  202.             // 定義數值範圍, 最小值和最大值  
  203.             this.speedTrackBar.Minimum = 20;  
  204.             this.speedTrackBar.Maximum = 500;  
  205.             // 定義當前值, 和定時器激發時間一致  
  206.             this.speedTrackBar.Value = this.timer.Interval;  
  207.             // 定義數值改變時事件委託方法  
  208.             this.speedTrackBar.ValueChanged += new EventHandler(SpeedTrackBarValueChanged);  
  209.             // 加入tablePaneInPage1第1行第2列  
  210.             this.tablePaneInPage1.Controls.Add(this.speedTrackBar, 1, 0);  
  211.    
  212.             /***** 設定核取方塊控制元件 *****/  
  213.             // 播放順序核取方塊  
  214.             this.playBackCheckBox = new CheckBox();  
  215.             // 預設不選中  
  216.             this.playBackCheckBox.Checked = false;  
  217.             // 控制元件加入tablePaneInPage1第2行第2列  
  218.             this.tablePaneInPage1.Controls.Add(this.playBackCheckBox, 1, 1);  
  219.    
  220.             // 播放暫停核取方塊  
  221.             this.pauseCheckBox = new CheckBox();  
  222.             // 預設不選中  
  223.             this.pauseCheckBox.Checked = false;  
  224.             // 設定選中狀態改變事件委託方法  
  225.             this.pauseCheckBox.CheckedChanged += new EventHandler(PauseCheckBoxCheckedChanged);  
  226.             // 控制元件加入tablePaneInPage1第3行第2列  
  227.             this.tablePaneInPage1.Controls.Add(this.pauseCheckBox, 1, 2);  
  228.    
  229.             /***** 設定流式面板 *****/  
  230.             this.purFlowPane = new FlowLayoutPanel();  
  231.             this.purFlowPane.Dock = DockStyle.Fill;  
  232.             // 設定佈局方向  
  233.             this.purFlowPane.FlowDirection = FlowDirection.LeftToRight;  
  234.             // 設定內容可以換行  
  235.             this.purFlowPane.WrapContents = true;  
  236.             // 禁止自動出現滾動條  
  237.             this.purFlowPane.AutoScroll = false;  
  238.             // 控制元件加入tablePaneInPage1第4行第2列  
  239.             this.tablePaneInPage1.Controls.Add(this.purFlowPane, 1, 3);  
  240.    
  241.             /***** 設定單選按鈕陣列 *****/  
  242.             // 陣列長度和圖片陣列長度一致  
  243.             this.purRadioButton = new RadioButton[this.pictrues.Length];  
  244.             for (int i = 0; i < this.purRadioButton.Length; i++) {  
  245.                 // 初始單選按鈕  
  246.                 RadioButton rb = new RadioButton();  
  247.                 // 設定顯示文字  
  248.                 rb.Text = string.Format("第{0}幀", i);  
  249.                 // 設定自動調整尺寸  
  250.                 rb.AutoSize = true;  
  251.                 // 控制元件加入purFlowPane容器內  
  252.                 this.purFlowPane.Controls.Add(rb);  
  253.                 // 控制元件物件存入陣列  
  254.                 this.purRadioButton[i] = rb;  
  255.             }  
  256.         }  
  257.    
  258.         /// <summary>  
  259.         /// 窗體載入事件委託方法  
  260.         /// </summary>  
  261.         protected override void OnLoad(EventArgs e) {  
  262.             base.OnLoad(e);  
  263.    
  264.             // 呼叫TabControlTabIndexChanged方法, 設定文字標籤控制元件文字  
  265.             //  注意, 呼叫事件委託方法, 第一個引數要傳遞和事件相關的控制元件物件引用,  
  266.             // 第二個引數如無特殊要求, 傳遞EventArgs.Empty, 表示沒有特殊事件引數  
  267.             TabControlTabIndexChanged(this.tabControl, EventArgs.Empty);  
  268.    
  269.             // 啟動定時器  
  270.             this.timer.Start();  
  271.         }  
  272.    
  273.         /// <summary>  
  274.         /// 選中卡片標籤事件處理委託方法  
  275.         /// </summary>  
  276.         private void TabControlTabIndexChanged(object sender, EventArgs e) {  
  277.             // 獲取被選中的卡片標籤標題文字, 將文字顯示在文字標籤控制元件中  
  278.             this.textLabel.Text = string.Format("卡片/"{0}/"被選中", this.tabControl.SelectedTab.Text);  
  279.         }  
  280.    
  281.         /// <summary>  
  282.         /// 卡片第1頁尺寸改變事件委託方法  
  283.         /// </summary>  
  284.         private void TabControlPage0Resize(object sender, EventArgs e) {  
  285.             // 獲取到卡片第1頁尺寸  
  286.             Size tabPageSize = this.tabControl.TabPages[0].Size;  
  287.             // 計算圖片框位置: 保持在其容器中央  
  288.             this.pictrueBox.Location = new Point(  
  289.                 (int)(((float)tabPageSize.Width – (float)this.pictrueBox.Size.Width) / 2.0F),  
  290.                 (int)(((float)tabPageSize.Height – (float)this.pictrueBox.Size.Height) / 2.0F)  
  291.             );  
  292.         }  
  293.    
  294.         /// <summary>  
  295.         /// 定時器激發事件委託方法  
  296.         /// </summary>  
  297.         private void TimerTick(object sender, EventArgs e) {  
  298.             // 為圖片框設定圖片陣列第pictrueIndex項圖片物件引用  
  299.             this.pictrueBox.Image = this.pictrues[this.pictrueIndex];  
  300.    
  301.             // 選中相應的單選按鈕陣列中的單選按鈕  
  302.             this.purRadioButton[this.pictrueIndex].Checked = true;  
  303.    
  304.             // 計算下一次顯示圖片的pictrueIndex值  
  305.             // 根據playBackCheckBox選中狀態, 決定pictrueIndex加1或減1  
  306.             if (this.playBackCheckBox.Checked) {  
  307.                 –this.pictrueIndex;  
  308.             } else {  
  309.                 ++this.pictrueIndex;  
  310.             }  
  311.    
  312.             // 如果pictrueIndex數字超出圖片陣列上限或下限, 修正數字  
  313.             if (this.pictrueIndex < 0) {  
  314.                 this.pictrueIndex = this.pictrues.Length – 1;  
  315.             }  
  316.             if (this.pictrueIndex == this.pictrues.Length) {  
  317.                 this.pictrueIndex = 0;  
  318.             }  
  319.         }  
  320.    
  321.         /// <summary>  
  322.         /// 數字滑塊改變事件委託方法  
  323.         /// </summary>  
  324.         private void SpeedTrackBarValueChanged(object sender, EventArgs e) {  
  325.             // 設定定時器激發時間間隔和滑塊當前數值一致  
  326.             this.timer.Interval = this.speedTrackBar.Value;  
  327.         }  
  328.    
  329.         /// <summary>  
  330.         /// 暫停核取方塊選中狀態改變事件委託方法  
  331.         /// </summary>  
  332.         private void PauseCheckBoxCheckedChanged(object sender, EventArgs e) {  
  333.             // 檢視核取方塊狀態  
  334.             if (this.pauseCheckBox.Checked) {  
  335.                 // 核取方塊選中, 停止定時器  
  336.                 this.timer.Stop();  
  337.             } else {  
  338.                 // 核取方塊未選中, 啟動定時器  
  339.                 this.timer.Start();  
  340.             }  
  341.         }  
  342.     }  
  343.    
  344.     /// <summary>  
  345.     /// 包括主方法的類  
  346.     /// </summary>  
  347.     static class Program {  
  348.         /// <summary>  
  349.         /// 應用程式的主入口點。  
  350.         /// </summary>  
  351.         static void Main() {  
  352.             Application.EnableVisualStyles();  
  353.             Application.SetCompatibleTextRenderingDefault(false);  
  354.             Application.Run(new MyForm());  
  355.         }  
  356.     }  
  357. }  

本節程式碼下載

本節程式碼使用圖片下載

本節練習使用的控制元件較多,佈局也較複雜。但通過前面的學習,應該不難做到這樣的佈局。幾個新的控制元件如PictrueBox用法都比較簡單,新增資源按照步驟就可以正確完成。

重點關注動畫的實現(297-319行),它展示了定時器的使用方法;各類控制元件對定時器的控制(324-341行)以及複雜的佈局技巧(173-255行),展示瞭如何通過控制元件和控制元件相對位置來計算控制元件的尺寸。