ASP.NET可交互式位圖窗體設計(3)
2024-07-10 12:55:39
供稿:網友
 
構造函數 
    構造函數傳遞三個參數:包含圓的中心坐標的點、圓的半徑以及一個 system.drawing.color 結構(包含用于繪制圓輪廓的顏色)。 
   
    然后我們根據中心和半徑計算邊框,并將筆顏色項設置為我們傳遞的顏色對象。 
   
    繪圖代碼 
    draw 方法重載實際上非常簡單:它根據我們保存在構造函數中的顏色對象創建一個筆對象,然后使用該筆,調用 graphics.drawellipse 方法繪制圓,同時傳遞了我們早先創建的邊框。 
   
    圖形、筆和畫筆 
    這里我們需要解釋一下 graphics、pen 和 brush 對象。(在開始填充我們的可填充對象時,就會看到 brush 對象。) 
   
    graphics 對象代表一個與某個真實繪圖空間相關聯的虛擬化的繪圖空間。虛擬化是指,通過在 graphics 對象上繪圖,我們可以使用相同的 graphics 方法在與該對象相關聯的任何類型的實際表面上繪圖。對于那些習慣于使用 mfc 或 microsoft windows? sdk 編程的用戶,graphics 對象相當于 windows 中稱為“設備上下文”(或 dc)的 .net 版本。 
   
    在此 windows 窗體應用程序中,傳遞到 draw 方法的 graphics 對象將與屏幕上的一個窗口相關聯 -- 這里是 picturebox。在我們的 microsoft asp.net 應用程序中使用這一代碼時,傳遞給 draw 方法的 graphics 對象將與一個位圖圖像相關聯。它也可以和打印機或其他設備相關聯。 
   
    這個方案的優點是我們可以使用相同的繪圖代碼在不同的表面上繪圖。在我們的繪圖代碼中,我們不需要知道任何有關屏幕、位圖、打印機等等之間的不同 -- .net framework(以及底層的操作系統)可以為我們處理所有細節。 
   
    利用相同的標記,筆和畫筆成為虛擬化的繪圖工具。筆代表線條屬性 -- 顏色、寬度、樣式,甚至可以是用來繪制線的位圖。畫筆代表一個填充區域的屬性 -- 顏色、樣式,甚至可以是用來填充區域的位圖。 
   
    在使用 using 后清除(或至少 dispose) 
    graphics、pen 和 brush 對象都與相似類型的 windows 對象相關聯。這些 windows 對象分配在操作系統的內存中 -- 這些內存尚未被 .net 運行時管理。長時間將這些對象駐留在內存中會導致性能問題,并且在 microsoft windows 98 下,當圖形堆填滿時會導致繪圖問題。因此,我們應盡快釋放這些 windows 對象。 
   
    當相應的 .net framework 對象完成操作并回收內存后,.net 運行時會自動釋放 windows 對象。但回收內存的時間會很長 -- 如果我們不迅速釋放這些對象,所有不幸的事情(包括填滿 windows 堆)都可能發生。在該應用程序的 asp.net 版本中,由于很多用戶在同一臺服務器上訪問該應用程序,所以這種現象會更加嚴重。 
   
    因為這些對象與未管理的資源相關聯,所以它們實現 idisposable 接口。該接口有一個方法,即 dispose,它將 .net framework 對象從 windows 對象中分離出來,并釋放 windows 對象,從而使計算機處于良好的狀態。 
   
    這時您只需完成一項任務:確保在使用完該對象后,調用 dispose 方法。 
   
    visual basic .net 代碼中顯示了這一內容的經典形式:首先創建對象,然后在一個 try 塊中使用該對象,最后在 finally 塊中清理該對象。try/finally 能夠確保即使出現異常也會清理對象。(在本例中,我們調用的繪圖方法可能不會引發異常,所以可能并不需要 try/finally 塊。但掌握這些技巧很有用,因此 dr. gui 也希望向您演示這一正確方法。) 
   
    這種形式很常見,因此 c# 為其提供了一個私有語句:using。c# 代碼中的 using 語句等同于 visual basic .net 代碼中的聲明和 try/finally -- 但更為簡潔、方便,并減少了發生錯誤的可能性。(dr. gui 不清楚為什么 visual basic .net 不包含一些諸如 using 的語句。) 
   
    接口 
    有些(但不是全部)可繪制對象可以被填充。某些對象(如點和線)不能被填充,因為它們不是封閉的區域,而矩形和圓等對象可以是中空的,或者被填充。 
   
    另一方面,就象將所有可繪制對象作為多態處理一樣,我們也可以將所有可填充對象作為多態處理,這會很方便。例如,如同我們將所有可繪制對象放到一個集合中,通過遍歷集合并在每個對象上調用 draw 來繪制對象一樣,我們可以將所有可填充對象放到一個集合中,而不考慮這些可填充對象的實際類型。因此,我們使用某種機制(如繼承)來獲得真正的多態。 
   
    因為不是所有的可繪制對象都可以被填充,因此不能將 fill 方法的聲明放在抽象基類中。.net framework 不允許類的多重繼承,所以也不能將其放在另一個抽象基類中。并且如果我們從不是非可填充類的其他基類中派生出可填充對象,則不能將所有可繪制對象作為多態處理。 
   
    但 .net framework 支持接口 -- 并提供了一個可實現任意數量的接口的類。接口不具有任何實現 -- 沒有代碼,也沒有任何數據。因此,實現接口的類必須提供所有內容。 
   
    接口所能包含的只有聲明。以下是我們在 c# 中的接口 ifillable。單擊此處在新窗口中查看全部源文件。 
   
   
   
    c# 
  public interface ifillable { 
  void fill(graphics g); 
  color fillbrushcolor { get; set; } 
  } 
   
   
    以下是等同的 visual basic .net 代碼。單擊此處在新窗口中查看全部源文件。 
   
    visual basic .net 
   
  public interface ifillable 
  sub fill(byval g as graphics) 
  property fillbrushcolor() as color 
  end interface 
   
    我們不需要聲明方法或者 virtual/overridable 或 abstract/mustoverride 屬性以及任何其他項,因為接口中的所有方法和屬性都自動設置為公開的和 abstract/mustoverride。 
   
    使用一個屬性:不能在接口中包含數據 
    請注意,雖然我們不能在接口中聲明字段,但可以聲明一個屬性,因為屬性實際上是作為方法實現的。 
   
    但這樣做會給接口的實現者帶來負擔,下面就會看到。實現者必須實現 get 和 set 方法,以及實現該屬性所必需的任何數據。如果實現非常復雜,則可以編寫一個 helper 類以封裝某些部分。在本文稍后我們將就一個略微不同的上下文環境顯示如何使用 helper 類。 
   
    實現接口 
    我們已經定義了接口,現在可以在類中實現它了。請注意,我們必須提供所實現接口的完整實現:不能只從中選取一部分。 
   
    下面我們看看 c# 中 dfilledrectangle 類的代碼。 
   
   
    c# 
  public class dfilledcircle : dhollowcircle, ifillable 
  { 
  public dfilledcircle(point center, int radius, color pencolor, 
  color brushcolor) : base(center, radius, pencolor) { 
  this.brushcolor = brushcolor; 
  } 
   
  public void fill(graphics g) { 
  using (brush b = new solidbrush(brushcolor)) { 
  g.fillellipse(b, bounding); 
  } 
  } 
  protected color brushcolor; 
  public color fillbrushcolor { 
  get { 
  return brushcolor; 
  } 
  set { 
  brushcolor = value; 
  } 
  } 
  public override void draw(graphics g) { 
  fill(g); 
  base.draw(g); 
  } 
  } 
   
   
    以下是 visual basic .net 中 dfilledrectangle 類的代碼。 
   
   
    visual basic .net 
  public class dfilledrectangle 
  inherits dhollowrectangle 
  implements ifillable 
  public sub new(byval rect as rectangle, _ 
  byval pencolor as color, byval brushcolor as color) 
  mybase.new(rect, pencolor) 
  me.brushcolor = brushcolor 
  end sub 
  public sub fill(byval g as graphics) implements ifillable.fill 
  dim b = new solidbrush(fillbrushcolor) 
  try 
  g.fillrectangle(b, bounding) 
  finally 
  b.dispose() 
  end try 
  end sub 
  protected brushcolor as color 
  public property fillbrushcolor() as color _ 
  implements ifillable.fillbrushcolor 
  get 
  return brushcolor 
  end get 
  set(byval value as color) 
  brushcolor = value 
  end set 
  end property 
  public overrides sub draw(byval g as graphics) 
  dim p = new pen(pencolor) 
  try 
  fill(g) 
  mybase.draw(g) 
  finally 
  p.dispose() 
  end try 
  end sub 
  end class 
   
   
    以下是有關這些類的注意事項。 
   
    從 hollowrectangle 中派生 
    我們從這個類的空心版本中派生出填充類。這個類中的多數內容都發生了改變:draw 方法和構造函數都是新的(但兩者都調用基類的版本),并且為 ifillable 接口的 fill 方法以及 fillbrushcolor 屬性添加了實現。 
   
    需要新構造函數的原因是我們在這個類中包含了需要初始化的其他數據,即填充畫筆。(您可以回顧我們前面討論的畫筆。)請注意此構造函數是如何調用基類構造函數的:在 c# 中,該調用被內置到聲明 (: base(center, radius, pencolor)) 中;在 visual basic .net 中,我們將它明確放在 new 方法(即構造函數)的第一行 (mybase.new(rect, pencolor))。 
   
    因為我們已經向基類構造函數中傳遞了三個參數中的兩個,現在只需初始化最后的字段即可。