Giáo trình Môi trường và công cụ lập trình 1 - Chương 4: Viewing .NET data

pdf 71 trang cucquyet12 3781
Bạn đang xem 20 trang mẫu của tài liệu "Giáo trình Môi trường và công cụ lập trình 1 - Chương 4: Viewing .NET data", để tải tài liệu gốc về máy bạn click vào nút DOWNLOAD ở trên

Tài liệu đính kèm:

  • pdfgiao_trinh_moi_truong_va_cong_cu_lap_trinh_1_chuong_4_viewin.pdf

Nội dung text: Giáo trình Môi trường và công cụ lập trình 1 - Chương 4: Viewing .NET data

  1. TRƯỜNG ĐẠI HỌC ĐÀ LẠT KHOA CÔNG NGHỆ THÔNG TIN NGUYỄN MINH HIỆP GIÁO TRÌNH MÔI TRƯỜNG VÀ CÔNG CỤ LẬP TRÌNH 1 Dành cho sinh viên ngành công nghệ thông tin Đà Lạt, 2010
  2. Khoa CNTT [MÔI TRƯỜNG VÀ CÔNG CỤ LẬP TRÌNH] Chương 4: Viewing .NET data Tổng quan Chương cuối cùng sẽ chỉ các cách khác nhau để chọn và thay đổi dữ liệu. Chương này, chúng tôi sẽ minh hoạ cách bạn thể hiện dữ liệu cho người sử dụng thấy bằng cách gắn kết các control Windows. Khả năng gắn kết dữ liệu của .NET giống với ADO và các control của VB. Tất cả ngôn ngữ .NET đều có khả năng sử dụng cùng những Control và phương thức. Khía cạnh mà chúng ta xem xét đó là control DataGrid. Một trong những đặc tính hay nhất của Datagrid là tính uyển chuyển- nguồn dữ liệu có thể là mảng, DataTable, DataView, DataSet hay là một thành phần thực thi các giao diện IListSource hay IList. Với một số lượng lớn các tuỳ chọn sẽ chỉ cách mà mọi nguồn dữ liệu này được sử dụng và được xem trong DataGrid. Gắn kết dữ liệu là một yêu cầu thông thường, và mặc dù VB 6 có khả năng này nhưng không bằng .NET, tất cả ngôn ngữ quản lý đều hoàn thiện khả năng gắn kết dữ liệu. Có những gì hơn khi giới hạn các cột dữ liệu, một control sẽ cập nhật tự động khi hàng dữ liệu hiện tại thay đổi. Chương này chúng ta tìm hiểu một vài khả năng gắn kết dữ liệu và chỉ cách để kết nối dữ liệu với control Windows Forms. Chúng ta sẽ xem vài công việc bên trong quá trình gắn kết dữ liệu để hiểu rõ hơn cách chúng hoạt động. Nguồn dữ liệu sẽ trở nên hợp lý hơn trong Visual studio.NET, và chương này sẽ chỉ cách sử dụng server Explorer để tạo một sự kết nối và phát sinh Dataset. Chúng ta sẽ tìm hiểu cách dùng của lược đồ XSD trong visual studio.NET. Trang 156
  3. Khoa CNTT [MÔI TRƯỜNG VÀ CÔNG CỤ LẬP TRÌNH] 4.1 The Control DataGrid DataGrid là một control mới hoàn toàn, được viết cho các ngôn ngữ .NET, và nó cho phép có những cái nhìn khác nhau về dữ liệu được hiển thị. Bạn có thể hiển thị dữ liệu bằng cách gọi phương thức SetDataBinding(). 4.1.1 Hiển thị dữ liệu xếp theo cột: Phần cuối của chương sẽ trình bày nhiều cách chọn dữ liệu và lấy nó trong một bảng dữ liệu, mặc dù dữ liệu được hiển thị trong một kiểu rất cơ bản; chúng ta chỉ đơn giản dùng Console.WriteLine() Ví dụ đầu tiên ở đây sẽ chỉ cách khôi phục dữ liệu và hiển thị trong một control DataGrid. Hình bên dưới là màn hình từ ứng dụng đã được xây dựng: Ứng dụng này chọn mỗi phần tử từ bản Customer trong cơ sở dữ liệu NorthWind và hiển thị những phần tử này cho người dùng trong một DataGrid. Đoạn mã này khá ngắn, ta sẽ từng bước xem xét chúng như sau: using System; using System.Windows.Forms; using System.Data; using System.Data.SqlClient; Trang 157
  4. Khoa CNTT [MÔI TRƯỜNG VÀ CÔNG CỤ LẬP TRÌNH] public class DisplayTabularData : System.Windows.Forms.Form { private System.Windows.Forms.Button retrieveButton; private System.Windows.Forms.DataGrid dataGrid; public DisplayTabularData() { this.AutoScaleBaseSize = new System.Drawing.Size(5, 13); this.ClientSize = new System.Drawing.Size(464, 253); this.Text = "01_DisplayTabularData"; Tiếp đến, ta sẽ tạo control khung lưới(grid), và cài đặt những thuộc tính của nó. Dòng thứ hai: dataGrid.BeginInit(); chỉ được dùng khi tạo nhiều sự thay đổi trên control. Nếu các sự kiện không giới hạn, mọi thay đổi trên khung lưới có thể tạo ra một Redraw trên màn hình. Sau đó ta xác định vị trí và kích thước của Control, định nghĩa chỉ mục tab, và neo control vào cả hai góc trên bên trái và góc dưới bên phải của cửa sổ để nó cân xứng trong cửa sổ ứng dụng chính. this.dataGrid = new System.Windows.Forms.DataGrid(); dataGrid.BeginInit(); dataGrid.Location = new System.Drawing.Point(8, 8); dataGrid.Size = new System.Drawing.Size(448, 208); dataGrid.TabIndex = 0; dataGrid.Anchor = AnchorStyles.Bottom | AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right; this.Controls.Add(this.dataGrid); dataGrid.EndInit(); Bây giờ ta tạo nút. Cùng với những bước cơ bản theo sau trong việc khởi tạo nút: Trang 158
  5. Khoa CNTT [MÔI TRƯỜNG VÀ CÔNG CỤ LẬP TRÌNH] this.retrieveButton = new System.Windows.Forms.Button(); retrieveButton.Location = new System.Drawing.Point(384, 224); retrieveButton.Size = new System.Drawing.Size(75, 23); retrieveButton.TabIndex = 1; retrieveButton.Anchor = AnchorStyles.Bottom | AnchorStyles.Right; retrieveButton.Text = "Retrieve"; retrieveButton.Click += new System.EventHandler (this.retrieveButton_Click); this.Controls.Add(this.retrieveButton); Chúng ta có một sự kiện click gọi bộ điều khiển sự kiện retrieveButton_click protected void retrieveButton_Click(object sender, System.EventArgs e) { retrieveButton.Enabled = false; string source = "server=(local)\\NetSDK;" + "uid=QSUser;pwd=QSPassword;" + "database=Northwind"; Sau khi chọn dữ liệu từ bảng Customer và điền dữ liệu vào tập dữ liệu. Ta gọi phương thức SetĐataBlinding để gắn kết dữ liệu giữa tập dữ liệu và khung lưới. Phương thức này sẽ được truyền vào tập dữ liệu và tên của bảng trong DataSet. Một khung lưới có thể chỉ hiện dữ liệu từ một DataTable tại một thời điểm mặc dù DataSet chứa nhiều bảng. string select = "SELECT * FROM Customers" ; SqlConnection conn = new SqlConnection(source); SqlDataAdapter da = new SqlDataAdapter( select , conn); DataSet ds = new DataSet(); da.Fill(ds , "Customers"); Trang 159
  6. Khoa CNTT [MÔI TRƯỜNG VÀ CÔNG CỤ LẬP TRÌNH] dataGrid.SetDataBinding(ds , "Customers"); } static void Main() { Application.Run(new DisplayTabularData()); } } Để biên dịch đoạn mã trên bạn gõ dòng lệnh sau: csc /t:winexe /debug+ /r:System.dll /r:System.Data.dll /r:system.windows.forms.dll /recurse:*.cs Tham số /recurse:*.cs sẽ biên dịch tất cả tập tin .cs trong thư mục hiện hành và các thư mục con. Đó là một cách viết tắt, nên bạn không cần phải nhớ tất cả các tập tin nhưng bạn phải chắc chắn là chỉ có những tập tin bạn cần nằm trong thư mục đó. 4.1.2 Nguồn dữ liệu: DataGrid là một cách rất linh động để hiển thị dữ liệu; thêm vào đó là để gọi phương thức SetDataBlinding() với một DataSet và tên của bảng để hiển thị thì phương thức này sẽ được gọi với bất kỳ nguồn dữ liệu sau: Một mảng Datatable DataView DataSet hay DataViewManager Những thành phần thực thi giao diện IListSource Những thành phần thực thi giao diện IList 4.1.2.1 Hiển thị dữ liệu từ một mảng: Trang 160
  7. Khoa CNTT [MÔI TRƯỜNG VÀ CÔNG CỤ LẬP TRÌNH] Nhìn thoáng qua có vẽ rất dễ dàng. Tạo một mảng, điền dữ liệu vào mảng và gọi phương thức SetDataBlinding(array,null) trên DataGrid. Như ví dụ sau: string[] stuff = new string[] {"One", "Two", "Three"}; dataGrid.SetDataBinding(stuff, null); Chú ý rằng phương thức SetDataBlinding() chỉ có hai tham số, tham số đầu là nguồn dữ liệu trong trường hợp này là mảng, tham số còn lại: nếu nguồn dữ liệu là DataSet hay DataViewMannager thì gán bằng tên của bảng muốn hiển thị còn ngược lại được gán giá trị null. Bạn có thể thay thế đoạn mã trong bộ điều khiển sự kiện retriveButton_click() của ví dụ trước với đoạn mã ở trên. Kết quả hiển thị sẽ có vấn đề sau: Bạn sẽ thấy kết quả hiển thị có nhiều hơn số chuỗi mà bạn định nghĩa trong mảng, Khung lưới sẽ hiển thị thêm chiều dài của những chuỗi đó. Nguyên nhân là khi sử dụng mảng là nguồn dữ liệu của Datagrid thì khung lưới sẽ tìm thuộc tính chung đầu tiên của đối tượng bên trong mảng để hiển thị và trường hợp này đó chính là chiều dài của chuỗi đó. Trang 161
  8. Khoa CNTT [MÔI TRƯỜNG VÀ CÔNG CỤ LẬP TRÌNH] Một cách giải quyết đó là tạo một lớp bao bọc cho các chuỗi này như bên dưới: protected class Item { public Item(string text) { m_text = text; } public string Text { get{return m_text;} } private string m_text; } 4.1.2.2 DataTable Có hai cách chính để hiển thị một DataTable trong một DataGrid: Nếu DataTable của bạn đứng một mình thì gọi phương thức SetDataBlingding(DataTable,null) Trang 162
  9. Khoa CNTT [MÔI TRƯỜNG VÀ CÔNG CỤ LẬP TRÌNH] Nếu DataTable của bạn chứa một DataSet thì gọi SetDataBlinding(DataSet," ") Ví dụ bên dưới để hiện một số cột: Chú ý hiển thị của cột cuối cùng; nó hiện một checkbox để thay cho các control thông thường. DataGrid sẽ đọc lượt đồ từ nguồn dữ liệu và suy ra các kiểu cột mà control được hiển thị. Dữ liệu trong cơ sở dữ liệu không thay đổi khi bạn thay đổi các trường trong khung lưới dữ liệu. 4.1.2.3 Hiển thị dữ liệu từ một DataView Một DataView cung cấp phương tiện để lọc và sắp xếp dữ liệu bên trong một DataTable. Khi bạn chọn một dữ liệu từ một cơ sở dữ liệu, thông thường nó cho phép người dùng sắp xếp dữ liệu đó. Thêm vào đó, bạn muốn lọc dữ liệu để chỉ hiện những hàng nào đó. Một DataView cho phép bạn giới hạn số hàng hiển thị cho người dùng nhưng nó không giới hạn số cột trong DataTable. Một DataView không cho phép bạn thay đổi các cột để hiển thị mà chỉ thay đổi các hàng. Trang 163
  10. Khoa CNTT [MÔI TRƯỜNG VÀ CÔNG CỤ LẬP TRÌNH] Bên dưới đây là một dòng mã để tạo một DataView dựa trên một DataTable đang tồn tại: DataView dv = new DataView(dataTable); Khi tạo bạn có thể thay đổi các cài đặt trên DataView, và nó sẽ ảnh hưởng đến dữ liệu và các tác vụ khi chúng được hiển thị bên trong một DataGrid. Một vài ví dụ là: Cài đặt AllowEdit = false khoá chức năng chỉnh sửa các dòng Cài đặt AllowNew = false khoá chức năng tạo dòng mới Cài đặt AllowDelete = false khoá khả năng xoá dòng Cài đặt RowStateFilter chỉ hiển thị những dòng của một trạng thái được cho Cài đặt RowFilter để lọc hàng Xắp xếp các dòng bằng các cột nào đó 4.1.2.4 Lọc các hàng bằng dữ liệu: Khi bạn tạo một DataView, bạn có thể thay đổi dữ liệu hiển thị bằng cách cài đặt thuộc tính RowFilter. Thuộc tính này như một chuỗi được dùng như một phương tiện để lọc trên những tiêu chuẩn nào đó- giá trị của chuỗi được dùng như tiêu chuẩn lọc. Vài ví dụ về các mệnh đề lọc được chỉ trong bảng dưới đây Mệnh đề Mục đích UnitsInStock > 50 Chỉ những hàng có UnitslnStock lớn hơn 50 Client = 'Smith' Chỉ trả về những mẫu cho một client County LIKE 'C*' Trả về tất cả mẫu mà trường Country bắt đầu ký tự C 4.1.2.5 Lọc các hàng trên trạng thái Trang 164
  11. Khoa CNTT [MÔI TRƯỜNG VÀ CÔNG CỤ LẬP TRÌNH] Mọi hàng trên DataView có một tập định nghĩa hàng, nó sẽ là một trong những giá trị sau. Tập này có thể được dùng để lọc những hàng được xem bởi người dùng: DataViewRowState Mục đích Added Tất cả hàng được tạo mới CurrentRows Tất cả hàng ngoại trừ những hàng được chọn sẽ bị xoá Deleted tất cả hàng được chọn bị xoá ModifiedCurrent Dãy tất cả hàng bị sửa đổi và hiện giá trị hiện hành cho mọi cột ModifiedOriginal Dãy tất cả hàng bị sửa đổi nhưng hiện giá trị ban đầu cho các cột chứ không phải giá trị hiện hành OriginalRows Tất cả hàng được chọn ban đầu từ nguồn dữ liệu .Không có những hàng mới. Chỉ hiện những giá trị ban đầu của các cột. Unchanged Tất cả các hàng không thể bị thay đổi Để xem ảnh hưởng của các trạng thái này trên một khung lưới, ta viết một ví dụ hiển thị hai khung lưới: một chứa dữ liệu được chọn từ cơ sở dữ liệu mà bạn có thể tương tác, cái còn lại hiện các hàng trong một trong những trạng thái trên. Trang 165
  12. Khoa CNTT [MÔI TRƯỜNG VÀ CÔNG CỤ LẬP TRÌNH] Bộ lọc không chỉ áp dụng đối với các hàng mà còn với trạng thái của các cột bên trong những hàng này. 4.1.2.6 Sắp xếp các hàng: Khi lọc dữ liệu, đôi lúc bạn cần sắp xếp dữ liệu trong một Dataview. Bạn có thể click trên tiêu đề của cột trong control DataGrid, và nó sẽ sắp xếp một cột theo thứ tự giảm dần hoặc tăng dần. Tuy nhiên bạn chỉ có thể sắp xếp một cột, những nơi nào có Dataview gạch dưới thì có thể sắp xếp theo nhiều cột. Trang 166
  13. Khoa CNTT [MÔI TRƯỜNG VÀ CÔNG CỤ LẬP TRÌNH] Khi một cột được sắp xếp, DataGrid sẽ hiển thị một mũi tên để cho biết cột đã được sắp xếp. Để thực hiện sắp xếp trên một cột bạn sử dụng thuộc tính Sort của DataView: dataView.Sort = "ProductName"; dataView.Sort = "ProductName ASC, ProductID DESC"; Dòng đầu tiên sẽ được sắp xếp theo cột ProductName. Dòng thứ hai sẽ được sắp xếp thứ tự tăng dần theo cột ProductName và sau đó theo thứ tự giảm dần của ProductID. Dataview hỗ trợ sắp xếp tăng dần và giảm dần trên các cột. Nếu bạn chọn sắp xếp trên nhiều cột thì DataGrid sẽ ngừng hiện các mũi tên sắp xếp. 4.1.2.7 Hiển thị dữ liệu từ một DataSet Ở ví dụ trước, DataGrid chỉ có thể hiển thị một DataTable đơn tại một thời điểm. Nhưng ở ví dụ này, nó có thể điều khiển nhiều mối quan hệ trong DataSet trên màn hình. Đoạn mã sau được dùng để tạo ra một DatasSet dựa trên các bảng Customers và Orders trong cơ sở dữ liệu Northwind. Ví dụ này thêm hai DataTable và tạo một mối quan hệ giữa chúng gọi là CustomerOrders: string source = "server=(local)\\NetSDK;" + "uid=QSUser;pwd=QSPassword;" + "database=northwind"; string orders = "SELECT * FROM Orders"; string customers = "SELECT * FROM Customers"; SqlConnection conn = new SqlConnection(source); SqlDataAdapter da = new SqlDataAdapter(orders, conn); DataSet ds = new DataSet(); da.Fill(ds, "Orders"); da = new SqlDataAdapter(customers , conn); Trang 167
  14. Khoa CNTT [MÔI TRƯỜNG VÀ CÔNG CỤ LẬP TRÌNH] da.Fill(ds, "Customers"); ds.Relations.Add("CustomerOrders", ds.Tables["Customers"].Columns["CustomerID"], ds.Tables["Orders"].Columns["CustomerID"]); Khi tạo, bạn có thể liên kết DataSet với DataGrid bằng cách gọi phương thức SetDataBinding: dataGrid1.SetDataBinding(ds, "Customers"); Nó sẽ tạo một hiển thị như sau: Bạn chú ý có một dấu + bên trái mỗi mẫu tin. Để biết rằng chúng ta đã tạo một Dataset với một mối quan hệ điều khiển giữa customers và orders. Bạn có thể định nghĩa nhiều mối quan hệ trong đoạn mã. Khi bạn click trên các dấu +, một danh sách các mối quan hệ hiện ra, click trên tên của mối quan hệ sẽ điều khiển khung lưới liên kết với các mẫu tin. Trang 168
  15. Khoa CNTT [MÔI TRƯỜNG VÀ CÔNG CỤ LẬP TRÌNH] Control DataGrid bao gồm một cặp biểu tượng mới ở góc trên bên phải. Mũi tên cho phép bạn quay lại hàng cha mẹ, và sẽ thay đổi hiển thị đến trang trước đó. Tiêu đề của hàng hiện chi tiết các mẫu tin cha mẹ có thể hiện hay ẩn bằng cách click trên những nút khác. 4.1.2.8 Hiển thị dữ liệu trong một DataViewManager Hiển thị dữ liệu trong một DataViewManager thì giống như DataSet. Nhưng khi một DataViewManager được tạo cho một DataSet thì một DataView đặc biệt được tạo ra cho mỗi DataTable, cho phép bạn có thể thay đổi hiển thị hàng, dựa vào một bộ lọc hay trạng thái hàng. Nếu bạn không muốn lọc dữ liệu, bạn sẽ đề nghị luôn luôn bao một DataSet trong một DataViewManager để hiển thị. Nó cho bạn nhiều tuỳ chọn khi sửa đổi mã của bạn. Đoạn mã dưới tạo một DataViewManager dựa trên DataSet từ ví dụ trước, và sau đó thay đổi DataView cho bảng Customers để chỉ hiện customers từ UK: DataViewManager dvm = new DataViewManager(ds); dvm.DataViewSettings["Customers"].RowFilter = "Country='UK'"; dataGrid.SetDataBinding(dvm, "Customers"); Kết quả hiển thị sẽ như sau: Trang 169
  16. Khoa CNTT [MÔI TRƯỜNG VÀ CÔNG CỤ LẬP TRÌNH] 4.1.2.9 Giao diện IListSource và IList DataGrid cũng hổ trợ bất kỳ đối tượng mà đưa vào một trong những giao diện IListSource hay IList. IListSource chỉ có một phương thức GetList() trả về một giao diện IList. IList được thực thi bởi rất nhiều lớp trong thời gian chạy. Vài lớp thực thi giao diện này là Array, ArrayList, StringCollection. Khi sử dụng IList, cùng điều kiện cho đối tượng bên trong tập hợp là true thì sự thực thi Array sẽ hiện dễ dàng hơn- Nếu sử dụng một StringCollection như nguồn dữ liệu cho DataGrid thì chiều dài của chuỗi được hiện bên trong khung lưới. 4.1.3 Thừa kế lớp DataGrid Thừa kế lớp cho những phần chính của DataGrid được hiện bên dưới: Trang 170
  17. Khoa CNTT [MÔI TRƯỜNG VÀ CÔNG CỤ LẬP TRÌNH] DataGrid bao gồm 0 hay nhiều DataGridTableStyles. Những kiểu này bao gồm 0 hay nhiều DataGridColumnStyles. Một ô trong khung lưới có thể được truy cập bởi nhiều phương tiện của cấu trúc DatagridCell. 4.1.3.1 DataGridTableStyle và DataGridColumnStyle Một DataGridTableStyle chứa sự miêu tả trực quan của DataTable. DataGrid chứa một tập hợp những kiểu này có thể truy cập được bằng thuộc tính TableStype. Khi một DataTable được hiển thị thì một sự kiểm tra được tạo xuyên qua tất cả đối tượng DataGridTableStype để tìm thuộc tính MappingName của nó bằng với thuộc tính TableName của DataTable. Trong khi tìm kiếm, kiểu đó sẽ được dùng trong việc hiển thị của bảng. DataGridTableStyle cho phép bạn định nghĩa những biến hiện hình cho DataGrid, như là màu nền và màu cận cảnh, font dùng trong tiêu đề cột và các thuộc tính khác. DataGridColumnStyle cho phép bạn lọc những tuỳ chọn hiển thị trên một cột, như là cài đặt trật tự của dữ liệu trong cột, văn bản được hiển thị một giá trị null và chiều rộng của cột trên màn hình. Khi DataGrid hiển thị một định nghĩa DataGridTableStype, bạn có thể định nghĩa các cột của dữ liệu được hiển thị bằng cách thêm một DataGridColumnStype. Chỉ những cột có một kiểu định nghĩa sẽ được hiển thị và có thể là lợi ích cho những cột ẩn như là giá trị của khoá chính không được hiển thị. Bạn cũng có thể định nghĩa một kiểu cột ReadOnly. Đoạn mã bên dưới là ví dụ của việc tạo một DataGridTableStyle. Đoạn mã tạo ra một đối tượng DataGridTableStyle, thêm vào hai đối tượng DataGridColumnStyle, và hiển thị tất cả dữ liệu bên trong bảng Customer. using System; using System.Windows.Forms; Trang 171
  18. Khoa CNTT [MÔI TRƯỜNG VÀ CÔNG CỤ LẬP TRÌNH] using System.Data; using System.Data.SqlClient; public class CustomDataGridTableStyle : System.Windows.Forms.Form { private System.Windows.Forms.Button retrieveButton; private System.Windows.Forms.DataGrid dataGrid; public CustomDataGridTableStyle() { this.AutoScaleBaseSize = new System.Drawing.Size(5, 13); this.ClientSize = new System.Drawing.Size(464, 253); this.Text = "07_CustomDataGridTableStyle"; this.dataGrid = new System.Windows.Forms.DataGrid(); dataGrid.BeginInit(); dataGrid.Location = new System.Drawing.Point(8, 8); dataGrid.Size = new System.Drawing.Size(448, 208); dataGrid.TabIndex = 0; dataGrid.Anchor = AnchorStyles.Bottom | AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right; this.Controls.Add(this.dataGrid); dataGrid.EndInit(); this.retrieveButton = new System.Windows.Forms.Button(); retrieveButton.Location = new System.Drawing.Point(384, 224); retrieveButton.Size = new System.Drawing.Size(75, 23); retrieveButton.TabIndex = 1; retrieveButton.Anchor = AnchorStyles.Bottom | AnchorStyles.Right; retrieveButton.Text = "Retrieve"; retrieveButton.Click += new System.EventHandler(this.retrieveButton_Click); this.Controls.Add(this.retrieveButton); Trang 172
  19. Khoa CNTT [MÔI TRƯỜNG VÀ CÔNG CỤ LẬP TRÌNH] } protected void retrieveButton_Click(object sender, System.EventArgs e) { retrieveButton.Enabled = false; DataSet sẽ được dùng tạo ra DataGridTableStyles để dùng trong ví dụ này và cuối cùng liên kết DataGrid với DataSet. Phương thức CreateDataSet không có gì mới như chúng ta thấy sau, nó chỉ đơn giản nhận tất cả hàng từ bảng Customers: DataSet ds = CreateDataSet(); CreateStyles(dataGrid); dataGrid.SetDataBinding(ds, "Customers"); } Phương thức CreateStyles() có nhiều đặc biệt. Dòng đầu tiên tạo đối tượng DataGridTableStype và cài thuộc tính MappingName của nó. Thuộc tính này được sử dụng khi DataGrid hiển thị một DataTable. DataGrid có thể hiển thị hàng trong những màu thay đổi. Đoạn mã ở đây định nghĩa màu theo từng cặp hàng. private void CreateStyles(DataGrid dg) { DataGridTableStyle style = new DataGridTableStyle(); style.MappingName = "Customers"; style.AlternatingBackColor = System.Drawing.Color.Bisque; DataGridTextBoxColumn customerID = new DataGridTextBoxColumn(); customerID.HeaderText = "Customer ID"; customerID.MappingName = "CustomerID"; customerID.Width = 200; DataGridTextBoxColumn name = new DataGridTextBoxColumn(); name.HeaderText = "Name"; Trang 173
  20. Khoa CNTT [MÔI TRƯỜNG VÀ CÔNG CỤ LẬP TRÌNH] name.MappingName = "CompanyName"; name.Width = 300; Khi các cột được định nghĩa, chúng được thêm vào GridColumnStypes bộ các đối tượng DataGridTableStype, các đối tượng này có thể tự thêm thuộc tính TableStype của DataGrid: style.GridColumnStyles.AddRange (new DataGridColumnStyle[]{customerID , name}); dg.TableStyles.Add(style); } private DataSet CreateDataSet() { string source = "server=(local)\\NetSDK;" + "uid=QSUser;pwd=QSPassword;" + "database=northwind"; string customers = "SELECT * FROM Customers"; SqlConnection con = new SqlConnection(source); SqlDataAdapter da = new SqlDataAdapter(customers , con); DataSet ds = new DataSet(); da.Fill(ds, "Customers"); return ds; } static void Main() { Application.Run(new CustomDataGridTableStyle()); } } Trang 174
  21. Khoa CNTT [MÔI TRƯỜNG VÀ CÔNG CỤ LẬP TRÌNH] Sau khi tạo đối tượng DataGridTableStyle, chúng ta tạo hai đối tượng thừa hưởng từ DataGridColumnStyle. Mọi cột có một số lượng thuộc tính được định nghĩa. Sau đây là một danh sách các thuộc tính khoá: Property Description Alignment một trong những giá trị liệt kê HorizontalAlignment - Left, Center, or Right. Nó cho biết cách mà dữ liệu trong cột được định nghĩa hợp lý. FontHeight Kích cỡ của font theo pixels. Nó sẽ mặc định nếu không có giá trị được cài. Thuộc tính này được bảo vệ, vì thế có thể chỉ sửa đổi nếu bạn tạo lớp con. HeaderText Văn bản hiển thị trong cột heading. MappingName Cột trong DataTable mô tả bởi cột hiển thị NullText Văn bản hiển thị bên trong cột nếu giá trị dữ liệu nằm dưới là DBNull. PropertyDescriptor Nó sẽ được bàn luận phần của chương ReadOnly Một cờ cho biết cột là read-write or read-only. Width Chiều rộng của cột theo pixels. Kết quả của đoạn mã hiện như sau: Trang 175
  22. Khoa CNTT [MÔI TRƯỜNG VÀ CÔNG CỤ LẬP TRÌNH] 4.2 Gắn kết dữ liệu Ở ví dụ trứơc đã xem xét tất cả control DataGrid, đó chỉ là một phần trong thời gian chạy.NET có thể dùng để hiển thị dữ liệu. Một tiến trình gắn kết một control và một nguồn dữ liệu được gọi là data binding. Nếu bạn có những kinh nghiệm với các ứngdụng lập trình Windows trong MFC. Có lúc nào đó bạn đã sử dụng chức năng Dialog Data Exchange (DDX)để móc các biến thành viên của một lớp với bộ điều khiển Win32. Bạn sẽ vui sướng khi biết rằng bạn có thể giấu cửa trên DDX, như nó dễ dàng hơn để móc dữ liệu vào bộ điều khiển trong .NET. Bạn có thể gắn kết dữ liệu không chỉ đến các bộ điều khiển Window mà còn với các trang Web ASP.NET. 4.2.1 Gắn kết đơn giản Một control hỗ trợ việc gắn kết đơn hiển thị chỉ những giá trị đơn tại một lúc, như là một hộp văn bản hay một nút chọn. Ví dụ sau chỉ cách gắn kết một cột từ một DataTable đến một hộp văn bản. DataSet ds = CreateDataSet(); textBox1.DataBindings.Add("Text", ds , "Products.ProductName"); Trang 176
  23. Khoa CNTT [MÔI TRƯỜNG VÀ CÔNG CỤ LẬP TRÌNH] Sau khi lấy lại vài dữ liệu từ bảng Products và lưu trữ trong một DataSet được trả về từ phương thức CreateDataSet() như trên, dòng thứ hai gắn kết thuộc tính Text của control đến cột Products.ProductName. Nếu bạn viết đoạn mã này từ cơ sở dữ liệu Northwind, bạn sẽ thấy màn hình như bên dưới đây: Hộp văn bản hiển thị vài thứ trong cơ sở dữ liệu. Để kiểm tra rằng nó là cột hay giá trị, bạn sẽ sử dụng công cụ SQL Server Query Analyzer để kiểm tra nội dung của bảng Procucttool. 4.2.2 Đối tượng gắn kết dữ liệu Sơ đồ sau chỉ một thừa kế lớp cho các đối tượng được sử dụng trong gắn kết dữ liệu. Trong phần này ta bàn luận về BindingContext, CurrencyManager, và PropertyManager các lớp của System.Windows.Forms, và trình cách chúng tương tác khi dữ liệu giới hạn trong một hay nhiều control trên một form. Các đối tượng chuyển màu được dùng trong gắn kết. Trang 177
  24. Khoa CNTT [MÔI TRƯỜNG VÀ CÔNG CỤ LẬP TRÌNH] Trong ví dụ trước, chúng ta sử dụng thuộc tính DataBinding của control TextBox để gắn kết một cột từ một DataSet đến thuộc tính Text của bộ điều khiển. Thuộc tính DataBindings là một thể hiện của ControlBindingsCollection : textBox1.DataBindings.Add("Text", ds, "Products.ProductName"); Dòng này thêm một đối tượng gắn kết từ một đối tượng Binding đến ControlBindingsCollection 4.2.2.1 Binding Context Mọi Windows form có một thuộc tính BindingContext. Form được thừa hưởng từ Control . Một đối tượng BindingContext có một tập thể hiện BindingManagerBase. Những thể hiện này được tạo và thêm vào đối tượng quản lý gắn kết khi một control bị giới hạn: Trang 178
  25. Khoa CNTT [MÔI TRƯỜNG VÀ CÔNG CỤ LẬP TRÌNH] BindingContext sẽ chứa vài nguồn dữ liệu, được gói trong một CurrencyManager hay một PropertyManager. Sự quyết định lớp nào được dùng dựa vào chính nguồn dữ liệu. Nếu nguồn dữ liệu chứa một dãy item như là DataTable, DataView, hay bất kỳ đối tượng khác thực thi giao diện IList thì một CurrencyManager sẽ được dùng, như nó có thể duy trì vị trí hiện tại bên trong nguồn dữ liệu. Nếu nguồn dữ liệu chỉ trả về một giá trị đơn thì một PropertyManager sẽ được lưu trữ trong BindingContext. Một CurrencyManager hay PropertyManager chỉ được tạo một lần cho một nguồn dữ liệu. Nếu bạn gắn kết hai hộp văn bản với một hàng từ một DataTable thì chỉ một currencyManager sẽ được tạo bên trong binding context. Mọi control thêm vào một form được gắn kết với bộ quản lý gắn kết của form, vì thế tất cả control chia sẽ cùng một thể hiện. Khi một control được tạo thuộc tính BindingContext của nó là null. Khi control được thêm bộ Control của form thì nó sẽ cài BindingContext đến bộ đó của form. Để gắn kết một control với một form, bạn cần thêm một thực thể vào thuộc tính DataBinding của nó. Đoạn mã bên dưới tạo một sự gắn kết mới: textBox1.DataBindings.Add("Text", ds, "Products.ProductName"); Trang 179
  26. Khoa CNTT [MÔI TRƯỜNG VÀ CÔNG CỤ LẬP TRÌNH] Phương thức Add() của ControlBindingsCollection tạo một thể hiện mới của đối tượng Binding từ những thông số của phương thưc này và thêm chúng vào bộ những việc gắn kết Hình trên trình bày những gì đang hoạt động khi bạn thêm một Binding đến một Control. Binding gắn kết control với một nguồn dữ liệu được duy trì bên trong BindingContext của Form. Sự thay đổi bên trong nguồn dữ liệu được phản ánh vào control như là những thay đổi trong control đó. 4.2.2.2 Binding Lớp này gắn kết một thuộc tính của control với một thành viên của nguồn dữ liệu. Khi những thành viên này thay đổi thì những thuộc tính của control được cập nhật để phản ánh sự thay đổi này và ngược lại Trang 180
  27. Khoa CNTT [MÔI TRƯỜNG VÀ CÔNG CỤ LẬP TRÌNH] Bindings có thể cài đặt từ bất kỳ cột nào đến bất kỳ thuộc tính nào của control, vì thế bạn sẽ gắn kết một cột với một hộp văn bản và có thể gắn kết cột khác với màu hộp văn bản Bạn có thể gắn kết các thuôc tính của một control đến các nguồn dữ liệu khác nhau. 4.2.2.3 CurrencyManager và PropertyManager Khi một đối tượng Binding được tạo, một đối tượng CurrencyManager hay PropertyManager sẽ được tạo nếu đó là lần đầu tiên dữ liệu đó từ nguồn bị giới hạn. Mục đích của lớp này là định nghĩa vị trí của mẫu tin hiện hành trong nguồn dữ liệu và kết hợp tất cả dãy bindings khi mẫu tin hiện hành này bị thay đổi. Ví dụ sau sẽ hiển thị hai trường từ bảng Product và bao gồm một cách để di chuyển giữa các mẫu tin bằng các phương tiện của một control TrackBar. Đoạn mã : using System; using System.Windows.Forms; using System.Data; using System.Data.SqlClient; public class ScrollingDataBinding : System.Windows.Forms.Form { Trang 181
  28. Khoa CNTT [MÔI TRƯỜNG VÀ CÔNG CỤ LẬP TRÌNH] private Button retrieveButton; private TextBox textName; private TextBox textQuan; private TrackBar trackBar; private DataSet ds; Ứng dụng trên tạo cửa sổ và tất cả control cho cửa sổ đó bên trong một hàm khởi tạo ScrollingDataBinding: public ScrollingDataBinding() { this.AutoScaleBaseSize = new System.Drawing.Size(5, 13); this.ClientSize = new System.Drawing.Size(464, 253); this.Text = "09_ScrollingDataBinding"; this.retrieveButton = new Button(); retrieveButton.Location = new System.Drawing.Point(4, 4); retrieveButton.Size = new System.Drawing.Size(75, 23); retrieveButton.TabIndex = 1; retrieveButton.Anchor = AnchorStyles.Top | AnchorStyles.Left; retrieveButton.Text = "Retrieve"; retrieveButton.Click += new System.EventHandler (this.retrieveButton_Click); this.Controls.Add(this.retrieveButton); this.textName = new TextBox(); textName.Location = new System.Drawing.Point(4, 31); textName.Text = "Please click retrieve "; textName.TabIndex = 2; textName.Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right ; Trang 182
  29. Khoa CNTT [MÔI TRƯỜNG VÀ CÔNG CỤ LẬP TRÌNH] textName.Size = new System.Drawing.Size(456, 20); textName.Enabled = false; this.Controls.Add(this.textName); this.textQuan = new TextBox(); textQuan.Location = new System.Drawing.Point(4, 55); textQuan.Text = ""; textQuan.TabIndex = 3; textQuan.Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Top; textQuan.Size = new System.Drawing.Size(456, 20); textQuan.Enabled = false; this.Controls.Add(this.textQuan); this.trackBar = new TrackBar(); trackBar.BeginInit(); trackBar.Dock = DockStyle.Bottom ; trackBar.Location = new System.Drawing.Point(0, 275); trackBar.TabIndex = 4; trackBar.Size = new System.Drawing.Size(504, 42); trackBar.Scroll += new System.EventHandler(this.trackBar_Scroll); trackBar.Enabled = false; this.Controls.Add(this.trackBar); } Khi nút Retrieve được click, sự kiện handler chọn tất cả mẫu tin từ bảng Product và lưu trữ trong Dataset riêng ds: protected void retrieveButton_Click(object sender, System.EventArgs e) { retrieveButton.Enabled = false ; Trang 183
  30. Khoa CNTT [MÔI TRƯỜNG VÀ CÔNG CỤ LẬP TRÌNH] ds = CreateDataSet(); Tiếp theo là hai control văn bản được giới hạn textName.DataBindings.Add("Text" , ds , "Products.ProductName"); textQuan.DataBindings.Add("Text" , ds , "Products.QuantityPerUnit"); trackBar.Minimum = 0 ; trackBar.Maximum = this.BindingContext[ds,"Products"].Count – 1; textName.Enabled = true; textQuan.Enabled = true; trackBar.Enabled = true; } Ở đây chúng ta có một mẫu tin cuộn để phản ứng lại với sự di chuyển của TrackBar: protected void trackBar_Scroll(object sender , System.EventArgs e) { this.BindingContext[ds,"Products"].Position = trackBar.Value; } private DataSet CreateDataSet() { string source = "server=(local)\\NetSDK;" + "uid=QSUser;pwd=QSPassword;" + "database=northwind"; string customers = "SELECT * FROM Products"; SqlConnection con = new SqlConnection(source); SqlDataAdapter da = new SqlDataAdapter(customers , con); Trang 184
  31. Khoa CNTT [MÔI TRƯỜNG VÀ CÔNG CỤ LẬP TRÌNH] DataSet ds = new DataSet(); da.Fill(ds , "Products"); return ds; } static void Main() { Application.Run(new ScrollingDataBinding()); } } Khi dữ liệu được khôi phục, vị trí lớn nhất trên track bar được cài là số lượng của mẫu tin. Sau đó, trong phương thức scroll ở trên, chúng ta cài vị trí của BindingContext cho DataTable products đến vị trí của scroll bar thumb. Nó thay đổi mẫu tin hiện hành từ DataTable, vì thế tất cả control giới hạn đến hàng hiện hành. Trang 185
  32. Khoa CNTT [MÔI TRƯỜNG VÀ CÔNG CỤ LẬP TRÌNH] Chương 5: File Operations Tổng quan Trong chương này chúng ta sẽ khảo sát làm thế nào để thực hiện các nhiệm vụ như đọc từ file và viết ra file và hệ thống đăng ký (Registry) trong C#. Cụ thể chúng ta sẽ được học. Cấu trúc thư mục, tìm kiếm file và folder hiện diện và kiểm tra thuộc tính của chúng. Di chuyển, sao chép, huỷ các file và folder Đọc và ghi văn bản trong các file Đọc và ghi các khoá trong Registry Microsoft cung cấp rất nhiều mô hình đối tượng trực giác mà chúng ta sẽ được khào sát trong chương này, chúng ta cũng biết cách sử dụng các lớp cơ bản của .NET để thực hiện các nhiệm vụ được đề cập ở trên. 5.1 Quản lý tập tin hệ thống Các lớp dưới đây được sử dụng để duyệt qua các file hệ thống và cách thức thực hiện như di chuyển, sao chép, huỷ các file được hiển thị trên sơ đồ. Namespace của mỗi lớp đặt trong ngoặc trong sơ đồ. Trang 186
  33. Khoa CNTT [MÔI TRƯỜNG VÀ CÔNG CỤ LẬP TRÌNH] Mục đích của các lớp trình bày dưới đây: System.MarshalByRefObject – Lớp đối tượng cơ sở cho các lớp của .NET nó điều khiển từ xa; cho phép điều hành dữ liệu giữa các vùng ứng dụng. FileSystemInfo – Lớp đối tượng cơ sở biểu diễn các file đối tượng hệ thống FileInfo and File – Các lớp này thể hiện một file trên file hệ thống DirectoryInfo and Directory – Các lớp này thể hiện một folder trên tập tin hệ thống Path – Lớp này chứa các bộ phận tĩnh dùng chế tác các pathnames 5.1.1 Các lớp .NET thể hiện các File và Folder Trước khi xem làm thế nào bạn có thể lấy dữ liệu từ các tập tin hoặc viết dữ liệu lên tập tin, .NET hỗ trợ thế nào những thao tác liên quan đến tập tin và thư mục. Trên Trang 187
  34. Khoa CNTT [MÔI TRƯỜNG VÀ CÔNG CỤ LẬP TRÌNH] .NET Framework, namespace System.IO là vùng của các thư viện lớp dành cho những dịch vụ liên quan đến xuất nhập dữ liệu dựa trên tập tin. Ở hình trên ta thấy System.IO cung cấp cho bạn 4 lớp (Directory, File, DirectoryInfo, FileInfo) cho phép bạn thao tác với tập tin riêng lẽ cũng như tương tác với cấu trúc thư mục của máy. Hai lớp đầu Directory và File cho phép những thao tác tạo, gỡ bỏ và thao tác khác nhau trên các tập tin và thư mục. Hai lớp có liên hệ mật thiết DirectoryInfo và FileInfo cũng có những chức năng tương tự nhưng các thành viên không phải là static, nên muốn triệu gọi các thành viên trước tiên bạn phải tạo một thể hiện đối tượng lớp.Hai lớp đầu Directory và File dẫn xuất từ lớp System.Object trong khi DirectoryInfo và FileInfo dẫn xuất từ lớp FileSystemInfo. Lớp FileSystemInfo là lớp cơ bản abstract có một số thuộc tính và phương thức cung cấp những thông tin liên quan đến một tập tin và thư mục. 5.1.1.1 Các thuộc tính của lớp cơ bản FileSystemInfo: Name Description CreationTime Thời gian file, folder được tạo DirectoryName (FileInfo), Tên đường dẫn của folder chức đựng Parent (DirectoryInfo) Exists Xác định file ,folder hiện hữu Extension Tên mở rộng của file, trả về khoảng trắng nếu là folder FullName Tên đường dẫn của file ,folder LastAccessTime Thời gian file, folder truy xuất làn cuối Trang 188
  35. Khoa CNTT [MÔI TRƯỜNG VÀ CÔNG CỤ LẬP TRÌNH] Name Description LastWriteTime Time file or folder was last modified Name Name of the file or folder Root Đường dẫn gốc Length Kích thước file tính bằng bytes (chỉ FileInfo ) Các phương thức bạn có thể thực hiện như sau: Name Purpose Create() Tạo một folder hoặc một file rỗng Delete() Huỷ file, folder MoveTo() Di chuyển hoặc sửa tên file, folder. CopyTo() (FileInfo only) Sao chép file, không sao chép fương thức cho folders. GetDirectories() (DirectoryInfo only) Trả về một mảng các đối tượng của DirectoryInfo đại diện tát cả folders được chứa trong folder này. GetFiles() (DirectoryInfo only) Trả về một mảng các đối tượng của FileInfo đại diện tát cả folders được chứa trong folder này Trang 189
  36. Khoa CNTT [MÔI TRƯỜNG VÀ CÔNG CỤ LẬP TRÌNH] Name Purpose GetFileSystemObjects() (DirectoryInfo only) Trả về đối trương FileInfo và DirectoryInfo như mảng của tham khảoFileSystemInfo . 5.1.1.2 Tạo một đối tượng DirectoryInfo Bạn bắt đầu làm việc với lớp DirectoryInfo bằng cách khai báo một đường dẫn cụ thể ví dụ : "C:\", "D:\WINNT", . . .nếu bạn muốn truy cập thư mục của ứng dụng đang thi hành bạn dùng ký hiệu "." thí dụ : //Tạo một thư mục mới từ thư mục hiện hành trở đi DirectoryInfo dirl = new DirectoryInfo("."); //Tạo mộtthư mục mới từ thư mục C:\Foo\Bar trở đi DirectoryInfo dir2 = new DirectoryInfo(@"C:\Foo\Bar"); 5.1.2 Lớp Path Lớp Path không là một lớp mà bạn khai báo cụ thể, Đúng ra, nó trình bày các phương thức tỉnh để thực hiện các phép toán trên tên đường dẫn dễ dàng hơn. ví dụ bạn muốn hiển thị tên đường dẫn cho một file ReadMe.txt trong folder C:\My Documents. Bạn có thể tìm đường dẫn đến file như sau: Console.WriteLine(Path.Combine(@"C:\My Documents", "ReadMe.txt")); Sử dụng lớp Path dễ dàng hơn nhiều so với khi bạn thực hiện các ký hiệu bằng tay nhất là bởi vì lớp Path nhận biết được các định dạng khác nhau của đường dẫn trên các hệ điều dành khác nhau. Tại lúc viết, Windows chỉ hỗ trợ hệ điều hành được hỗ trợ bởi Trang 190
  37. Khoa CNTT [MÔI TRƯỜNG VÀ CÔNG CỤ LẬP TRÌNH] .NET nhưng nếu .NET sử dụng trên Unix, Path sẽ dùng / thay cho \ làm dấu ngăn cách tên đường dẫn. Path.Combine() là phương thức mà lớp này thường hay sử dụng nhưng Path cũng thực hiện những phương thức khác để cung cấp thông tin về đường dẫn hoặc yêu cầu định dạng của nó. 5.1.3 Thí dụ : A File Browser Phần này trình bày ví dụ hướng dẫn làm thế nào để trình duyệt thư mục và hiển thị thuộc tính của file: Một thí dụ ứng dụng C# gọi FileProperties, nó trình bày một giao diện người dùng đơn giản cho phép bạn trình duyệt các file hệ thống và hiển thị thời gian tạo thành, thời gian truy xuất làn cuối, kích thước file. Ứng dụng FileProperties nhìn như sau. Bạn gõ tên của folder hoặc tên file trong textbox chính ở phía trên của cửa sổ và nhấn vào nút Display. Nội dung của folder được tạo trên listbox. Màn hình sẽ hiễn thị thuộc tính file đang được dùng xem xét một folder: Trang 191
  38. Khoa CNTT [MÔI TRƯỜNG VÀ CÔNG CỤ LẬP TRÌNH] Nếu bạn chỉ muốn hiển thị creation time, last access time, and last modification time cho folders – DirectoryInfo thực thi các thuộc tính thích hợp, chúng ta chỉ chọn vào thuộc tính của chúng. Ta tạo một dự án như sau standard C# Windows application trong Visual Studio.NET, và thêm vào các textboxes và listbox từ Windows Forms area của toolbox. Chúng ta cũng đổi tên các điều khiển một cách trực giác như textBoxInput, textBoxFolder, buttonDisplay, buttonUp, listBoxFiles, listBoxFolders, textBoxFileName, txtBoxCreationTime, textBoxLastAccessTime, textBoxLastWriteTime, và txtBoxFileSize. Sau đó chúng ta thêm vào đoạn code. Đầu tiên ta cần khai báo sử dụng System.IO namespace: Trang 192
  39. Khoa CNTT [MÔI TRƯỜNG VÀ CÔNG CỤ LẬP TRÌNH] using System; using System.Drawing; using System.Collections; using System.ComponentModel; using System.Windows.Forms; using System.Data; using System.IO; Chúng ta thêm trường thành viên vào main form: public class Form1 : System.Windows.Forms.Form { private string currentFolderPath; currentFolderPath sẽ giữ dường dẫn của thư mục và nội dung sẽ hiễn thị trên listboxes. Chúng ta cần thêm một số sự kiện ngõ ra như sau: User nhấn nút Display: Trong trường hợp này ta cần có nơi ngõ ra folder. Nếu là một folder ta đưa ra danh sách file và thư mục con, Nếu nó là file, chúng ta cho hiển thị thuộc tính ra textboxes. User nhấn lên file trên listbox: chúng ta chỉ cho hiển thị thuộc tính của file trên textboxes. User nhấn trên một folder trên Folders listbox: chúng ta xoá tất cả các điều khiển và hiển thị nội dung của thư mục con trên listboxes. User nhấn trên nút Up : chúng ta xoá tất cả điều khiển và hiển thị thư mục cha trên listboxes. Trang 193
  40. Khoa CNTT [MÔI TRƯỜNG VÀ CÔNG CỤ LẬP TRÌNH] Trước tiên ta lên danh sách các sự kiện, và làm rỗng nội dung của các điều khiển: protected void ClearAllFields() { listBoxFolders.Items.Clear(); listBoxFiles.Items.Clear(); textBoxFolder.Text = ""; textBoxFileName.Text = ""; textBoxCreationTime.Text = ""; textBoxLastAccessTime.Text = ""; textBoxLastWriteTime.Text = ""; textBoxFileSize.Text = ""; } thứ hai ta định nghĩa một fương thức, DisplayFileInfo(), nó thực hiện tiến trình hiển thị thông tin lên textboxes. Phương thức này lấy một thông số, tên đường dẫn file, và làm việc bởi một đối tượng FileInfo dựa trên path này: protected void DisplayFileInfo(string fileFullName) { FileInfo theFile = new FileInfo(fileFullName); if (!theFile.Exists) throw new FileNotFoundException("File not found: " + fileFullName); textBoxFileName.Text = theFile.Name; textBoxCreationTime.Text = theFile.CreationTime.ToLongTimeString(); Trang 194
  41. Khoa CNTT [MÔI TRƯỜNG VÀ CÔNG CỤ LẬP TRÌNH] textBoxLastAccessTime.Text = theFile.LastAccessTime.ToLongDateString(); textBoxLastWriteTime.Text = theFile.LastWriteTime.ToLongDateString(); textBoxFileSize.Text = theFile.Length.ToString() + " bytes"; } Ta tạo phương thức DisplayFolderList(), Nó hiển thị nội dung được cho bởi folder trong hai listboxes. protected void DisplayFolderList(string folderFullName) { DirectoryInfo theFolder = new DirectoryInfo(folderFullName); if (!theFolder.Exists) throw new DirectoryNotFoundException("Folder not found: " + folderFullName); ClearAllFields(); textBoxFolder.Text = theFolder.FullName; currentFolderPath = theFolder.FullName; // list all subfolders in folder foreach(DirectoryInfo nextFolder in theFolder.GetDirectories()) listBoxFolders.Items.Add(nextFolder.Name); // list all files in folder foreach(FileInfo nextFile in theFolder.GetFiles()) Trang 195
  42. Khoa CNTT [MÔI TRƯỜNG VÀ CÔNG CỤ LẬP TRÌNH] listBoxFiles.Items.Add(nextFile.Name); } protected void OnDisplayButtonClick(object sender, EventArgs e) { try { string folderPath = textBoxInput.Text; DirectoryInfo theFolder = new DirectoryInfo(folderPath); if (theFolder.Exists) { DisplayFolderList(theFolder.FullName); return; } FileInfo theFile = new FileInfo(folderPath); if (theFile.Exists) { DisplayFolderList(theFile.Directory.FullName); int index = listBoxFiles.Items.IndexOf(theFile.Name); listBoxFiles.SetSelected(index, true); return; } throw new FileNotFoundException("There is no file or folder with " + "this name: " + textBoxInput.Text); Trang 196
  43. Khoa CNTT [MÔI TRƯỜNG VÀ CÔNG CỤ LẬP TRÌNH] } catch(Exception ex) { MessageBox.Show(ex.Message); } } Trong đoạn code trên chúng ta thành lập nếu văn bản cung cấp trình bày một folder hoặc một file bởi thể hiện DirectoryInfo và FileInfo instances and khảo sát thuộc tính có sẵn của mỗi đối tượng Nếu không có sẵn chúng ta đưa ra ngoại lệ. protected void OnListBoxFilesSelected(object sender, EventArgs e) { try { string selectedString = listBoxFiles.SelectedItem.ToString(); string fullFileName = Path.Combine(currentFolderPath, selectedString); DisplayFileInfo(fullFileName); } catch(Exception ex) { MessageBox.Show(ex.Message); } } Trang 197
  44. Khoa CNTT [MÔI TRƯỜNG VÀ CÔNG CỤ LẬP TRÌNH] Bộ quản lý sự kiện cho chọn lựa của folder trong Folders listbox được thi hành trong cách tương tự ngoại trừ trường hợp chúng ta gọi phương thức DisplayFolderList() để cập nhật nội dung của listboxes: protected void OnListBoxFoldersSelected(object sender, EventArgs e) { try { string selectedString = listBoxFolders.SelectedItem.ToString(); string fullPathName = Path.Combine(currentFolderPath, selectedString); DisplayFolderList(fullPathName); } catch(Exception ex) { MessageBox.Show(ex.Message); } } Cuối cùng khi nút Up được nhấn , DisplayFolderList() phải cũng được gọi ngoại trừ lúc này chúng ta cần nhận được đường dẫn của cha thư mục hiện hành được hiển thị. protected void OnUpButtonClick(object sender, EventArgs e) { try { string folderPath = new FileInfo(currentFolderPath).DirectoryName; Trang 198
  45. Khoa CNTT [MÔI TRƯỜNG VÀ CÔNG CỤ LẬP TRÌNH] DisplayFolderList(folderPath); } catch(Exception ex) { MessageBox.Show(ex.Message); } } 5.2 Di chuyển, Sao chép, Huỷ File Chúng ta vừa mới đế cập di chuyển và huỷ files hoặc folders bằng phương thức MoveTo() và Delete() của lớp FileInfo và DirectoryInfo. Các phương thức tương đương nhau trên các lớp File và Directory là Move() và Delete(). Lớp FileInfo và File cũng có cách thức thực hiện tương tự, CopyTo() and Copy(). Không có fương thức nào copy các folder, tuy nhiên bạn có thể copy từng file trong folder. Tất cả các phương thức này đều hoàn toàn trực giác, bạn có thể tìm kiếm chi tiết trong help MSDN. Trong phần này chúng ta sẽ học cách gọi các phương thức tỉnh Move(), Copy(), và Delete() trong lớp File.Để thực hiện chúng ta dùng bài học phần trước FileProperties làm ví dụ, FilePropertiesAndMovement. Ví dụ này ta sẽ thêm tính năng mỗi lần thuộc tính của một file được hiển thị, ứng dụng này sẽ cho ta chọn lựa thêm xoá file hoặc di chuyển hoặc sao chép nó đến vị trí khác 5.2.1 Ví dụ: FilePropertiesAndMovement Ví dụ mới như sau: Trang 199
  46. Khoa CNTT [MÔI TRƯỜNG VÀ CÔNG CỤ LẬP TRÌNH] Từ hình trên chúng ta có thể thấy nó rất giống nhau trong lần xuất hiện ở ví dụ FileProperties, Ngoại trừ chúng có thêm một nhóm gồm ba nút button và một textbox tại phía dưới cửa xổ. Các điều khiển này chỉ hiện khi ví dụ hiển thị thuộc tính của một file- Lần khác chúng bị ẩn, Khi thuộc tính của file được hiển thị , FilePropertiesAndMovement tự động đặt tên đầy đủ đường dẫn của file đó ở cuối của xổ trong textbox. User có thể nhấn bất kỳ buttons để thực hiện phép toán thích hợp. Khi chương trình chạy một message box tương ứng được hiển thị xác nhận hành động. Trang 200
  47. Khoa CNTT [MÔI TRƯỜNG VÀ CÔNG CỤ LẬP TRÌNH] Để mã hoá chúng ta cần thêm các điều khiển thích hợp, giống như thêm các sự kiện điều khiển cho ví dụ FileProperties . Chúng ta tạo các controls mới với tên buttonDelete, buttonCopyTo, buttonMoveTo, và textBoxNewPath. Chúng ta sẽ thấy sự kiện điều kiện nhận được khi user nhấn vào Delete button; protected void OnDeleteButtonClick(object sender, EventArgs e) { try { string filePath = Path.Combine(currentFolderPath, textBoxFileName.Text); string query = "Really delete the file\n" + filePath + "?"; if (MessageBox.Show(query, "Delete File?", MessageBoxButtons.YesNo) == DialogResult.Yes) { File.Delete(filePath); DisplayFolderList(currentFolderPath); Trang 201
  48. Khoa CNTT [MÔI TRƯỜNG VÀ CÔNG CỤ LẬP TRÌNH] } } catch(Exception ex) { MessageBox.Show("Unable to delete file. The following exception" + " occurred:\n" + ex.Message, "Failed"); } } Đoạn code thực hiện phương thức này sẽ có phần nắm bắt lỗi, thông báo sẽ lỗi không thể xoá được nếu file xoá đang thực hiện trên một tiến trình khác. Chúng ta xây dựng đường dẫn của file của file bị xoá từ trường CurrentParentPath , Nơi nó chứa dường dẫn thư mục cha, và tên file trong textBoxFileName textbox: Phương thức di chuyển và sao chép file được cấu trúc : protected void OnMoveButtonClick(object sender, EventArgs e) { try { string filePath = Path.Combine(currentFolderPath, textBoxFileName.Text); string query = "Really move the file\n" + filePath + "\nto " + textBoxNewPath.Text + "?"; if (MessageBox.Show(query, "Move File?", MessageBoxButtons.YesNo) == DialogResult.Yes) Trang 202
  49. Khoa CNTT [MÔI TRƯỜNG VÀ CÔNG CỤ LẬP TRÌNH] { File.Move(filePath, textBoxNewPath.Text); DisplayFolderList(currentFolderPath); } } catch(Exception ex) { MessageBox.Show("Unable to move file. The following exception" + " occurred:\n" + ex.Message, "Failed"); } } protected void OnCopyButtonClick(object sender, EventArgs e) { try { string filePath = Path.Combine(currentFolderPath, textBoxFileName.Text); string query = "Really copy the file\n" + filePath + "\nto " + textBoxNewPath.Text + "?"; if (MessageBox.Show(query, "Copy File?", MessageBoxButtons.YesNo) == DialogResult.Yes) { Trang 203
  50. Khoa CNTT [MÔI TRƯỜNG VÀ CÔNG CỤ LẬP TRÌNH] File.Copy(filePath, textBoxNewPath.Text); DisplayFolderList(currentFolderPath); } } catch(Exception ex) { MessageBox.Show("Unable to copy file. The following exception" + " occurred:\n" + ex.Message, "Failed"); } } Chúng ta cũng tạo buttons và textbox mới được đánh dấu enabled và disabled ở thời điểm thích hợp để enable chúng khi chúng ta hiển thị nội dung của file, chúng ta cần thêm đoạn code sau: protected void DisplayFileInfo(string fileFullName) { FileInfo theFile = new FileInfo(fileFullName); if (!theFile.Exists) throw new FileNotFoundException("File not found: " + fileFullName); textBoxFileName.Text = theFile.Name; textBoxCreationTime.Text = theFile.CreationTime.ToLongTimeString(); textBoxLastAccessTime.Text = theFile.LastAccessTime.ToLongDateString(); textBoxLastWriteTime.Text = theFile.LastWriteTime.ToLongDateString(); Trang 204
  51. Khoa CNTT [MÔI TRƯỜNG VÀ CÔNG CỤ LẬP TRÌNH] textBoxFileSize.Text = theFile.Length.ToString() + " bytes"; // enable move, copy, delete buttons textBoxNewPath.Text = theFile.FullName; textBoxNewPath.Enabled = true; buttonCopyTo.Enabled = true; buttonDelete.Enabled = true; buttonMoveTo.Enabled = true; } Chúng ta cũng cần thay đổi DisplayFolderList: protected void DisplayFolderList(string folderFullName) { DirectoryInfo theFolder = new DirectoryInfo(folderFullName); if (!theFolder.Exists) throw new DirectoryNotFoundException("Folder not found: " + folderFullName); ClearAllFields(); DisableMoveFeatures(); textBoxFolder.Text = theFolder.FullName; currentFolderPath = theFolder.FullName; // list all subfolders in folder Trang 205
  52. Khoa CNTT [MÔI TRƯỜNG VÀ CÔNG CỤ LẬP TRÌNH] foreach(DirectoryInfo nextFolder in theFolder.GetDirectories()) listBoxFolders.Items.Add(NextFolder.Name); // list all files in folder foreach(FileInfo nextFile in theFolder.GetFiles()) listBoxFiles.Items.Add(NextFile.Name); } DisableMoveFeatures là một hàm tiện ích nhỏ nó disables các controls mới: void DisableMoveFeatures() { textBoxNewPath.Text = ""; textBoxNewPath.Enabled = false; buttonCopyTo.Enabled = false; buttonDelete.Enabled = false; buttonMoveTo.Enabled = false; } Chúng ta cần thêm phương thức ClearAllFields() để xoá các textbox thêm vào: protected void ClearAllFields() { listBoxFolders.Items.Clear(); listBoxFiles.Items.Clear(); textBoxFolder.Text = ""; Trang 206
  53. Khoa CNTT [MÔI TRƯỜNG VÀ CÔNG CỤ LẬP TRÌNH] textBoxFileName.Text = ""; textBoxCreationTime.Text = ""; textBoxLastAccessTime.Text = ""; textBoxLastWriteTime.Text = ""; textBoxFileSize.Text = ""; textBoxNewPath.Text = ""; } 5.3 Đọc và viết vào File Đọc và viết vào files nói chung rất đơn giản; tuy nhiên , Điều này không phải bắt buộc biết các đối tượng DirectoryInfo hoặc FileInfo mà chúng ta vừa khảo sát.Thay vào đó chúng ta phải biết một số lớp trình bày nội dung chung gọi là stream, Điều này chúng ta sẽ khảo sát sau đây. 5.3.1 Streams Đọc và viết dữ liệu sẽ được thực hiện thông qua lớp stream. Stream là dòng dữ liệu chảy đi. Đây là một thực thể (entity) có khả năng nhận được hoặc tạo ra một "nhúm" dữ liệu. System.IO.Stream là một lớp abstract định nghĩa một số thành viên chịu hỗ trợ việc đọc/viết đồng bộ (synchronus) hoặc không đồng bộ (asynchronous) đối với khối trữ tin (nghĩa là một tập tin trên đĩa hoặc tập tin trên ký ức). Vì Stream là một lớp abstract, nên bạn chỉ có thể làm việc với những lớp được dẫn xuất từ Stream. Các hậu duệ của Stream tượng trưng dữ liệu như là một dòng dữ liệu thô dạng bytes (thay vì dữ liệu dạng văn bản). Ngoài ra, các lớp được dẫn xuất từ Stream hỗ trợ việc truy tìm (seek) nghĩa là một tiến trình nhận lấy và điều chỉnh vị trí trên một dòng chảy. Trước khi tìm hiểu những chức năng mà lớp Stream cung cấp, bạn nên xem qua các thành viên của lớp Stream. Trang 207
  54. Khoa CNTT [MÔI TRƯỜNG VÀ CÔNG CỤ LẬP TRÌNH] Ý tưởng của stream đã có từ lâu. Một stream là một đối tượng dùng để chuyển dữ liệu. Dữ liệu có thể được truyền theo hai hướng: Nếu dữ liệu được truyền từ nguồn bên ngoài vào trong chương trình của bạn, ta gọi là đọc dữ liệu Nếu dữ liệu được truyền từ chương trình của bạn ra nguồn bên ngoài , ta gọi là viết dữ liệu Thường thì nguồn bên ngoài sẽ là một file, ngoài ra nó còn bao gồm cả trường hợp sau: Đọc hoặc ghi dữ liệu trên mạng dùng giao thức mạng Đọc hoặc ghi đến một đường ống chỉ định Đọc hoặc ghi đến một vùng của bộ nhớ Các lớp có mối liên hệ trong namespace System.IO như hình sau: Trang 208
  55. Khoa CNTT [MÔI TRƯỜNG VÀ CÔNG CỤ LẬP TRÌNH] 5.3.2 Làm việc với Binary Files Reading and writing to binary files thường được làm việc với lớp FileStream . 5.3.2.1 Làm việc với FileStream Lớp FileStream đem lại việc thi công cho những thành viên của lớp abstract Stream theo một thể thức thích hợp đối với các file-base streaming giống như các lớp DirectoryInfo và FileInfo, lớp FileStream cho phép mở những tập tin hiện hữu cũng như tạo mới file. Khi tạo tập tin , lớp FileStream thường dùng những enum FileMode, FileAccess và FileShare // tạo một tập tin mới trên thư mục làm việc FileStream myFStream = new FileStream("test.dat",FileMode.OpenOrCreate, FileAccess.ReadWrite); 5.3.2.2 The FileStream Class FileStream được sử dụng đọc và viết dữ liệu vào hoặc từ một file. Để khởi tạo một FileStream, bạn cần 4 phần sau: file bạn muốn truy xuất . mode, cho biết bạn muốn mở file như thế nào. access, cho biết bạn muốn truy xuất file như thế nào – bạn định đọc hoặc viết file hoặc cả hai. share access – khả năng truy xuất file. Enumeration Values FileMode Append, Create, CreateNew, Open, OpenOrCreate, or Truncate Trang 209
  56. Khoa CNTT [MÔI TRƯỜNG VÀ CÔNG CỤ LẬP TRÌNH] Enumeration Values FileAccess Read, ReadWrite, or Write FileShare Inheritable, None, Read, ReadWrite, or Write 5.3.3 Làm việc với BufferedStream Khi bạn triệu gọi hàm Read() thì một công tác đọc dữ liệu cho đầy buffer từ đĩa được tiến hành. Tuy nhiên, để cho có hiệu năng, hệ điều hành thường phải đọc trong một lúc một khối lượng lớn dữ liệu tạm thời trữ trên bufer. Buffer hoạt động như mọt kho hàng. Một đối tượng Bufered stream cho phép hệ điều hành tạo buffer riêng cho mình dùng, rồi đọc dữ liệu vào hoặc viết dữ liệu lên ổ đĩa theo một khối lượng dữ liệu nào đó mà hệ điều hành thấy là có hiệu năng. Tuy nhiên, bạn xũng có thể ấn định chiều dài khối dữ liệu. Nhưng bạn nhớ cho là buffer sẽ chiêmd chỗ trong ký ức chứ không phải trên đĩa từ. Hiệu quả sử dụng đến buffer là ciệc xuất nhập dữ liệu chạy nhanh hơn. Một đối tượng BufferedStream được hình thành xung quanh một đối tượng Stream mà bạn đã tạo ra trước đó. Muốn sử dụng đến một BufferedStream bạn bắt đầu tạo một đối tượng Stream thông thường như trong thí dụ : stream inputstream = File.OpenRead(@"C;\test\source\folder3.cs "); stream outputstream = File.Openwrite(@"C:test\source\folder3.bak"); Một khi bạn đã có stream bình thường, bạn trao đối tượng này cho hàm constructor của buffere stream: BufferedStrream bufInput = new BufferedStream(inputstream); Trang 210
  57. Khoa CNTT [MÔI TRƯỜNG VÀ CÔNG CỤ LẬP TRÌNH] BufferedStream bufOutput =new BufferedStream(outputstream); Sau đó, bạn sử dụng BufferedStream như là một stream bình thường, bạn triệu gọi hàm Read() hoặc Write() như bạn đã làm trước kia. Hệ điều hành lo việc quản lý vùng đêm: while ((bytesRead = bufInput.Read(buffer, 0, SIZE_BUFF))>0) { bufOutput.Write(buffer, 0, bytesRead); } Chỉ có một khác biệt mà bạn phải nhớ cho là phải tuôn ghi (flush) nội dung của buffer khi bạn muốn bảo đảm là dữ liệu được ghi lên đĩa. bufOutput.Flush(); Lệnh trên bảo hệ điều hành lấy toàn bộ dữ liệu trên buffer cho tuôn ra ghi lên tập tin trên đĩa. 5.3.4 Làm việc với file văn bản Nếu bạn biết file bạn đang làm việc (đọc/viết) thuộc loại văn bản nghĩa là dữ liệu kiểu string, thì bạn nên nghĩ đến việc sử dụng đến các lớp StreamReader và StreamWriter. Cả hai lớp theo mặc nhiên làm việc với ký tự Unicode. Tuy nhiên bạn có thể thay đổi điều này bằng cách cung cấp một đối tượng quy chiếu được cấu hình một cách thích hợp theo System.Text.Reference. Nói tóm lại hai lớp này được thiết kế để thao tác dễ dàng các tập tin loại văn bản. Lớp StreamReader được dẫn xuất từ một lớp abstract mang tên TextReader cũng giống như String Reader. Lớp cơ bản TextReader cung cấp một số chức năng hạn chế cho mỗi hậu duệ, đặc biệt khả năng đọc và "liếc nhìn" (peek) lên một dòng ký tự (character stream). Trang 211
  58. Khoa CNTT [MÔI TRƯỜNG VÀ CÔNG CỤ LẬP TRÌNH] Lớp StreamWriter và StringWriter cũng được dẫn xuất từ một lớp abstract mang tên TextWriter; lớp này định nghĩa những thành viên cho phép các lớp dẫn xuât viết những dữ liệu văn bản lên một dòng văn bản nào đó. 5.3.4.1 Các thành viên của lớp TextWriter Tên phương thức Ý nghĩa Cho đóng lại các writer và giải phóng mọi nguồn lực chiếm Close() dụng Flush() Cho xoá sạch tất cả các buffer đối với writer hiện hành NewLine Thuộc tính này dùng làm hằng sang hằng Write() Viết một hằng lên text stream không có newline constant WriteLine() Viết một hằng lên text stream có newline constant 5.3.4.2 Ví dụ đọc, viết một tập tin văn bản: Ví dụ ReadWriteText trình bày cách sử dụng của lớp StreamReader và StreamWriter. Nó trình bày file được đọc vào và hiển thị Nó cũng có thể lưu file. Nó sẽ lưu bất kỳ file ở định dạng Unicode . Màn hình trình bày ReadWriteText được dùng hiển thị file CivNegotiations. Chúng ta có thể đọc được ở nhiều định dạng file khác. Trang 212
  59. Khoa CNTT [MÔI TRƯỜNG VÀ CÔNG CỤ LẬP TRÌNH] Chúng ta nhìn vào đoạn mã sau. Trước tiên ta thêm câu lệnh using , Từ đây bên cạnh System.IO, chúng ta sử dụng lớp StringBuilder từ System.Text namespace để xây dựng chuỗi trong textbox: using System.IO; using System.Text; Tiếp theo chúng ta thêm các trường cho lớp main form public class Form1 : System.Windows.Forms.Form { private OpenFileDialog chooseOpenFileDialog = new OpenFileDialog(); private string chosenFile; Chúng ta cũng cần thêm vài chuẩn mã Windows Forms để thực hiện điều khiển cho menu và hộp thoại: Trang 213
  60. Khoa CNTT [MÔI TRƯỜNG VÀ CÔNG CỤ LẬP TRÌNH] public Form1() { InitializeComponent(); menuFileOpen.Click += new EventHandler(OnFileOpen); chooseOpenFileDialog.FileOk += new CancelEventHandler(OnOpenFileDialogOK); } void OnFileOpen(object Sender, EventArgs e) { chooseOpenFileDialog.ShowDialog(); } void OnOpenFileDialogOK(object Sender, CancelEventArgs e) { chosenFile = chooseOpenFileDialog.FileName; this.Text = Path.GetFileName(chosenFile); DisplayFile(); } Từ đây chúng ta thấy mỗi khi người sử dụng nhấn OK để chọn một file trong hộp thoại, chúng ta gọi phương thức DisplayFile(), dùng để đọc file. void DisplayFile() { int nCols = 16; Trang 214
  61. Khoa CNTT [MÔI TRƯỜNG VÀ CÔNG CỤ LẬP TRÌNH] FileStream inStream = new FileStream(chosenFile, FileMode.Open, FileAccess.Read); long nBytesToRead = inStream.Length; if (nBytesToRead > 65536/4) nBytesToRead = 65536/4; int nLines = (int)(nBytesToRead/nCols) + 1; string [] lines = new string[nLines]; int nBytesRead = 0; for (int i=0 ; i 65536) break; char nextChar = (char)nextByte; if (nextChar < 16) nextLine.Append(" x0" + string.Format("{0,1:X}", (int)nextChar)); else if Trang 215
  62. Khoa CNTT [MÔI TRƯỜNG VÀ CÔNG CỤ LẬP TRÌNH] (char.IsLetterOrDigit(nextChar) || char.IsPunctuation(nextChar)) nextLine.Append(" " + nextChar + " "); else nextLine.Append(" x" + string.Format("{0,2:X}", (int)nextChar)); } lines[i] = nextLine.ToString(); } inStream.Close(); this.textBoxContents.Lines = lines; } Như vậy chúng ta đã mở được file nhờ phương thức DisplayFile(). bây giờ chúng ta xử lý cách để lưu file chúng ta thêm đoạn mã SaveFile(). Bạn nhìn vào phương thức SaveFile() chúng ta viết mỗi dòng ra textbox, bằng stream StreamWriter void SaveFile() { StreamWriter sw = new StreamWriter(chosenFile, false, Encoding.Unicode); foreach (string line in textBoxContents.Lines) sw.WriteLine(line); sw.Close(); } Trang 216
  63. Khoa CNTT [MÔI TRƯỜNG VÀ CÔNG CỤ LẬP TRÌNH] Bây giờ ta xem xét làm thế nào file được đọc vào. Trong quá trình xử lý thực sự chúng ta không biết có bao nhiêu dòng sẽ được chứa (cũng có nghĩa là có bao nhiêu ký tự (char)13 (char)10 tuần tự trong file đến khi nào kết thúc file) Chúng ta giải quyết vấn đề này bằng cách ban đầu đọc file vào trong lớp đại diện StringCollection, được nằm trong System.Collections.Specialized namespace. Lớp này được thiết kế để giữ một bộ của chuỗi có thể được mở rộng một cách linh hoạt. Nó thực thi hai phương thức : Add(), nó thêm một chuỗi vào bộ chọn lựa (collection) , và CopyTo(), nó sao chép string collection vào trong một mảng. Mỗi thành phần của đối tượng StringCollection object sẽ giữ 1 hàng của file. Bây giờ chúng ta sẽ xem xét phương thức ReadFileIntoStringCollection() . Chúng ta sử dụng StreamReader để đọc trong mỗi hàng. Khó khăn chính là cần đếm ký tự đọc để chắc chúng ta không vượt quá khả năng chứa đựng của textbox: StringCollection ReadFileIntoStringCollection() { const int MaxBytes = 65536; StreamReader sr = new StreamReader(chosenFile); StringCollection result = new StringCollection(); int nBytesRead = 0; string nextLine; while ( (nextLine = sr.ReadLine()) != null) { nBytesRead += nextLine.Length; if (nBytesRead > MaxBytes) break; Trang 217
  64. Khoa CNTT [MÔI TRƯỜNG VÀ CÔNG CỤ LẬP TRÌNH] result.Add(nextLine); } sr.Close(); return result; } Đến đây đoạn mã được hoàn thành. 5.4 Đọc và viết vào Registry Trong các của Windows từ Windows 95 trở đi Registry là trung tâm lưu trữ tất cả các thông tin cấu hình liên quan đến cài đặt Windows, sở thích người dùng, phần mền cài đặt, thiết bị. Hầu hết tất cả các phần mềm thương mại sử dụng Registry để chứa thông tin của chính nó, và các thành phần COM phải được đặt thông tin của chúng trong Registry để mà được gọi bởi các ứng dụng khách NET Framework đã giảm sự quan trọng của Registry đối với ứng dụng, vì assembly đã trở thành "tự cung tự cấp" do đó không cần thông tin đặc điết để trữ trên Registry. Registry giờ đây chỉ là nơi tiện lợi để bạn trữ thông tin về sở thích của người sử dụng (user preference). Namespace Trang 218
  65. Khoa CNTT [MÔI TRƯỜNG VÀ CÔNG CỤ LẬP TRÌNH] Microsoft.Win32 định nghĩa một vài lớp cho phép đọc hoặc viết system registry một cách dễ dàng. Trước tiên chúng ta cùng xem lại cấu trúc của Registry 5.4.1 The Registry Registry có một cấu trúc đẳng cấp giống như hệ thống các tập tin (file system). Cách thông thường để nhìn xem hoặc thay đổi nội dung của Registry là với một trong hai tiên ích: regedit.exe hoặc regedt32.exe hiện diện trong tất cả các phiên bản Windows, từ khi Window 95 trở thành chuẩn. Còn Regedt32.exe thì chỉ hiện diện trong Windows NT và Windows 2000, ít thân thiện so với regedit.exe nhưng cho phép truy cập vào thông tin an ninh mà regedit không có khả năng nhìn xem. Trong phần này chúng ta sử dụng regedit.exe tại khung đối thoại Run hoặc command promt Khi bạn khởi chạy regedit đầu tiên bạn sẽ thấy hình sau đây: Regedit có giao diện mang dáng dấp treeview/listview giống như Windows Explorer, khớp với cấu trúc đẳng cấp của bản thân Registry . Tuy nhiên chúng ta sẽ thấy có vài sự khác biệt. Trang 219
  66. Khoa CNTT [MÔI TRƯỜNG VÀ CÔNG CỤ LẬP TRÌNH] Trong một file system, các mắt cấp chóp có thể được xem là những partitions trên ổ đĩa , C:\, D:\, . . Trong Registry,tương đương với partition là registry hive. Các khuôn này có định và không thể thay đổi và có cả thảy là bảy. HKEY_CLASSES_ROOT (HKCR) chứa những chi tiết và các loại tập tin(.txt, .doc, and so on), và những ứng dụng nào có khả năng mở các tập tin loại nào. Ngoài ra nó còn chứa thông tin đăng ký đối với tất cả các cấu kiện COM (chiếm phần lớn Registry, vì Windows mang theo vô số thành phần COM ). HKEY_CURRENT_USER (HKCU) chứa chi tiết liên quan đến sở thích của người sử dụng hiện đang đang nhập trên máy tính HKEY_LOCAL_MACHINE (HKLM) là hive đồ sộ chứa chi tiết tất cả phần mềm và phấn cứng được cài đặt trên máy tính HKEY_USERS (HKUSR) chứa chi tiết liên quan đến sở thích của tất cả người sử dụng Như bạn có thể chờ đợi, nó cũng chứa hive HKCU đơn giản là một ánh xạ lên một trong những key trên HKEY_USERS. HKEY_CURRENT_CONFIG (HKCF) chứa đựng chi tiết liên quan đến phần cứng máy tính. Phần còn lại là hai key chứa thông tin mang tình trạng tạm thời và thay đổi thường xuyên: HKEY_DYN_DATA là một container tổng quát đối với bất cứ dữ liệu volatile nào cần lưu trữ đâu đó trên Registry HKEY_PERFORMANCE_DATA chứa thông tin liên quan đến thành tích ứng dụng đang chạy. Trong lòng các hive là một cấu trúc cây gồm các Registry key. Mỗi key (mục khoá) cũnggiống như một folder hoặc file trong một file system. Tuy nhiên có một khác Trang 220
  67. Khoa CNTT [MÔI TRƯỜNG VÀ CÔNG CỤ LẬP TRÌNH] biệt rất quan trọng . File system phân biệt giữa các files và folders nhưng Registry hiện diện chỉ toàn là key. Một key có thể chứa cả dữ liệu và các key khác. Nếu một key chứa dữ liệu thì lúc này nó sẽ hiện diện như là một loạt các trị Mỗi trị sẽ có một cái tên một kiểu dữ liệu và một trị Một key có thể có một trị mặc nhiên không được đặt tên Key HKCU\Control Panel\Appearance có 3 bộ trị có mang tên mặc dù trị mặc nhiên không chứa bất cứ dữ liệu nào. Cột Type chi tiết hoá kiểu dữ liệu của mỗi trị. Các mục vào vào Registry có thể được định dạng theo một trong 3 kiểu dữ liệu: REG_SZ gần như tương đương với .NET string REG_DWORD gần như tương đương với .NET unit REG_BINARY bản dãy các byte 5.4.2 The .NET Registry Classes Việc truy cập vào Registry trên .NET sẽ thông qua hai lớp Registry và RegistryKey thuộc amespace Microsoft.Win32. Một thể hiện của lớp RegistryKey tượng Trang 221
  68. Khoa CNTT [MÔI TRƯỜNG VÀ CÔNG CỤ LẬP TRÌNH] trưng cho một registry key. Lớp RegistryKey cung cấp những thành viên cốt lõi cho phép bạn làm việc với registry key. Lớp RegistryKey sẽ là lớp mà bạn sẽ dùng để làm việc với registry key. Ngược lại lớp Registry là lớp mà bạn chả bao giờ thể hiện. Vai trò của nó là cung cấp cho bạn những thể hiện RegistryKey tượng trưng cho key top-level những hive khác nhau để qua các thuộc tính static và có cả thảy 7 thuộc tính bao gồm ClassesRoot, CurrentConfig, CurrentUser, DynData, LocalMachine, PerformanceData, and Users. chắc chắn bạn đã biết thuộc tính nào chỉ Registry hive nào Do đó muốn có một thể hiện của một RegistryKey tượng trưng cho key HKLM, bạn viết RegistryKey hklm = Registry.LocalMachine; Nếu bạn muốn đọc một vài dữ liệu trên key HKLM\Software\Microsoft, bạn phải đi lấy qui chiếu về key như sau : RegistryKey hklm = Registry.LocalMachine; RegistryKey hkSoftware = hklm.OpenSubKey("Software"); RegistryKey hkMicrosoft = hkSoftware.OpenSubKey("Microsoft"); Một registry key được truy cập theo kiểu này chi cho phép bạn đọc mà thôi, Nếu bạn muốn có khả năng viết lên key (bao gồm viết lên trị của key, tạo hoặc gỡ bỏ cây con cái thuộc quyền), bạn phải sử dụng một OpenSubkey nhận thêm một thông số thứ hai thuộc kiểu bool cho biết quyền read-write đối với key. Ví dụ bạn muốn có khả năng thay đổi key Microsoft. RegistryKey hklm = Registry.LocalMachine; RegistryKey hkSoftware = hklm.OpenSubKey("Software"); RegistryKey hkMicrosoft = hkSoftware.OpenSubKey("Microsoft", true); Trang 222
  69. Khoa CNTT [MÔI TRƯỜNG VÀ CÔNG CỤ LẬP TRÌNH] Phương thức OpenSubKey() là một trong những hàm mà bạn triệu gọi nếu bạn chờ đợi key hiện hữu. Nếu nó không có thì nó sẽ trở về null preference. Còn nếu bạn muốn tạo một key mới bạn sẽ dùng CreateSubKey() (hàm này tự hoạt động cho quyền read write): RegistryKey hklm = Registry.LocalMachine; RegistryKey hkSoftware = hklm.OpenSubKey("Software"); RegistryKey hkMine = hkSoftware.CreateSubKey("MyOwnSoftware"); Một khi bạn đã có registry key bạn muốn đọc hoặc thay đổi, bạn có thể sử dụng các phương thức SetValue() hoặc GetValue() để đặt hoặc để lấy dữ liệu trên key. Thí dụ: RegistryKey hkMine = HkSoftware.CreateSubKey("MyOwnSoftware"); hkMine.SetValue("MyStringValue", "Hello World"); hkMine.SetValue("MyIntValue", 20); Đoạn mã trên sẽ đặt key về hai trị : MyStringValue sẽ mang kiểu dữ liệu REG_SZ, trong khi MyIntValue sẽ mang kiểu dữ liệu REG_DWORD. RegistryKey.GetValue() cũng như vậy Nó được định nghĩa trả về một quy chiếu đối tượng, nghĩa là trả về một quy chiếu string nếu nó thấy có kiểu dữ liệu REG_SZ, và int nếu phát hiện kiểu dữ liệu REG_DWORD: string stringValue = (string)hkMine.GetValue("MyStringValue"); int intValue = (int)hkMine.GetValue("MyIntValue"); Cuối cùng khi xong việc bạn phải cho đóng lại hkMine.Close(); Các thành phần của RegistryKey bao gồm thuộc tính và phương thức sau: Trang 223
  70. Khoa CNTT [MÔI TRƯỜNG VÀ CÔNG CỤ LẬP TRÌNH] 5.4.2.1 Properties Property Name Description Name Tên của key (read-only) SubKeyCount số lượng sub key ValueCount Các trị trên key 5.4.2.2 Methods Method Name Purpose Close() Đóng lại key CreateSubKey() Tạo một subkey của một tên được cho DeleteSubKey() Bỏ một key được chỉ định DeleteSubKeyTree() Gỡ bỏ một cách đệ quy một subkey DeleteValue() Tháo bỏ một tên trị từ một key GetSubKeyNames() Trả về một dãy chuỗi chứa tên của subkeys GetValue() Trả về một tên trị GetValueNames() Trả về một dãy chuỗi chứa tên của tất cả các trị của key OpenSubKey() Trả về một tham khảo đến một RegistryKey,hàm này cho tìm lại subkey Trang 224
  71. Khoa CNTT [MÔI TRƯỜNG VÀ CÔNG CỤ LẬP TRÌNH] Method Name Purpose SetValue() Hàm này cho đặt một trị được chỉ định. Trang 225