XAML Basics

Cú pháp của chuẩn XAML tương đối đơn giản:

  • Mọi thành phần (elements) trong XAML document đều là một thể hiện của một lớp nào đó trong .NET. Tên của các elements này hoàn toàn giống với tên của các class. Ví dụ như thẻ <Button> trong WPF sẽ tạo ra một đối tượng thuộc lớp Button.
  • Cũng như các tài liệu theo chuẩn XML khác, bạn có thể gom (nest) một thẻ đặt bên trong một thẻ khác. Việc gom các thẻ đặt vào một thẻ khác thường mô tả một object nào đó được chứa trong một object khác – nói cách khác, nếu bạn thấy thẻ Button đặt bên dưới thẻ Grid, giao diện người dùng của bạn sẽ có một button được đặt trong grid.
  • Bạn có thể thiết lập các property của mỗi class thông qua các attribute (thuộc tính). Tuy nhiên, trong một vài trường hợp, attribute không đủ khả năng xử lí một công việc nào đó. Để giải quyết vấn đề này, chúng ta sẽ dùng một cú pháp đặc biệt.

Trước khi tiếp tục, chúng ta sẽ cùng xem qua một ví dụ cơ bản của XAML. Ví dụ này sẽ tạo ra một blank windows (tạo bởi Visual Studio).

<Window x:Class="WindowsApplication1.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" Height="300" Width="300">
    <Grid>
    </Grid>
</Window>

Trong ví dụ trên, ta thấy nó chỉ chứa 2 thẻ, đó là thẻ window – top-level element – biểu diễn toàn bộ window, và thẻ Grid – nơi bạn có thể đặt các control khác lên. Có 3 loại top-level element, đó là:

  • Window
  • Page (tương tự như Window nhưng dùng để chuyển đổi giữa các ứng dụng
  • Application (định nghĩa các application resources và thiết lập khởi động)

Và cũng như tất cả các tài liệu XML, chỉ có thể có duy nhất một thẻ top-level. Trong ví dụ trên, ta thấy không hề có thể bất kì thẻ nào được khai báo bên dưới thẻ </Window>. Tại thẻ Window, bạn thấy ở đây có một vài attribute, bao gồm class name, 2 XML namespace (sẽ được giới thiệu bên dưới). Bạn cũng sẽ thấy có 3 properies trong thẻ này: Title=”Window1” Heigt=”300” Width=”300”> Mỗi attribute sẽ ứng với duy nhất một property của Window class. Tất cả các chi tiết này được WPF dùng để tạo ra một cửa sổ với title là Window1, kích thước 300×300.


Note: WPF sử dụng hệ thống đo lường đặc biệt. Thay vì cho phép bạn thiết lập kích thước của cửa sổ theo pixel, WPF sử dụng một đơn vị đo lường khác, đó là device-independent units, nó sẽ thay đổi kích thước của cửa sổ tùy theo độ phân giải màn hình khác nhau. Một device-independent units sẽ bằng 1/96 inch. Nghĩa là với kích thước 300×300 như ví dụ trên, WPF sẽ render ra một cửa sổ có kích thước 300×300 pixel nếu hệ thống của bạn có DPI (dots-per-inch) bằng 96. Như vậy, trên màn hình có dpi cao hơn, sẽ có nhiều điểm anh hơn trong 1 inch.


XAML Namespaces

Quả thực là một thiếu sót nếu chúng ta chỉ đề cập đến class name. Khi phân tích một tài liệu XAML, chúng ta cần phải biết các class này nằm ở namespace nào của .NET. Ví dụ như, class Window có thể tồn tại trong nhiều nơi khác nhau, có thể nằm trong System.Windows.Window class, hoặc nó cũng có thể là Window class mà bạn tự định nghĩa trong ứng dụng của mình. Để xác định bạn đang muốn nói đến class nào, chúng ta cần phải phân tích XML namespace được định nghĩa trong document.

Trong ví dụ ở trên, ta thấy có 2 namespace được định nghĩa:

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"


Note: XML namespace được khai báo như cách khai báo attributes. Các attribute này có thể được đặt bên trong bất cứ thẻ bắt đầu nào. Tuy nhiên, quy tắc là tất cả các namespace mà bạn sử dụng trong tài liệu nên được khai báo trong thẻ đầu tiên. Một khi đã khai báo namespace rồi, bạn có thể sử dụng nó bất kì đâu trong tài liệu.


xmlns attribute là một thuộc tính đặc biệt của XML. Bạn có thể thấy trong mọi tài liệu WPF XAML bạn tạo ra đều có 2 namespace

  • http://schemas.microsoft.com/winfx/2006/xaml/presentation là core namespace của WPF. Nó chứa tất cả các class trong WPF, bao gồm các controls bạn dùng để xây dựng giao diện. Trong ví dụ trên, namespace này được khai báo không có prefix, như vậy, nó trở thành default namespace cho toàn bộ tài liệu. Nói cách khác, mọi element đều được tự động đặt trong namespace này, ngoại trừ một vài khai báo đặc biệt do bạn thiết lập.
  • http://schemas.microsoft.com/winfx/2006/xaml/ là XAML namespace. Nó báo gồm các tính năng khác nhau của XAML cho phép bạn can thiệp vào cách biên dịch tài liệu của mình. Namespace này được khai báo với prefix x. Nghĩa là bạn có thể áp dụng nó bằng cách đặt namespace prefix trước tên của thẻ ( <x:ElementName> ).
    Như bạn thấy, tên của XML namespace không trùng với bất kì namespace nào trong .NET. Có một vài lí do để giải thích cho điều này. Theo quy ước, XML namespace thường là các URIs (như trong ví dụ này). Các URI này nhìn thì giống như đang trỏ vào một trang web nào đó, nhưng thực tế không phải vậy. Chuẩn URI được sử dụng với mục đích tránh xảy ra tình trạng các tổ chức, doanh nghiệp khác vô tình tạo ra một loại ngôn ngữ khác tuân theo chuẩn XML với namespace giống nhau. Ở đây, bởi vì tên miền schemas.microsoft.com thuộc quyền sở hữu của Microsoft, do đó chỉ có Microsoft sử dụng nó làm để đặt tên trong XML namespace. Một lí do khác đó là không thể có chuyện là mỗi namespace XML trong XAML lại liên kết với một namespace trong .NET, bởi vì nếu điều đó xảy ra, nó sẽ làm cho XAML document của bạn trở nên cực kì phức tạp. Ta thấy WPF có đến cả tá namespaces (tất cả đều bắt đầu bằng System.Windows). Nếu mỗi .NET namespace lại có một XML namespace khác, bạn sẽ cần phải khai báo chính xác cho namespace cho mỗi loại control mà bạn sẽ dụng, điều này có thể làm bạn dễ nhầm lẫn hoặc thiếu sót… Thay vào đó, những người tạo ra WPF chọn cách gom tất cả các .NET namespaces này vào một XML namespace duy nhất.

The Code-Behind Class

XAML cho phép bạn xây dựng một giao diện người dùng, nhưng để cung cấp các tính năng cho ứng dụng, bạn cần phải connect các event handlers chưa phần mã nguồn để thực thi các chức năng nhất định. XAML giúp thực hiện việc này trở lên dễ dàng bằng cách sử dụng Class attribute:

<Window x:Class="WindowsApplication1.Window1">

Class attribute giúp cho XAML biết rằng nó cần phải phát trình một class mới với tên nhất định. Class mới này được kết thừa từ class có tên như tên của XML element. Trong trường hợp này, chương trình sẽ tạo ra một class mới có tên là Window1 và class này kế thừa từ class Window. Class Window1 được phát sinh tự động ở compile time. Nhưng đây mới chính là điểm thụ vị. Bạn có thể tạo ra một Window1 class, và class này sẽ tự động kết hợp với class Window1 được tạo ra bởi XAML lúc compile. Class mà bạn khai báo này thường là nơi bạn cài đặt cách xử lí sự kiện theo mong muốn của mình.


Note: điều này được thực hiện nhờ một tính năng trong C#, đó là partial classes. Partial classes cho phép bạn chia một class thành 2 hoặc nhiều phần nhỏ khác trong quá trình phát triển ứng dụng và nối các phần này lại khi biên dịch ra hợp ngữ.


The InitializeComponent() method

Hiện tại, class Window1 vẫn chưa có một tính năng thực sự nào bởi chúng ta vẫn chưa cài đặt phần code cho nó. Tuy nhiên, nó có một chi tiết rất quan trọng – default contructor. Constructor này sẽ gọi hàm InitializeComponent() mỗi khi bạn tạo ra một thể hiện của class này.


Note: phương thức InitializeComponent() đóng vai trò chủ chốt trong ứng dụng WPF. Vì lí do đó, bạn không bao giờ nên delete hàm InitializeComponent() trong constructor của class Window. Tương tự, nếu bạn thêm constructor khác cho window class, nhớ thêm vào hàm InitializeComponent().


Naming Elements

Thêm một chi tiết nữa chúng ta cần phải lưu tâm. Trong code-behind class, giả sử bạn muốn đọc hoặc thay đổi properties hoặc những công việc khác liên quan đến xử lí sự kiện. Để làm được việc này, các control phải được đặt tên thông qua thuộc tính Name. Trong ví dụ trên, ta thấy Grid control không có Name attribute, như vậy, bạn sẽ không thể nào tương tác với no trong code-behind file. Đây là cách đặt tên cho Grid:

<Grid x:Name="grid1">
</Grid>

Bây giờ, bạn đã có thể thao tác với grid trong class Window1 của bạn thông qua tên grid1:

MessageBox.Show(String.Format("The grid is {0}x{1} units in size",
                               grid1.ActualWidth, grid1.ActualHeight));

Trong ví dụ này, việc đặt tên không thực sự làm nổi bật vai trò của Name attribute, tuy nhiên, nó trở nên quan trọng thực sự trong trường hợp bạn muốn độc một giá trị input từ control như textbox, listbox… Bạn có thể đặt tên cho element theo kiểu XAML Name property (sử dụng prefix x:) hoặc sử dụng Name property của riêng element đó (bỏ prefix đi). Cả hai cách này đều cho kết quả tương tự trong đa số các trường hợp.

<Grid Name="grid1">
</Grid>

[To be continued]

Xem thêm: