This article has been localized into Vietnamese by the community.
Lazy loading TreeView items
Quá trình thông thường khi sử dụng TreeView là liên kết với một bộ sưu tập các vật phẩm hoặc thêm thủ công từng cấp độ cùng một lúc. Tuy nhiên, trong một số trường hợp, bạn muốn trì hoãn việc tải các mục con của nút cho đến khi chúng thực sự cần thiết. Điều này đặc biệt hữu ích nếu bạn có một cây rất sâu, với nhiều cấp độ và nút con và một ví dụ tuyệt vời về điều này, là cấu trúc thư mục của máy tính Windows của bạn.
Mỗi ổ đĩa trên máy tính Windows của bạn có một loạt các thư mục con và mỗi thư mục con đó có các thư mục con bên trong chúng, v.v. Xoay vòng qua từng ổ đĩa và mỗi ổ đĩa thư mục con có thể trở nên cực kỳ tốn thời gian và TreeView của bạn sẽ sớm bao gồm rất nhiều nút, với tỷ lệ cao trong số chúng không bao giờ cần thiết. Đây là nhiệm vụ hoàn hảo cho TreeView được lazy-loaded, trong đó các thư mục con chỉ được load theo yêu cầu.
Để đạt được điều này, chúng tôi chỉ cần thêm một thư mục giả vào mỗi ổ đĩa hoặc thư mục con, sau đó khi người dùng mở rộng nó, chúng tôi xóa thư mục giả và thay thế bằng các giá trị thực. Đây là giao diện của ứng dụng của chúng tôi khi nó khởi động - vào thời điểm đó, chúng tôi chỉ nhận được một danh sách các ổ đĩa có sẵn trên máy tính:
Bây giờ bạn có thể bắt đầu mở rộng các nút và ứng dụng sẽ tự động tải các thư mục con. Nếu một thư mục trống, nó sẽ được hiển thị là trống khi bạn cố gắng mở rộng nó, như có thể nhìn thấy trên ảnh chụp màn hình tiếp theo:
Vậy nó được thực hiện như thế nào? Chúng ta hãy xem code:
<Window x:Class="WpfTutorialSamples.TreeView_control.LazyLoadingSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="LazyLoadingSample" Height="300" Width="300">
<Grid>
<TreeView Name="trvStructure" TreeViewItem.Expanded="TreeViewItem_Expanded" Margin="10" />
</Grid>
</Window>
using System;
using System.IO;
using System.Windows;
using System.Windows.Controls;
namespace WpfTutorialSamples.TreeView_control
{
public partial class LazyLoadingSample : Window
{
public LazyLoadingSample()
{
InitializeComponent();
DriveInfo[] drives = DriveInfo.GetDrives();
foreach(DriveInfo driveInfo in drives)
trvStructure.Items.Add(CreateTreeItem(driveInfo));
}
public void TreeViewItem_Expanded(object sender, RoutedEventArgs e)
{
TreeViewItem item = e.Source as TreeViewItem;
if((item.Items.Count == 1) && (item.Items[0] is string))
{
item.Items.Clear();
DirectoryInfo expandedDir = null;
if(item.Tag is DriveInfo)
expandedDir = (item.Tag as DriveInfo).RootDirectory;
if(item.Tag is DirectoryInfo)
expandedDir = (item.Tag as DirectoryInfo);
try
{
foreach(DirectoryInfo subDir in expandedDir.GetDirectories())
item.Items.Add(CreateTreeItem(subDir));
}
catch { }
}
}
private TreeViewItem CreateTreeItem(object o)
{
TreeViewItem item = new TreeViewItem();
item.Header = o.ToString();
item.Tag = o;
item.Items.Add("Loading...");
return item;
}
}
}
XAML là rất đơn giản và chỉ có một chi tiết thú vị là hiện nay: Cách chúng tôi đăng ký vào trường hợp mở rộng của TreeViewItem. Lưu ý rằng đây thực sự là TreeViewItem chứ không phải TreeView, nhưng vì sự kiện nổi lên, chúng tôi có thể chỉ chụp nó ở một nơi cho toàn bộ TreeView, thay vì phải đăng ký nó cho mỗi mục chúng tôi thêm vào tree. Sự kiện này được gọi mỗi khi một mục được mở rộng, mà chúng ta cần lưu ý để tải các mục con của nó theo yêu cầu.
Trong Code-behind, chúng tôi bắt đầu bằng cách thêm từng ổ đĩa được tìm thấy trên máy tính vào điều khiển TreeView. Chúng tôi gán phiên bản DriveInfo cho thuộc tính Tag để sau này chúng tôi có thể truy xuất nó. Lưu ý rằng chúng tôi sử dụng một phương thức tùy chỉnh để tạo TreeViewItem, được gọi là CreateTreeItem(), vì chúng tôi có thể sử dụng chính xác phương thức đó khi chúng tôi muốn tự động thêm một thư mục con sau này. Lưu ý trong phương thức này cách chúng ta thêm một mục con vào bộ sưu tập Mục, dưới dạng một chuỗi với dòng chữ "Loading...".
Tiếp theo là sự kiện TreeViewItem_Expanded. Như đã đề cập, sự kiện này được nêu ra mỗi khi một mục TreeView được mở rộng, vì vậy điều đầu tiên chúng ta làm là kiểm tra xem mục này đã được tải chưa, bằng cách kiểm tra xem các mục con hiện chỉ có một mục, đó là một chuỗi - nếu vậy, chúng tôi đã tìm thấy mục con "Loading...", có nghĩa là bây giờ chúng tôi sẽ tải nội dung thực tế và thay thế mục giữ chỗ bằng nó.
Bây giờ chúng ta sử dụng thuộc tính Tag của các mục để lấy tham chiếu đến đối tượng DriveInfo hoặc DirectoryInfo mà mục hiện tại đại diện, và sau đó chúng ta nhận được một danh sách các thư mục con, chúng ta thêm vào mục được nhấp, một lần nữa sử dụng phương thức CreateTreeItem(). Lưu ý rằng vòng lặp nơi chúng tôi thêm từng thư mục con nằm trong khối try..catch - điều này rất quan trọng, vì một số đường dẫn có thể không truy cập được, thường là vì lý do bảo mật. Bạn có thể lấy ngoại lệ và sử dụng nó để phản ánh điều này trong giao diện theo cách này hay cách khác.
Tổng kết
Bằng cách đăng ký vào sự kiện Expanded, chúng ta có thể dễ dàng tạo một TreeView được lazy-loaded, đây có thể là một giải pháp tốt hơn nhiều so với một sự kiện được tạo tĩnh trong một số tình huống.