Lập trình với C# Gvhd: Nguyễn Tấn Trần Minh Khang
119
13.1.2 Tạo một ứngdụng Windows Form khác
Trong ứngdụng trên ta đã thảo luận sơ qua về ứngdụng Windows Form, phần này
ta sẽ tạo một ứngdụng Windows khác thực tế hơn. Ứngdụng có tên là
FileCopier, cho phép chép hay xóa một hoặc nhiều tập tin từ vị trí này sang vị
trí khác. Mục đích củaứngdụng là minh họa sâu hơn về các kỹ năng lập trình C#và
giúp người đọc hiểu thêm về namespace
Windows.Forms
. Giao diện củaứng
dụng sau khi hoàn chỉnh sẽ như sau :
Lập trình với C# Gvhd: Nguyễn Tấn Trần Minh Khang
120
Hình 13-7 Giao diện người dùngcủaứngdụng FileCopier.
Giao diện củaứngdụng gồm các thành phần sau :
• Labels: Các tập tin nguồn (Source Files) and Thư múc đích (Target Directory).
•
Buttons: Bỏ các dấu chọn trên cây bên trái (Clear), Copy, Delete, and Cancel.
• Checkbox : ghi đè lên nếu đã có sẵn ( "Overwrite if exists" )
•
Checkbox : hiển thị đường dẫn của mục được trọn ở cây bên phải.
• Hai cây (TreeView) chứa tập tin.
Khi người dùng nhấn vào Button ‘Copy’ thì tất các tập tin được chọn ở cây bên
trái sẽ được chép qua cây bên phải, cũng như khi nhấn vào Button ‘Delete’ thì sẽ
xóa các tập tin được chọn.
13.1.2.1 Tạo giao diện cho ứngdụng
Đầu tiên ta tạo một dự án Windows Form mới có tên FileCopier. IDE sẽ hiển thị
màn hình thiết kế (
Designer
) lên, ta sẽ thực hiện kéo thả các Label, Button,
Checkbox và TreeView cho đến khi thích hợp như hình dưới đây :
Lập trình với C# Gvhd: Nguyễn Tấn Trần Minh Khang
121
Hình 13-8 Tạo giao diện ứngdụng bằng cách kéo thả dùng Designer
Sau khi tạo giao diện xong, ta đặt thuộc tính
CheckBoxes cho cây bên trái có tên
tvwSource thành true, còn cây bên phải có tên tvwTargetDir thành false, để thực
hiện ta đơn giản chỉ chọn và sửa đổi trên cửa sổ thuộc tính của từng đối tượng. Khi
ta nhấn đúp lên bất kỳ Điều khiển nào thì tự động Visual Studio .NET sẽ phát sinh
ra mã tương ứng để bắt sự kiện của Điều khiển đó và đặt con trỏ ( Cursor ) vào ngay
tại hàm đó, ta nhấn đúp vào Button “
Cancel
” và bổ sung mã như sau :
protected void btnCancel_Click( object sender, System.EventArgs e)
{
Application.Exit( );
}
13.1.2.2 Quản lý điều khiển TreeView
Trong ứngdụng này, hai điều khiển
TreeView
hoạt động tương tự nhau, ngoại trừ
điều khiển cây bên trái
tvwTargetDir
có thuộc tính
CheckBoxes
là
true
và liệt kê
cả tập tin lẫn thư mục, còn cây bên phải là
false
và chỉ liệt ke thư mục. Mặc nhiên
thì điều khiển cây cho phép chọn nhiều mục một lúc, nhưng ta sẽ chỉnh lại sao cho
chỉ cây bên trái
tvwSource mới được chọn nhiều mục một lúc,bên phải thì không.
Ta sẽ tạo ra một hàm đẩy dữ liệu vào cây :
private void FillDirectoryTree(TreeView tvw, bool isSource)
Có 2 tham số :
TreeView tvw
: điều khiển cây cần đẩy dữ liệu vào
Bool isSource: cờ xác định là dữ liệu đẩy cho cây. Nếu isSource
là true thì cây sẽ liệt kê cả tập tin và thư mục, false thì chỉ có
tập tin.
Hàm này sẽ được dùng chung cho cả hai điều khiển cây :
Lập trình với C# Gvhd: Nguyễn Tấn Trần Minh Khang
122
FillDirectoryTree(tvwSource, true);
FillDirectoryTree(tvwTargetDir, false);
Đối tượng TreeNode
Điều khiển
TreeView có một thuộc tính Nodes. thuộc tính này nhận vào một đối
tượng
TreeNodeCollection
, đối tượng này thực chất là một mảng chứa các đối
tượng
TreeNode
, mỗi một
TreeNode
là một nút trên cây.
Trước tiên ta cần khởi tạo cây về rỗng :
tvw.Nodes.Clear( );
Sau đó ta gọi hàm tĩnh GetLogicalDrives() của đối tượng Enviroment để lấy về tất
cả các ổ đĩa logic hiện đang có trên máy. Đối tượng Enviroment cung cấp các thông
tin như : tên máy tính, phiên bản hệ điều hành, hệ thống thư mục … trên máy tính
hiện hành.
string[] strDrives = Environment.GetLogicalDrives( );
strDrives sẽ chứa tên các ổ đĩa logic hiện có trên máy.
Sau đó ta sẽ duyệt qua từng ổ đĩa bằng cách dùng lệnh foreach. Với mỗi ổ đĩa logic,
ta gọi hàm GetDirectories() của đối tượng DirectoryInfo. Hàm này sẽ trả về danh
sách các đối tượng DirectoryInfo, chứa tất cả các tập tin và thư mục trên ổ đĩa logic
đó. Những tại đây ta không quan tâm đến kết quả mà nó trả về, mục đích ta gọi hàm
này chủ yếu là để kiểm tra xem các ổ đĩa có hợp lệ hay không, nếu có bất kỳ một lỗi
nào trên ổ đĩa thì hàm GetDirectories() sẽ quăng ra một ngoại lệ. Ta sẽ dùng khối
bắt lỗi try…catch để bắt lỗi này.
foreach (string rootDirectoryName in strDrives)
{
DirectoryInfo dir = new DirectoryInfo(rootDirectoryName);
dir.GetDirectories( );
}
Khi ỗ đĩa hợp lệ, ta sẽ tạo ra một TreeNode ứng với rootDirectoryName ổ đĩa đó,
chẳng hạn như : “C:\”, “D:\” …Rồi thêm TreeNode này vào điều khiển cây dùng
hàm Add() thông qua thuộc tính Nodes của cây.
TreeNode ndRoot = new TreeNode(rootDirectoryName);
tvw.Nodes.Add(ndRoot);
Tiếp theo ta tiến hành duyệt trên mọi thư mục con của đối tượng TreeNode gốc trên,
để làm điều này ta gọi hàm GetSubDirectoriesNodes( ), hàm này cần nhận vào các
đối số : TreeNode gốc, tên của nó và cờ xác định là có đẩy cả tập tin vào cây hay
không.
if (isSource)
{
GetSubDirectoryNodes(ndRoot, ndRoot.Text, true);
}
else
{
GetSubDirectoryNodes(ndRoot, ndRoot.Text, false);
}
Lập trình với C# Gvhd: Nguyễn Tấn Trần Minh Khang
123
Duyệt đệ qui trên các thư mục con
Hàm GetSubDirectoryNodes() bắt đầu bằng việc gọi hàm GetDirectories() để nhận
về một danh sách các đối tượng DirectoryInfo :
private void GetSubDirectoryNodes(
TreeNode parentNode, string fullName, bool getFileNames)
{
DirectoryInfo dir = new DirectoryInfo(fullName);
DirectoryInfo[] dirSubs = dir.GetDirectories( );
Ở đây ta thấy node truyền vào có tên là parentNode ( nút cha ), nghĩa là những nút
sau này sẽ được xem là nút con của nó. Bạn sẽ rõ hơn khi tìmhiểu hết hàm này.
Ta tiến hành duyệt qua danh sách các thư mục con dirSubs, bỏ qua các mục có trạng
thái là ẩn ( Hidden ).
foreach (Directory dirSub in dirSubs)
{
if ( (dirSub.Attributes & FileSystemAttributes.Hidden) != 0 )
{
continue;
}
FileSystemAttributes là biến có kiểu enum, nó chứa một số giá trị như : Archive,
Compressed, Encrypted, Hidden, Normal, ReadOnly …Nếu như mục hiện hành
không ở trạng thái ẩn, ta sẽ tạo ra một TreeNode mới với tham số là tên của nó. Sau
đó Thêm nó vào nút cha parentNode :
TreeNode subNode = new TreeNode(dirSub.Name);
parentNode.Nodes.Add(subNode);
Ta sẽ gọi lại đệ qui hàm GetDirectoriesNodes() để liệt kê hết mọi mục con trên thư
nút hiện hành, với ba thông số : nút được chuyển vào như nút cha, tên đường dẫn
đầy đủ của mục hiện hành và cờ trạng thái.
GetSubDirectoryNodes(subNode,dirSub.FullName,getFileNames);
Chú ý : Thuộc tính dirSubs.FullName sẽ trả về đường dẫn đầy đủ của
mục hiện hành ( “C:\dir1\dir2\file1” ), còn thuộc tính dirSubs.Name chỉ
trả về tên của mục hiện hành ( “file1”). Khi ta tạo ra một nút con
subNode, ta chỉ truyền cho nó tên của mục hiện hành, vì ta chỉ muốn
hiển thị thị tên của nó trên cây. Còn khi ta gọi đệ qui hàm
GetSubDirectoryNodes() thì ta cần truyền cho nó tên đường dẫn đầy đủ
của mục hiện hành, để có thể liệt kê toàn bộ mục con cùa thực mục đang
xét.
Đến đây chắc bạn đã hiểu được sự phân cấp của cấu trúc cây vàtại sao hàm
GetSubDirectoryNodes() cần truyền có đối số FullName.
Lấy về các tập tin trong thư mục
Nếu biến cờ getFileNames là True thì ta sẽ tiến hành lấy về tất cả các tập tin thuộc
thư mục. Để thực hiện ta gọi hàm GetFiles() của đối tượng DirectoryInfo, hàm này
sẽ trả về danh sách các đối tượng FileInfo. Ta sẽ duyệt qua danh sách này để lấy ra
Lập trình với C# Gvhd: Nguyễn Tấn Trần Minh Khang
124
tên của từng tập tin một, sau đó tạo ra một nút TreeNode với tên này, nút này sẽ
được thêm vào nút cha parentNode hiện hành.
13.1.2.3 Quản lý sự kiện trên điều khiển cây
Trong ứngdụng này, chúng ta sẽ phải quản lý một số sự kiện. Đầu tiên là sự kiện
người dùng nhấn lên ô CheckBox để chọn các tập tin hay thư mục ở cây bên phải
hay nhấn các nút ở cây bên phải. Tiếp theo là các sự kiện nhấn vào Button ‘Cancel’,
‘Copy’,’Delete’ hay ‘Clear’.
Ta sẽ khảo sát sự kiện trên điều khiển cây trước.
Sự kiện chọn một nút trên điều khiển cây bên trái
Khi người dùng muốn chọn một tập tin hay thư mục để chép hay xóa. Ứng với mỗi
lần chọn sẽ phát sinh ra một số sự kiện tương ứng. Ta sẽ bắt sự kiện AfterCheck của
điều khiển cây. Ta gõ vào các đoạn mã sau :
tvwSource.AfterCheck +=
new TreeViewEventHandler( this.tvwSource_AfterCheck );
Ta viết lệnh thực thi cho hàm bắt sự kiện AfterCheck có tên là
tvwSource_AfterCheck, hàm này có hai tham số : đầu tiên là biến Sender chứa
thông tin về đối tượng phát sinh ra sự kiện, thứ hai là đối tượng
TreeViewEventArgs chứa thông tin về sự kiện phát ra. Ta sẽ đánh dấu là chọn cho
thư mục được chọn và tất cả các tập tin hay thư mục con của thư mục đó thông qua
hàm SetCheck() :
protected void tvwSource_AfterCheck (
object sender, System.Windows.Forms.TreeViewEventArgs e)
{
SetCheck(e.node,e.node.Checked);
}
Hàm SetCheck() sẽ tiến hành thực hiện đệ qui trên nút hiện hành, hàm gồm hai
tham số : nút cần đánh dấu và cờ xác định là đánh dấu hay bỏ đánh dấu chọn, nếu
thuộc tính Count bằng không ( nghĩa là nút này là nút lá ) thì ta sẽ đánh dấu chọn
cho nút đó. Nếu không ta gọi đệ qui lại hàm SetCheck() :
private void SetCheck(TreeNode node, bool check)
{
node.Checked = check;
foreach (TreeNode n in node.Nodes)
{
if (node.Nodes.Count == 0)
{
node.Checked = check;
}
else
{
SetCheck(n,check);
}
}
}
. Mục đích của ứng dụng là minh họa sâu hơn về các kỹ năng l p trình C# và
gi p người đọc hiểu thêm về namespace
Windows.Forms
. Giao diện của ứng
dụng sau. Windows Form, phần này
ta sẽ tạo một ứng dụng Windows khác thực tế hơn. Ứng dụng có tên là
FileCopier, cho ph p ch p hay xóa một hoặc nhiều t p tin từ vị