Công nghệ Silverlight - Step by Step

Có thể coi SilverLight như một đối thủ nặng ký của Adobe Flash, ra đời sau, thừa hưởng tính ưu việt của các công nghệ hiện có, nhỏ gọn, (sẽ) đa nền tảng, bộ công cụ phát triển mạnh mẽ và hoàn chỉnh, và hơn hết là được phát triển bởi Microsoft - ông trùm số một trong thế giới phần mềm.

pdf26 trang | Chia sẻ: haohao89 | Lượt xem: 2066 | Lượt tải: 2download
Bạn đang xem trước 20 trang tài liệu Công nghệ Silverlight - Step by Step, để xem tài liệu hoàn chỉnh bạn click vào nút DOWNLOAD ở trên
Silverlight - Step by Step I. CHƯƠNG 1 Chào mừng bạn đến với SilverLight, một công nghệ đa nền tảng, cho phép xây dựng các ứng dụng tương tác trên Web không phụ thuộc trình duyệt và tương tác với server. Dùng SilverLight, bạn có thể xây dựng các loại ứng dụng sau: - Các ứng dụng nặng cho phép xem phim, nghe nhạc trên Internet - Các ứng dụng nhỏ, kiểu như game hoặc các thành phần tương tác khác - Các thành phần trực quan trên Web, hiển thị dữ liệu ... Có thể coi SilverLight như một đối thủ nặng ký của Adobe Flash, ra đời sau, thừa hưởng tính ưu việt của các công nghệ hiện có, nhỏ gọn, (sẽ) đa nền tảng, bộ công cụ phát triển mạnh mẽ và hoàn chỉnh, và hơn hết là được phát triển bởi Microsoft - ông trùm số một trong thế giới phần mềm. 1. Tạo dự án SilverLight Làm thế nào để đưa SilverLight vào trang Web của bạn ? Một dự án SilverLight tiêu biểu thường có 4 file: 1 file HTML để chứa Silverlight plug-in, 1 file silverlight.js, 1 file XAML và một file Javascript chứa các hàm hỗ trợ cho file HTML. Tài liệu này mô tả cách tạo ra một dự án Silverlight cơ bản và đưa thêm nội dung vào file HTML trong vòng 3 bước Trước khi bắt đầu Trước khi bắt đầu, bạn cần chuẩn bị một số thứ sau: - Silverlight plug-in: nếu chưa có, xin mời nhấn vào đây để cài đặt Silverlight. Silverlight plug-in là phần mềm chạy trên trình duyệt để xử lý nội dung Silverlight, nó cũng tương tự như Flash Player - Một file HTML: bạn sẽ cần file này để hiển thị nội dung Silverlight, bạn có thể tự tạo một file của riêng bạn hoặc copy từ đây - một trình soạn thảo văn bản: bạn sẽ cần nó để chỉnh sửa file HTML, bạn có thể dùng Notepad, UltraEdit hoặc EditPlus 2. Thêm các tham chiếu cần thiết vào file HTML Trong bước này, bạn sẽ thêm các tham chiếu đến các file Silverlight.js và createSilverlight.js vào trong trang HTML, đồng thời tạo một element để chứa plug-in Silverlight. File Silverlight.js là một file hỗ trợ viết bằng Javascript, nó cho phép nội dung Silverlight có thể hiển thị được trên nhiều nền tảng khác nhau. Bạn cũng sẽ tạo file createSilverlight.js trong bước 2. a. Lấy file Silverlight.js Bạn có thể lấy file này từ thư mục Tools trong bộ Silverlight 1.0 SDK. b. Mở file HTML và thêm đoạn mã sau vào phần . Nếu bạn chưa có sẵn một file HTML để dùng, nhấn nút phải chuột lên trên liên kết SampleHTMLPage.html và chọn "Save Target As..." để lưu file SampleHTMLPage.html vào cùng thư mục với file Silverlight.js. c. Tạo một file trống và đặt tên là createSilverlight.js, bạn sẽ dùng file này trong bước 3. d. Trong trang HTML(SampleHTMLPage.html), thêm một tham chiếu đến script khác trong phần , đặt thuộc tính src của tham chiếu là createSilverlight.js. Trang HTML của bạn giờ đã có những thành phần cơ bản sau: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" ""> A Sample HTML page 2. Tạo thành phần chứa Silverlight và khởi tạo trên trang HTML a. Thêm ba dòng sau vào giữa cặp thẻ , nơi bạn muốn Silverlight hiển thị: Bạn có thể thay đổi giá trị của ID trong thẻ nếu muốn. Nếu bạn đang tạo nhiều plug-in Silverlight trên cùng một trang web, làm lại bước này cho một cái và phải đảm bảo giá trị của ID là duy nhất. b. Tạo khối lệnh khởi tạo: ngay sau đoạn HTML bạn vừa thêm vào ở bước trước, hãy thêm đoạn HTML và script sau: // Retrieve the div element you created in the previous step. var parentElement = document.getElementById("mySilverlightPluginHost"); // This function creates the Silverlight plug-in. createMySilverlightPlugin(); Nếu đang tạo nhiểu plug-in Silverlight trên cùng trang, hãy lặp lại bước này cho mỗi cái, bảo đảm mỗi tên hàm là duy nhất, hoặc bạn có thể dùng một hàm nhận tham số ID mà bạn đã đặt giá trị duy nhất. Cũng phải đảm bảo một điều là mỗi đọan script phải nằm ngay sau thẻ DIV tương ứng mà bạn đã tạo ở bước trên. Bạn đã hoàn thành bước 2. File HTML của bạn giờ có nội dung như sau: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" ""> A Sample HTML page // Retrieve the div element you created in the previous step. var parentElement = document.getElementById("mySilverlightPluginHost"); // This function creates the Silverlight plug-in. createMySilverlightPlugin(); 3. Định nghĩa hàm khởi tạo đối tượng Silverlight plug-in Mở file createSilverlight.js file bạn đã tạo ở bước 1 và thêm vào hàm javascript sau: function createMySilverlightPlugin() { Silverlight.createObject( "myxaml.xaml", // Source property value. parentElement, // DOM reference to hosting DIV tag. "mySilverlightPlugin", // Unique plug-in ID value. { // Per-instance properties. width:'300', // Width of rectangular region of // plug-in area in pixels. height:'300', // Height of rectangular region of // plug-in area in pixels. inplaceInstallPrompt:false, // Determines whether to display // in-place install prompt if // invalid version detected. background:'#D6D6D6', // Background color of plug-in. isWindowless:'false', // Determines whether to display plug-in // in Windowless mode. framerate:'24', // MaxFrameRate property value. version:'1.0' // Silverlight version to use. }, { onError:null, // OnError property value -- // event handler function name. onLoad:null // OnLoad property value -- // event handler function name. }, null); // Context value -- event handler function name. } Đoạn script trên chứa một số tham số mà bạn có thể sẽ muốn tùy biến, kiểu như chiều cao và chiều rộng của plug-in (có thể đặt kích thước theo phần trăm %), tên của file XAML chứa nội dung Silverlight của bạn, và một giá trị chỉ ra plug-in của bạn sẽ hoạt động ở chế độ cửa sổ hay tòan màn hình. Nếu đang thêm nhiều plug-in vào cùng một trang, hãy tạo một hàm mới cho mỗi cái, hoặc dùng hàm tạo sử dụng tham số (xem lại 2b). Với cách nào bạn cũng đều phải đảm bảo rằng lời gọi khởi tạo chỉ đến ID của mỗi plug-in khác nhau ("mySilverlightPlugin" trong ví dụ trên). 4. Tạo file chứa nội dung Silverlight Bạn đã tạm hoàn thành file HTML, giờ hãy tạo ra nội dung cho đối tượng Silverlight. Tạo một file trống có tên "myxaml.xaml" trong cùng thư mục với file HTML. Nếu bạn đã đổi tham số tên file trong bước trước, hãy đặt lại tên file của bạn cho đúng với giá trị mới của bạn. Bước này là tùy chọn: nếu muốn dự án Silverlight của bạn có khả năng xử lý các sự kiện, tạo mã động, hoặc những gì khác cho phép tương tác với người dùng, bạn sẽ cần thêm một file script nữa. Tạo file một file Javascript và thêm tham chiếu đến nó trong file HTML, bạn có thể làm giống ví dụ sau, trong ví dụ này tên file được đặt là "my-script.js". File HTML của bạn giờ sẽ chứa các thành phần sau: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" ""> A Sample HTML page // Retrieve the div element you created in the previous step. var parentElement = document.getElementById("mySilverlightPluginHost"); createMySilverlightPlugin(); Tạo nhiều đối tượng Silverlight Nếu muốn tạo nhiều đối tượng Silverlight trên trang của bạn, hãy lặp lại các bước 2,3, và 4 cho mỗi đối tượng. Mỗi thẻ DIV (tạo trong bước 2a) phải có một giá trị ID duy nhất. Mỗi đoạn lệnh khởi tạo (trong bước 2b) phải nằm ngay sau thẻ DIV tương ứng được tạo ở bước trước (2a). Mỗi tham số ID của đối tượng cũng là duy nhất. Ghi chú: trong phần trên, các cụm từ "Silverlight plug-in", "đối tượng Silverlight" được dịch từ cụm từ "Silverlight plug-in instance" II. Chương 2 Trong bài trước, tạo một dự án Silverlight, bạn đã thêm một đối tượng Silverlight vào một trang HTML và tạo một file XAML trống. Bài này sẽ hướng dẫn bạn cách tạo nội dung Silverlight bên trong file XAML. Bước 1: tạo một đối tượng Canvas và khai báo namespace Mở file myxaml.xaml mà bạn đã tạo trong bài trước, tạo một Canvas và khai báo namespace cho Silverlight và XAML bằng cách copy đoạn mã sau vào file XAML của bạn: <Canvas xmlns="" xmlns:x=""> Mỗi file XAML Silverlight bắt đầu với một thẻ , trong đó có một thuộc tính xmlns dùng để khai báo namespace của Silverlight, và một thuộc tính khác là xmlns:x dùng để khai báo namespace cho XAML. Bước 2: Vẽ lên trên đối tượng Silverlight Cắt và dán doạn mã lệnh sau vào trong file XAML của bạn, giữa cặp thẻ rồi lưu lại. <Ellipse Height="200" Width="200" Stroke="Black" StrokeThickness="10" Fill="SlateBlue" /> Bước 3: Xem nội dung XAML của bạn Để xem nội dung hiển thị bởi XAML, nháy đúp vào file HTML. Bạn sẽ nhìn thấy một hình tròn màu tím với viền đen đậm. <Canvas xmlns="" xmlns:x=""> <Ellipse Height="200" Width="200" Stroke="Black" StrokeThickness="10" Fill="SlateBlue" /> Nếu máy của bạn có cài đặt WPF thi khi nháy đúp lên file XAML sẽ làm cho WPF chạy chứ không phải là Silverlight. Nhưng cũng đừng lo lắng về điều này, vì file XAML được đặt cùng chỗ với file HTML trên Web server nên người dùng không thể nháy đúp vào được. Xin chúc mừng! Bạn đã tạo ra được ứng dụng Silverlight đầu tiên !!! III. Chương 3 Một Canvas là một đối tượng được tạo ra để chứa các đối tượng điều khiển (control) và các đối tượng hình vẽ (shape). Tất cả các file XAML phải chứa ít nhất một Canvas và nó sẽ được coi là đối tượng gốc. Bài này giới thiệu về đối tượng Canvas và cách thêm, xác định vị trí và kích thước của các đối tượng con. Thêm một đối tượng vào Canvas Một Canvas chứa và đặt vị trí cho các đối tượng khác. Để thêm một đối tượng vào Canvas, bạn hãy chèn nó vào giữa cắp thẻ . Ví dụ sau sẽ thêm một hình ellipse và trong một Canvas. Vì Canvas là đối tượng gốc nên tốt hơn hết là bạn nên khai báo các thuộc tính về namespace (xmlns) cần thiết cho nó. <Canvas xmlns="" xmlns:x=""> <Ellipse Height="200" Width="200" Stroke="Black" StrokeThickness="10" Fill="SlateBlue" /> Một Canvas có thể chứa một số bất kỳ các đối tượng khác, thậm chí cả các Canvas khác. Đặt vị trí cho một đối tượng Để đặt vị trí cho một đối tượng trong Canvas, bạn đặt các thuộc tính Canvas.Left và Canvas.Top trên đối tượng đó. Thuộc tính Canvas.Left chỉ ra khoảng cách từ đối tượng đế cạnh bên trái của Canvas chứa nó, và Canvas.Top chỉ ra khoảng cách đến cạnh trên của Canvas. Ví dụ sau cũng vẫn dùng đối tượng ellipse trong cùng ví dụ trước như đặt lại ví trí của nó là 30 pixel (điểm trên màn hình) tính từ bên trái và 30 pixel tính từ phía trên của Canvas. <Canvas xmlns="" xmlns:x=""> <Ellipse Canvas.Left="30" Canvas.Top="30" Height="200" Width="200" Stroke="Black" StrokeThickness="10" Fill="SlateBlue" /> Hình minh họa sau sẽ giúp bạn hiểu hơn về hệ tọa độ được dùng trong Canvas và vị trí của hình ellipse trong ví dụ trước. z-order Thuật ngữ z-order được dùng để chỉ độ sâu (chiều thứ 3 trên hệ tọa độ xyz), hay nói cách khác là nếu có nhiều đối tượng nằm chồng lên nhau thì z-order sẽ xác định đối tượng nào nằm trên, đối tượng nào nằm dưới Mặc nhiên, z-order của các đối tượng trong một Canvas sẽ được xác định bởi thứ tự chúng được khai báo. Đối tượng nào được khai báo sau sẽ nằm lên trên đối tượng được khai báo trước. Ví dụ sau sẽ tạo ra 3 ellipse, bạn sẽ thấy đối tượng được khai báo sau cùng (màu xanh lá cây) sẽ nằm lên trên các đối tượng khác. <Canvas xmlns="" xmlns:x=""> <Ellipse Canvas.Left="5" Canvas.Top="5" Height="200" Width="200" Stroke="Black" StrokeThickness="10" Fill="Silver" /> <Ellipse Canvas.Left="50" Canvas.Top="50" Height="200" Width="200" Stroke="Black" StrokeThickness="10" Fill="DeepSkyBlue" /> <Ellipse Canvas.Left="95" Canvas.Top="95" Height="200" Width="200" Stroke="Black" StrokeThickness="10" Fill="Lime" /> Bạn có thể thay đổi điều này bằng cách đặt lại thuộc tính Canvas.ZIndex của đối tượng bên trong Canvas, giá trị càng cao thì sẽ nằm càng gần về phía trước, và càng thấp thì càng nằm ra sau. Ví dụ sau cũng sẽ tương tự như cái trước, chỉ có một thay đổi là các giá trị của z-order đã được đặt ngược lại, bạn sẽ thấy trong trường hợp này, hình ellipse màu bạc sẽ nằm lên trên cùng. <Canvas xmlns="" xmlns:x=""> <Ellipse Canvas.ZIndex="3" Canvas.Left="5" Canvas.Top="5" Height="200" Width="200" Stroke="Black" StrokeThickness="10" Fill="Silver" /> <Ellipse Canvas.ZIndex="2" Canvas.Left="50" Canvas.Top="50" Height="200" Width="200" Stroke="Black" StrokeThickness="10" Fill="DeepSkyBlue" /> <Ellipse Canvas.ZIndex="1" Canvas.Left="95" Canvas.Top="95" Height="200" Width="200" Stroke="Black" StrokeThickness="10" Fill="Lime" /> Chiều rộng và chiều cao Canvas, hình họa và nhiều thành phần khác đều có thuộc thuộc tính Width và Height cho phép chỉ ra kích thước của nó. Ví dụ sau sẽ tạo một hình ellipse cao 200 pixel và rộng 200 pixel, nhớ là không được dùng giá trị theo kiểu phần trăm %. <Canvas xmlns="" xmlns:x=""> <Ellipse Canvas.Left="30" Canvas.Top="30" Height="200" Width="200" Stroke="Black" StrokeThickness="10" Fill="SlateBlue" /> Ví dụ tiếp theo đặt Width và Height của đối tượng Canvas cha thành 200 và đặt màu nền cho nó là Lime. <Canvas xmlns="" xmlns:x="" Width="200" Height="200" Background="LimeGreen"> <Ellipse Canvas.Left="30" Canvas.Top="30" Height="200" Width="200" Stroke="Black" StrokeThickness="10" Fill="SlateBlue" /> Khi chạy ví dụ này, hình chữ nhật màu xanh lá cây chính là Canvas, phần màu trắng là phần còn lại của Silverlight plug-in không bị che phủ bởi Canvas. Bạn sẽ thấy rằng phần nằm ngoài Canvas của hình ellipse sẽ không bị xén mất. Nếu không đặt thì giá trị mặc nhiên của Width và Height sẽ là 0. Các Canvas lồng nhau Một Canvas có thể chứa những Canvas khác. trong ví dụ sau bạn sẽ thấy một Canvas lại chứa 2 Canvas khác bên trong. <Canvas xmlns="" xmlns:x=""> <Canvas Height="50" Width="50" Canvas.Left="30" Canvas.Top="30" Background="blue"/> <Canvas Height="50" Width="50" Canvas.Left="130" Canvas.Top="30" Background="red"/> IV. Chương 4 Silverlight hỗ trợ đồ họa cơ bản cho phép bạn có thể vẽ được hình ellipse, chữ nhật, đường thẳng, đa giác và các đường cong... Các thành phần này được gọi chung là các hình họa (shape). Các hình họa cơ bản Silverlight cung cấp ba thành phần hình họa cơ bản: hình ellipse, chữ nhật và đường thẳng. Đối tượng ellipse mô tả một hình ellipse hay hình tròn. Bạn có thể kiểm soát chiều rộng và chiều cao bằng cách đặt thuộc tính Width và Height. Đối tượng Rectangle mô tả một hình vuông hoặc một hình chữ nhật, có thể bo tròn góc. Bạn kiểm soát chiều rộng và chiều cao bằng cách đặt giá trị các thuộc tính Width hoặc Height. Bạn cũng có thể dùng thuộc tính RadiusX và RadiusY để xác định độ cong của góc. Thay vì dùng các thuộc tính Width và Height, bạn kiểm soát kích thước và vị trí của một đường thẳng bằng cách đặt giá trị cho các thuộc tính X1, Y1, X2, Y2 của nó. Ví dụ sau vẽ một Ellipse, một Rectangle, và một Line trong một Canvas. <Canvas xmlns="" xmlns:x=""> <Ellipse Height="200" Width="200" Canvas.Left="30" Canvas.Top="30" Stroke="Black" StrokeThickness="10" Fill="SlateBlue"/> <Rectangle Height="100" Width="100" Canvas.Left="5" Canvas.Top="5" Stroke="Black" StrokeThickness="10" Fill="SlateBlue"/> <Line X1="280" Y1="10" X2="10" Y2="280" Stroke="black" StrokeThickness="5"/> Các đối tượng hình họa khác Ngoài các đối tượng Ellipse, Line, và Rectangle, Silverlght còn cung cấp 3 đối tượng hình họa khác: Polygon, Polyline, và Path. Một Polygon (đa giác) là một hình đóng với một số cạnh, trong khi một PolyLine là một chuỗi các đoạn thẳng nối với nhau, các đoạn thẳng này có thể tạo thành một hình đóng (đa giác) hoặc không. Ví dụ sau sẽ tạo nên một Polygon và một PolyLine: <Canvas xmlns="" xmlns:x=""> <Polyline Points="150, 150 150, 250 250, 250 250, 150" Stroke="Black" StrokeThickness="10"/> <Polygon Points="10,10 10,110 110,110 110,10" Stroke="Black" StrokeThickness="10" Fill="LightBlue"/> Đối tượng Path có thể được dùng để biểu diễn một hình dạng phức tạp bao gồm cả cung và đường cong. Để dùng Path, bạn phải dùng một kiểu cú pháp đặc biệt để đặt thuộc tính Data của nó. Ví dụ sau sẽ tạo nên ba đối tượng Path. <Canvas xmlns="" xmlns:x=""> <Path Data="M0,0 L11.5,0 11.5,30 5.75,40 0,30z" Stroke="Black" Fill="SlateBlue" Canvas.Left="10" Canvas.Top="10" /> <Path Data="M 10,100 C 10,300 300,-200 250,100z" Stroke="Red" Fill="Orange" Canvas.Left="10" Canvas.Top="10" /> <Path Data="M 0,200 L100,200 50,50z" Stroke="Black" Fill="Gray" Canvas.Left="150" Canvas.Top="70" /> Để có thêm thông tin về cú pháp của Path, hãy xem phần Path Markup Syntax trong bộ Silverlight SDK. Vẽ hình bằng cách sử dụng bút vẽ Hầu hết các hình họa đều bao gồm hai phần, đường viền và nền bên trong được kiểm soát bởi các thuộc tính Stroke và Fill. Hình minh họa sau cho bạn thấy phần đường viền và nền của hình chữ nhật ở ví dụ đầu tiên. Không phải mọi hình đều có đầy đủ nền và viền: một Line (đoạn thẳng) chỉ có đường viền. Đặt giá trí thuộc tính Fill của một Line sẽ không có tác dụng. Bạn đặt giá trị cho Stroke và Fill bằng cách sử dụng Brush (bút vẽ). Có 5 kiểu đối tượng bút vẽ mà bạn có thể dùng: - SolidColorBrush - nearGradientBrush - RadialGradientBrush - ImageBrush - VideoBrush (mô tả trong phần media) Vẽ dùng một màu với SolidColorBrush Để vẽ một vùng với một màu nào đó, bạn dùng SolidColorBrush. XAML cung cấp một số cách để tạo SolidColorBrush. Bạn có thể dùng một số cú pháp để chỉ tên của một màu, kiểu như “Black” hay “Gray”. Bạn cũng có thể dùng cú pháp thập lục phân để mô tả các thành phần đỏ, xanh lá cây và xanh nước biển, cùng một thành phần tùy chọn chỉ ra độ trong suốt. Có thể dùng những cách sau: - Ký pháp 6-số: Dạng là #rrggbb, trong đó rr là hai chữ số thập lục phân mô tả thành phần màu đỏ, gg mô tả màu xanh lá cây va bb mô tả màu xanh nước biển. Ví dụ như: #0033FF. - Ký pháp 8-số: Định dạng tương tự ký pháp 6-số, ngoại trừ có thêm 2 ký tự mở rộng mô tả giá trị alpha, chỉ ra độ trong suốt: #aarrggbb. Ví dụ: #990033FF. Ngoài ra còn có ký pháp 3 hoặc 4 số cho các màu 8-bit, nhưng ít được sử dụng. Thay vì dùng các thuộc tính như Stroke và Fill để đặt giá trị cho bút vẽ, bạn cũng có thể tạo một đối tượng SolidColorBrush riêng và đặt thuộc tính Color cho nó, dùng một trong những định dạng màu đã nói ở trên. Ví dụ sau đây cho thấy một số cách để vẽ một đối tượng bằng màu đen. <Canvas xmlns="" xmlns:x=""> <Ellipse Height="90" Width="90" Canvas.Left="10" Canvas.Top="10" Fill="black"/> <Ellipse Height="90" Width="90" Canvas.Left="110" Canvas.Top="10" Fill="#000000"/> <Ellipse Height="90" Width="90" Canvas.Left="10" Canvas.Top="110" Fill="#ff000000"/> Vẽ hình với gradient dùng LinearGradientBrush và RadialGradientBrush Silverlight hỗ trợ cả linear và radial gradiant. Gradient có một hoặc nhiều gradient stop mô tả sự chuyển đổi và vị trí của các màu sắc khác nhau trong gradient. Hầu hết các gradient chỉ cần hai gradient stop, nhưng bạn có thể tạo nhiều hơn nếu muốn. - LinearGradientBrush vẽ một gradient theo một đường thẳng. Đường thẳng này mặc nhiên sẽ nằm theo đường chéo từ góc trên trái xuống góc dưới phải. Bạn có thể đặt lại giá trị cho thuộc tính StartPoint và EndPoint để thay đổi vị trí của đoạn thẳng này. - RadialGradientBrush vẽ một gradient dọc theo một đường tròn, mặc nhiên tâm hình tròn này sẽ nằm ở giữa vùng được vẽ. Bạn có thể thay đổi hình thức của gradient bằng cách đặt lại giá trị các thuộc tính GradientOrigin, Center, RadiusX, and RadiusY. Để thêm gradient stop vào bút vẽ, bạn hãy tạo các đối tượng GradientStop. Đặt lại thuộc tính Offset của một GradientStop
Tài liệu liên quan