Dano

New Member
Metro Style Apps là cái gì thế?


Thực ra nếu dùng Windows 7 hay các phiên bản cũ hơn thì các bạn cũng không biết Metro App là cái gì vì nó chỉ mới xuất hiện trong phiên bản mới nhất của Windows bây giờ, Windows 8!


Nó trông như thế nào?


Chắc hẳn ai cũng quen thuộc với giao diện metro của windows 8 nhỉ. Còn nếu bạn nào chưa hình dung ra thì Metro Apps nó như thế này:




Nhìn nó hơi giống với Apps của Windows Phone! Tuy nhiên, các Apps này thì chạy được cả trên nền PC lẫn Tablet.

Làm Metro Apps Như Thế Nào?


Hiện nay, với mỗi phiên bản Windows 8, có một công cụ cho phép chúng ta Build Metro Apps trên nền Desktop. Với Windows 8 Consumer Preview thì là Visual Studio 11 còn Windows 8 Release Preview thì là Visual Studio 2012 RC. Trong bìa viết này, mình dùng bản 2012 RC vì mình đang dùng Windows 8 Release Preview !


Ngôn ngữ lập trình mình sử dụng trong tut này là Javascript. Mình thích dùng Javascript tại vì nhìn nó quen rồi Với cả nó cũng không phức tạp cho lắm. Tất nhiên cần có các ngôn ngữ khác để tạo UI(User Interface), ở đây ta sẽ dùng HTML và CSS(HTML5 và CSS3).


các bạn cũng có thể dùng các ngôn ngữ khác như VB, C++ hay C#. Mấy cái này có thể mình sẽ trình bày lại sau:


Bây giờ thì chúng ta có thể bắt đầu vào việc chính trong ngày hum nay:

Phần 1:

Làm ứng dụng Hello World



các bạn để ý nhé, khi bắt đầu học ngôn ngữ lập trình nào thì đầu tiên đều có bài tập là “Viết một ứng dụng Hello World”. Ta phải làm bài tập này vì trong đó chứa các cấu trúc và các lệnh cực kì căn bản trong ngôn ngữ lập trình ta định học, ví dụ như lệnh in ra màn hình một dòng chữ, hay gì gì đó.


Đầu tiên phải khởi động cái VS 2012 RC, khi khởi động, màn hình của nó là thế này:




Các Tools của nó thì ta sẽ học cách sử dụng sau, bây giờ đi vào vấn đề chính luôn, để tạo một Metro Style App, ta vào File -> New Project -> Templates -> JavaScript -> Windows Metro style






Đặt tên cho nó theo ý bạn nhé, ở đây mình đặt tên là “Chào!” . Sau khi gõ tên vào khung Name xong, các bạn nhấn OK để nó bắt đầu Build.


Chú ý: Nếu đây là lần đầu tiên bạn cài đặt và chạy thử Visual Studio 2012 RC, nó sẽ hiện lên một khung đăng nhập để cung cấp cho bạn Developer Licences. Dùng tài khoản của Microsoft như live.com để đăng nhập nhé!


Sau khi nhấn OK và đợi nó load cho chúng ta một cái Project, bước tiếp theo là tùy biến cái Project mẫu đó cho đúng ý ta thôi. (Nếu bây giờ các bạn nhấn F5 để chạy thử thì nó sẽ ra một trang trắng màu đen )


Bắt đầu làm phần “ngoại hình” trước nhé, nhìn sang khung Solution Explore bên phải, nó sẽ có dạng như thế này:




Double-Click vào default.html để mở file này lên, đoạn code bên trong đó có dạng như thế này:










Chào_



















Content goes here








Chẳng khác gì một file HTML bình thường cả, cái phần thì tạm để nguyên, ta chưa động chạm đến, bây giờ ta khám phá phần trước.Trong ví dụ này, mình sẽ chèn một và một






Nhấn F5 để chạy thử và xem thành quả!




Nhìn nó hơi bị xấu, nội dung bên trong nó sát vào lề quá, ta dùng CSS để căn chỉnh cho nó đẹp nhé! Bạn nhìn sang khung Solution Explore, tìm file default.css trong thư mục css, double-click vào đó, ta chỉnh sửa như bình thường (CSS căn bản ý mà).Thêm những dòng CSS sau vào file default.css:


body {

/*Tác dụng của 2 dòng này là làm tăng khoảng cách lề trái và lề trên*/

margin-left: 30px;

margin-top: 20px;

}


Tác dụng của 2 dòng CSS đó là làm tang khoảng cách lề trái và lề trên so với nội dung bên trong. Vậy là xong phần giao diện, giờ bắt tay vào code phần điều khiển. các bạn mở file default.js trong thư mục js ở khung Solution Explore ra, nội dung bên trong của nó sẽ là như thế này:




// For an introduction to the Blank template, see the following documentation:

//


(function () {

"use strict";

var app = WinJS.Application;

var activation = Windows.ApplicationModel.Activation;

WinJS.strictProcessing();

app.onactivated = function (args) {

if (args.detail.kind === activation.ActivationKind.launch) {

if (args.detail.previousExecutionState !==

activation.ApplicationExecutionState.terminated) {

// TODO: This application has been newly launched. Initialize

// your application here.

} else {

// TODO: This application has been reactivated from suspension.

// Restore application state here.

}

args.setPromise(WinJS.UI.processAll());

}

};

app.oncheckpoint = function (args) {

// TODO: This application is about to be suspended. Save any state

// that needs to persist across suspensions here. You might use the

// WinJS.Application.sessionState object, which is automatically

// saved and restored across suspension. If you need to complete an

// asynchronous operation before your application is suspended, call

// args.setPromise().

};

app.start();

})();




Đoạn từ: (Ta gọi đoạn này là đoạn 1)




app.onactivated = function (args) {

if (args.detail.kind === activation.ActivationKind.launch) {

if (args.detail.previousExecutionState !==

activation.ApplicationExecutionState.terminated) {

//Code trong phần này sẽ được chạy khi ứng dụng mới được mở ra.

} else {

//Code trong phần này sẽ được chạy lại khi ứng dụng bị ngắt đột ngột

//(tức là bị Suppend vì một lí do nào đó)

}

args.setPromise(WinJS.UI.processAll());

}

//Code trong phần này sẽ được chạy khi ứng dụng hoạt động

};




Đoạn từ: (Ta gọi đoạn này là đoạn 2)




app.oncheckpoint = function (args) {

//Những dòng lệnh ở đây sẽ giúp bạn nếu ứng dụng của bạn bắt buộc phải dừng

//đột ngột(suppend) bởi một lí do chính đáng(Chắc là tự tay dừng lại).

};




Đoạn: (Ta gọi đoạn này là đoạn 3)

app.start();

//Code ở đoạn này sẽ được chạy khi được gọi

})();


Ta hãy tạo một function có tên là chao() nhé! Đặt nó ở đoạn 3 đi:

app.start();

function chao() {

}

})();


Ta coi như sau khi nhấn nút “Ờh!” thì các lệnh bên trong function này sẽ được thực thi. Những gì ta cần làm ở trong đó là lấy nội dung mà người dùng nhập bên trong khung text, sau đó in ra màn hình câu chào!


Đoạn mã này sẽ giúp ta làm điều đó:



function chao() {

var khung_text = document.getElementById('name');

var name = khung_text.value;

}



Hoàn toàn là Javascript căn bản, đầu tiên ta chọn khung text bằng lệnh document.getElementById(‘Id của phần tử cần chọn’). Sau đó ta lấy nội dung ở phần tử vừa chọn bằng cách dùng .value. Làm tương tự, ta chọn cái
và gán nội dung cho nó nhé!

var khung_ket_qua = document.getElementById('ket_qua');

khung_ket_qua.innerText = "Chào "+name+" !Bạn vừa nhập tên vào khung kia làm gì

thế??!";



Nếu muốn thay đổi nội dung, ta không dùng .value nữa mà chuyển qua dùng .innerText hay .innerHTML. Với .innerText, bạn chỉ có thể gán kí tự vào trong nội dung của phần tử nhưng với .innerHTML, bạn có thể chèn thêm các tag của HTML vào nữa, các tag này sẽ có hiệu lực. Nếu bây giờ chúng ta nhấn F5 để chạy thử thì sẽ thấy cái giao diện vừa nãy. Thử nhập tên vào và nhất “Ờh!” thì nó cũng không ra cái gì. Tại vì ta chỉ cho ứng dụng biết khi người dung nhấn “Ờh!” thì nó phải làm gì. Bây giờ chúng ta sẽ chỉ cho nó biết:


Để ý lên đoạn 1 nhé:

app.onactivated = function (args) {

if (args.detail.kind === activation.ActivationKind.launch) {

if (args.detail.previousExecutionState !==

activation.ApplicationExecutionState.terminated) {

//Code trong phần này sẽ được chạy khi ứng dụng mới được mở ra.

} else {

//Code trong phần này sẽ được chạy lại khi ứng dụng bị ngắt đột ngột

//(tức là bị Suppend vì một lí do nào đó)

}

args.setPromise(WinJS.UI.processAll());

}

//Code trong phần này sẽ được chạy khi ứng dụng hoạt động

};



Ta sẽ chèn code sau:

var $submit = document.getElementById('submit');

$submit.addEventListener("click", chao, false);



Đoạn code này sẽ Select phần tử có ID là submit và thêm một sự kiện vào phần tử vừa chọn đó bằng lệnh .addEventListener(“tên sự kiện”, hoạt động, [Boolean useCapture]) trong đó cái [Boolean useCapture] thì thường người ta hay để là false.


Code đầy đủ của đoạn 1 là như sau:



app.onactivated = function (args) {

if (args.detail.kind === activation.ActivationKind.launch) {

if (args.detail.previousExecutionState !==

activation.ApplicationExecutionState.terminated) {

//Code trong phần này sẽ được chạy khi ứng dụng mới được mở ra.




} else {




//Code trong phần này sẽ được chạy lại khi ứng dụng bị ngắt đột ngột

//(tức là bị Suppend vì một lí do nào đó)

}

args.setPromise(WinJS.UI.processAll());

}

var $submit = document.getElementById('submit');

$submit.addEventListener("click", chao, false)

};









Nó đã hoạt động rồi phải không! các bạn nên viết code và đặt tên các biến khác với mình đặt để cho nó nhớ lâu.


Nói thêm chút về phần trình bày sản phẩm tí. Như các bạn thấy thì Metro App nào nó cũng có một cái icon ở ngoài, không phải hình vuông thì hình chữ nhật phải không. Nhưng cái icon của App mà ta đang viết nó lại có cái hình vuông gạch chéo, bây giờ ta muốn thay nó đi thì làm như thế nào?


Mình sẽ viết tiếp phần 2 vào ngày mai.

 

Alcott

New Member
Viết Metro Style Apps Windows 8 và windows 8.1 (Phần 2: Vòng đời của các metro)

Trong bài viết trước, mình đã giới thiệu tới các bạn cách viết ứng dụng Hello World. Hôm nay, để tiếp tục chủ đề, mình giới thiệu tới các bạn bài viết về Vòng đời của một Metro App.

Phần 2: Vòng đời của một Metro App


Ở phần 2 này mình sẽ nói tiếp về vòng đời của một metro app. Ứng với mỗi đoạn code thì một Metro App có những cách thức, thời điểm khác nhau để biểu diễn chúng. Cụ thể, vòng đời của một Metro App gồm những sự kiện như Start, Running, Suppend, Shutdown được biểu diễn theo sơ đồ như sau:




Mình vẽ không được đẹp cho lắm nhưng nói chung là cũng hiểu được. Trình tự của nó sẽ như sau:


- Khởi nguồn là khi ứng dụng đang tắt (Shut Down), chưa được ai mở lên.


- Khi có một người mở lên, ứng dụng sẽ từ trạng thái Shut Down sang trạng thái Running


- Khi ứng dụng Running (đang chạy), nó sẽ có hai lí do để dừng lại:


+ Một là khi máy bị quá tải, hay nó bị gì gì đó(Mình gọi là bị phê) thì ứng dụng sẽ từ Running chuyển sang Suppend (Dừng đột ngột).


+ Hai là khi người dùng tắt đi, cái này cũng gọi là dừng đột ngột (Suppend).


- Khi ứng dụng bị dừng đột ngột kiểu đó, nó sẽ có hai con đường, một lại trở lại chạy tiếp (Running), hai là tắt (Shut down) . Kết thúc một vòng đời.


Nghĩa là chỉ có Mở -> Chạy -> Đơ -> Tắt -> Mở ->Chạy…..


Vậy với cái vòng đời thế này thì code của ta nó sẽ chạy kiểu gì? Có rất nhiều trường hợp sẽ xảy ra khi code của chúng ta đang chạy. Giả sử, code của ta là code xem phim, nghe nhạc chẳng hạn, người dùng đang lần mò tìm bài hát họ thích, lần mãi mới mò ra, bỗng nhiên máy nó nổi điên và Shut Down mất cái ứng dụng. Nếu bây giờ người dùng mở lại ứng dụng lên mà phải lần mò tìm lại bài hát mình thích thì chắc ức cmn chế mà tắt máy luôn .Vậy cách đối phó tốt nhất với cái vòng đời bất ổn này chính là lưu các dữliệu người dùng đang làm ngay trước khi nó bị Suppend. Rồi sau khi người dùng mở lên, ta lại load lại cái dữ liệu vừa lưu đó cho họ. Các ví dụ điển hình là mấy ứng dụng cung cấp sẵn của Microsoft như Travel, Music, News, Sport…Có hai kiểu lưu dữ liệu, một là lưu Local vào máy, hai là lưu Sesson.Ta bắt đầu tìm hiểu về hai kiểu lưu dữ liệu này và áp dụng vào ứng dụng Hello World vừa viết nhé!


*Lưu Local


Mở file default.js lên, ta sẽ thêm đoạn mã sau:

var $khung = document.getElementById('name');


$khung.addEventListener("change", luu);



Đoạn mã này thêm cho chúng ta một event vào có id là name. Cụ thể là nếu cái có id là name đó nó bị thay đổi giá trị, lập tức function luu sẽ được chạy.


Thêm function luu vào bên dưới function chao ở đoạn 3:
function luu(eventInfo) {

var getControl = eventInfo.srcElement;

//Lấy dữ liệu hiện có của Apps


var duLieu = Windows.Storage.ApplicationData.current;

//Gọi một cách nào đó để lưu Local

var localSetting = duLieu.localSettings;

//Lấy nội dung của khung có id là name

var noi_dung = getControl.value;

//Phần tử "ten" của LocalSetting sẽ được đặt giá trị của biến noi_dung

localSetting.values["ten"] = noi_dung;

}





Function luu này có một tùy biến là eventInfo, nó thay mặt cho khung đã được gán(addEventListener) function này. Ta dùng .srcElement để có thể truy cập các phần tử của tùy biến này của tùy biến. Mấy dòng sau thì bạn có thể đọc các chú thích ở trên để hiểu công dụng của chúng. Vậy là đã lưu xong, bây giờ lấy ra nếu nó bị Suppend thì kiểu gì? Ta dùng đoạn code này nhé.



else {

// TODO: This application has been reactivated from suspension.

// Restore application state here.

}

}

var $submit = document.getElementById('submit');

$submit.addEventListener("click", chao, false);

var $khung = document.getElementById('name');

$khung.addEventListener("change", luu);

//Lấy Data đã lưu

var localSetting = Windows.Storage.ApplicationData.current.localSetti ngs;

if (tra_lai_ten) {

//Gán giá trị của khung có id là name bằng với giá trị ta đã lưu

document.getElementById("name").value = localSetting.values["ten"];

}

};





Mình post thế này để các bạn biết đoạn code mới được để ở đâu. Đọc comment thì các bạn đã biết được công dụng của từng dòng đúng không!


*Lưu Sesson

Để lưu Sesson, ta dùng code sau:



app.oncheckpoint = function (args) {

// TODO: This application is about to be suspended. Save any state

// that needs to persist across suspensions here. You might use the

// WinJS.Application.sessionState object, which is automatically

// saved and restored across suspension. If you need to complete an

// asynchronous operation before your application is suspended, call

// args.setPromise().


//Chọn khung có id là ket_qua

var kq = document.getElementById("ket_qua");

//Gán giá trị cho dữ liệu được lưu bằng với giá trị của khung kq

WinJS.Application.sessionState.ket_qua = kq.innerText;

};





Đoạn mã này được đặt ở đoạn 2 vì khi ứng dụng bị suppend, nó sẽ tự tạo một checkpoint(giống chơi game, khi chết thì sẽ quay lại checkpoint). Code bên trong checkpoint sẽ được gọi ra ngay lúc ứng dụng bị suppend.


Lấy giá trị được lưu Session thì đơn giản hơn Local.
} else {

// TODO: This application has been reactivated from suspension.

// Restore application state here.

//Lấy dữ liệu vừa lưu xong

var ket_qua = WinJS.Application.sessionState.ket_qua;

//Gán giá trị cho khung có id là ket_qia bằng với giá trị lưu session

document.getElementById("ket_qua").innerText = ket_qua;

}

}

var $submit = document.getElementById('submit');

$submit.addEventListener("click", chao, false);


Bây giờ ta thử chạy rồi cho ứng dụng Suppend nhé! Khi chạy ứng dụng, nhập tên và nhấn nút, ta sẽ quay lại desktop và cho ứng dụng suppen bằng cách sau:




Sau đó ta nhấn F5 để chạy lại ứng dụng và thấy các dữ liệu đã được lấy lại
 

abc_304

New Member
Viết Metro Style Apps Windows 8 và windows 8.1 (Phần 3: PageControl objects and navigation)

Ứng dụng " hello world" mà bạn tạo ra trong hướng dẫn trước đây chỉ có một trang nội dung. Hầu hết các ứng dụng thực tế sẽ có nhiều trang. Trong hướng dẫn này, ta sao chép code từ ứng dụng "hello, world" vào một ứng dụng mới sử dụng các mẫu Navigation App , và sau đó bạn sẽ thêm một trang bổ sung.


Tìm hiểu cách:


+ Sử dụng Navigation App mẫu để tạo ra một ứng dụng có chứa nhiều trang nội dung.


+ Sử dụng đối tượng PageControl để tách biệt mã của bạn thành các đơn vị mô-đun.


+ Sử dụng mô hình chuyển hướng trang đơn ( single page) để điều hướng giữa các trang.


+ Sử dụng một AppBar để cung cấp lệnh chuyển hướng.


Trước khi bạn bắt đầu


Đây là hướng dẫn thứ ba trong series của mình. Trước khi bạn bắt đầu hướng dẫn này, vui lòng đọc Phần 2: Vòng đời của một Metro App . Chúng ta sẽ bắt đầu với mã được tạo ra ở phần 2. Còn bây giờ thì chúng ta có thể bắt đâu


Bước 1: Tạo một Danh mục ứng dụng mới trong Visual Studio


Chúng ta hãy tạo ra một ứng dụng mới có tên HelloWorldWithPages có sử dụng các Navigation App mẫu. Đây là cách:


1. Khởi động Microsoft Visual Studio Express 2012 cho Windows 8.


2. Từ menu File chọn New Project.


Hộp thoại New Project xuất hiện. Bảng điều khiển bên trái của hộp thoại cho phép bạn chọn các loại mẫu để hiển thị.


3. Trong khung bên trái, mở rộng Installed, sau đó mở rộng Templates, sau đó mở rộng JavaScript và chọn Windows Store trong các kiểu mẫu. Bảng điều khiển trung tâm của hộp thoại sẽ hiển thị một danh sách các mẫu project bằng JavaScript. Trong hướng dẫn này chúng ta sẽ sử dụng Navigation App


4. Trong bảng điều khiển trung tâm, chọn mục Navigation App


5. Trong hộp Name, nhập vào "HelloWorldWithPages".


6. Bỏ chọn hộp kiểm Create directory for solution




7. Nhấn OK để tạo project.


Visual Studio tạo ra project của bạn và hiển thị nó trong Solution Explorer.




Chú ý rằng Navigation App mới tạo của bạn chứa một vài file nhiều hơn app "hello, world".


Chúng ta hãy nhìn vào những tập tin mới:

+ /pages/home/home.css, /pages/home/home.html, and /pages/home/home.js


Ba trang xác định một PageControl cho app home page. Một PageControl được tạo thành từ một file HTML,một file JavaScript, và một file CSS. Một PageControl là một đơn vị mô-đun của HTML, CSS và JavaScript có thể được chuyển đến (như một trang HTML) hay sử dụng như một điều khiển tùy chỉnh. Bạn có thể sử dụng đối tượng PageControl chia một ứng dụng lớn thành nhỏ hơn, dễ quản lý hơn. Đối tượng PageControl hỗ trợ một số phương pháp mà làm cho việc sử dụng chúng trong ứng dụng của bạn dễ dàng hơn so với sử dụng một tập hợp HTML, CSS, JavaScript. Bạn sẽ tìm hiểu thêm về các phương pháp này trong một bước sau.

/js/navigator.js


Tập tin này cung cấp các lớp PageControlNavigator mà bạn có thể sử dụng để hiển thị các đối tượng PageControl và điều hướng giữa chúng. Bạn không cần nó để hiển thị một PageControl, nhưng nó có thể làm cho việc sử dụng chúng dễ dàng hơn. Chúng ta hãy nhìn vào trang default.html của ứng dụng mới :















HelloWorldWithPages








































data-win-options="{home: '/pages/home/home.html'}">












Phần body của tập tin có chứa hai yếu tố: Một thẻ div cho PageControlNavigator và một div cho một AppBar.Bạn hãy hãy bỏ qua thanh ứng dụng bây giờ và có một cái nhìn sâu hơn về các thẻ div đầu tiên.



data-win-options="{home: '/pages/home/home.html'}">




Mặc dù nó không rõ ràng, nhưng các ứng dụng đang thực sự hiển thị cả hai trang default.html và home.html.

Bước 2: Chép nội dung file HTML và CSS của bạn từ ứng dụng "hello, world"


Ứng dụng mới của chúng tui có 2 trang html: default.html và home.html. Vậy nơi nào bạn sẽ đặt nội dung?


+ Trang default.html cho giao diện người dùng nên luôn luôn có mặt. Ví dụ, bạn có thể sử dụng


default.html để lưu trữ một thanh điều hướng.

+ Sử dụng các trang, chẳng hạn như home.html có nội dung tạo nên một màn hình cá nhân trong các ứng dụng.

+ Hãy mở home.html và có một cái nhìn tại một số các chỗ đánh dấu .







homePage






























Có một khu vực tiêu đề trang bao gồm một nút quay lại để điều hướng ngược cho một khu vực tiêu đề.


Mẫu có chứa mã tự động cho phép các nút quay lại khi bạn có thể di chuyển về phía sau. Nút sẽ không


được hiển thị cho đến khi tui thêm một trang thứ hai và điều hướng nó.










Welcome to HelloWorldWithPages!








Nó có một phần nội dung chính của bạn.




Content goes here.







Hãy thêm nội dung từ ứng dụng "hello, world" của chúng ta vào home.html của đối tượng HelloWorldWithPages


Thêm nội dung html và CSS từ ứng dụng "hello, world"


Sao chép nội dung HTML cuối cùng từ các tập tin default.html của ứng dụng "hello, world" vào phần nội dung chính của / page / home / home.html trong project mới của bạn.


Welcome to HelloWorldWithPages!

Hello, world!

What's your name?







Hello, world!













What's your name?


































Thay thế các tham chiếu đến dark style sheet:


<!-- WinJS references -->





Với cái này







Mỗi PageControl có Cascading Style Sheets riêng của mình (CSS) .


Sao chép style greetingOutput từ tập tin default.css bạn tạo ra trong Phần 1: Tạo một ứng dụng "hello, world".

.homepage section[role=main] {


margin-left: 120px;


}




#greetingOutput {


height: 20px;


margin-bottom: 40px;


}




@media screen and (-ms-view-state: snapped) {


.homepage section[role=main] {


margin-left: 20px;


}


}




@media screen and (-ms-view-state: portrait) {


.homepage section[role=main] {


margin-left: 100px;


}


}



Chạy ứng dụng.






Bạn đã tái tạo lại nội dung ban đầu từ ứng dụng "Hello, world" . Tiếp theo, chúng ta thêm các tương tác bằng cách sao chép các sự kiện của ứng dụng "hello, world"


Đọc kĩ các phần đã viết:


Phần 1: hello world


Phần 2: Vòng đời của một metro app
 

mr_gio

New Member
Mình dùng VS 2013 Ultimate sao vào File -> New Project -> Templates -> JavaScript không có Windows Metro style
 
Mình dùng VS 2013 Ultimate sao vào File -> New Project -> Templates -> JavaScript không có Windows Metro style có thể b phải cài thêm vào hay thông qua update để có
 

honghanh_429

New Member
có thể b phải cài thêm vào hay thông qua update để có mình đã search trong update rồi nhưng không có
 

Imanol

New Member
Vâng! Xin chân thành Thank bác Thắng Cò đã đi tiếp con đường còn dang dở. Thực ra e muốn viết tiếp bộ này lắm nhưng mà thời gian nó không cho phép T_T
 

Các chủ đề có liên quan khác

Top