184
Bài mđầu TNG QUAN VWINDOWS REPRESENTATION FOUNDATION Bài này giới thiệu tổng quan về công nghệ Windows Presentation Foundation (WPF). Phần đầu nói về những thách thức trong việc xây dựng giao diện người dùng hiện đại, từ đó dẫn đến sự ra đời của công nghệ WPF, công nghệ xây dựng giao diện mới của Microsoft. Sau đó sẽ giới thiệu những khái niệm, mục tiêu căn bản và các thành phần quan trọng của WPF. Phần cuối sẽ giới thiệu các công cụ cần thiết để phát triển ứng dụng WPF và giúp học viên làm quen với WPF bằng việc hướng dẫn phát triển một ứng dụng đơn giản cụ thể. Giao diện người dùng hiện đại và nhng thách thc Trong các ứng dụng hiện đại, giao diện người dùng trực quan chiếm vị trí hết sức quan trọng. Việc trình diễn đúng thông tin, theo đúng cách và vào đúng thời điểm có thể đem lại những giá trị kinh tế xã hội đáng kể. Với những ứng dụng thương mại, chẳng hạn một ứng dụng bán hàng trực tuyến, việc cung cấp một giao diện người dùng mạnh có thể tạo nên sự khác biệt giữa một công ty với các đối thủ cạnh tranh, góp phần làm tăng tăng doanh số và giá trị thương hiệu của hãng này so với hãng khác. Để có được một giao diện người dùng như vậy, việc tích hợp đồ họa, media, văn bản và các thành phần trực quan khác như một thể thống nhất đóng đóng vai trò mấu chốt. Hãy xem xét một ứng dụng cụ thể trong quản lý và theo dõi bệnh nhân của một bệnh viện nào đó. Với sự phát triển của công nghệ đa phương tiện hiện nay, yêu cầu về giao diện người dùng cho hệ thống mới này sẽ bao gồm: - Hiển thị hình ảnh và text về bệnh nhân. - Hiển thị và cập nhật hình ảnh 2 chiều cho biết trạng thái của bệnh nhân như nhịp tim, huyết áp. - Cung cấp hình ảnh chồng lớp 3 chiều về thông tin của người bệnh. - Trình diễn những đoạn video siêu âm và những chẩn đoán khác, trong đó, cho phép bác sỹ hay y tá thêm vào các ghi chú. - Cho phép nhân viên bệnh viện đọc và ghi chú trên những tài liệu mô tả về bệnh nhân và tình trạng của người đó.

Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

  • Upload
    others

  • View
    4

  • Download
    0

Embed Size (px)

Citation preview

Page 1: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Bài mở đầu

TỔNG QUAN VỀ WINDOWS REPRESENTATION

FOUNDATION

Bài này giới thiệu tổng quan về công nghệ Windows Presentation Foundation (WPF). Phần đầu

nói về những thách thức trong việc xây dựng giao diện người dùng hiện đại, từ đó dẫn đến sự ra đời của

công nghệ WPF, công nghệ xây dựng giao diện mới của Microsoft. Sau đó sẽ giới thiệu những khái

niệm, mục tiêu căn bản và các thành phần quan trọng của WPF. Phần cuối sẽ giới thiệu các công cụ cần

thiết để phát triển ứng dụng WPF và giúp học viên làm quen với WPF bằng việc hướng dẫn phát triển

một ứng dụng đơn giản cụ thể.

Giao diện người dùng hiện đại và những thách thức

Trong các ứng dụng hiện đại, giao diện người dùng trực quan chiếm vị trí hết sức quan trọng.

Việc trình diễn đúng thông tin, theo đúng cách và vào đúng thời điểm có thể đem lại những giá trị kinh tế

xã hội đáng kể. Với những ứng dụng thương mại, chẳng hạn một ứng dụng bán hàng trực tuyến, việc

cung cấp một giao diện người dùng mạnh có thể tạo nên sự khác biệt giữa một công ty với các đối thủ

cạnh tranh, góp phần làm tăng tăng doanh số và giá trị thương hiệu của hãng này so với hãng khác. Để có

được một giao diện người dùng như vậy, việc tích hợp đồ họa, media, văn bản và các thành phần trực

quan khác như một thể thống nhất đóng đóng vai trò mấu chốt.

Hãy xem xét một ứng dụng cụ thể trong quản lý và theo dõi bệnh nhân của một bệnh viện nào

đó. Với sự phát triển của công nghệ đa phương tiện hiện nay, yêu cầu về giao diện người dùng cho hệ

thống mới này sẽ bao gồm:

- Hiển thị hình ảnh và text về bệnh nhân.

- Hiển thị và cập nhật hình ảnh 2 chiều cho biết trạng thái của bệnh nhân như nhịp tim, huyết

áp.

- Cung cấp hình ảnh chồng lớp 3 chiều về thông tin của người bệnh.

- Trình diễn những đoạn video siêu âm và những chẩn đoán khác, trong đó, cho phép bác sỹ

hay y tá thêm vào các ghi chú.

- Cho phép nhân viên bệnh viện đọc và ghi chú trên những tài liệu mô tả về bệnh nhân và tình

trạng của người đó.

Page 2: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team |WPF – Bài mở đầu: Tổng quan về WPF 2

- Có khả năng hoạt động như một ứng dụng Windows, trong đó, các nhân viên bệnh viện đều

được sử dụng đầy đủ các tính năng, đồng thời có thể chạy trên trình duyệt Web có giới hạn về an

ninh, cho phép các bác sỹ truy nhập có hạn chế từ xa qua mạng Internet.

Với công nghệ từ trước năm 2006, một giao diện như vậy trên Windows đã có thể xây dựng được, tuy

nhiên, sẽ gặp không ít khó khăn bởi một số nguyên nhân chính sau:

- Có rất nhiều công nghệ khác nhau được sử dụng để làm việc với hình ảnh âm thanh và video.

Tìm được những lập trình viên có khả năng sử dụng tốt nhiều công nghệ như vậy không dễ và

chi phí cao cho cả quá trình phát triển cũng như bảo trì ứng dụng.

- Thiết kế một giao diện biểu diễn có hiệu quả tất cả những tính năng như vậy cũng là một

thách thức. Nó đòi hỏi phải có những người thiết kế giao diện chuyên nghiệp, bởi lập trình viên

phần mềm đơn thuần sẽ không có đủ các kỹ năng cần thiết. Điều này lại dẫn tới những khó khăn

phát sinh khi người thiết kế và người lập trình làm việc chung.

- Việc cung cấp một giao diện đầy đủ tính năng, hoạt động được như một ứng dụng Windows

riêng biệt trên máy desktop, đồng thời có thể được truy nhập thông qua trình duyệt có thể đòi hỏi

phải xây dựng hai phiên bản độc lập sử dụng hai công nghệ khác nhau. Ứng dụng Windows trên

desktop sử dụng Windows Forms và các công nghệ thuần Windows khác, trong khi ứng dụng

trên trình duyệt lại sử dụng HTML và JavaScript. Do đó, cần phải có hai nhóm phát triển với hai

phần kỹ năng khác nhau.

WPF ra đời chính là để xây dựng một nền tảng chung giải quyết những thách thức đã

nêu trên.

WPF là gì?

WPF, viết tắt của Windows Presentation Foundation, là hệ thống API mới hỗ trợ việc xây dựng

giao diện đồ hoạ trên nền Windows. Được xem như thế hệ kế tiếp của WinForms, WPF tăng cường khả

năng lập trình giao diện của lập trình viên bằng cách cung cấp các API cho phép tận dụng những lợi thế

về đa phương tiện hiện đại. Là một bộ phận của .NET Framework 3.0, WPF sẵn có trong Windows Vista

và Windows Server 2008. Đồng thời, WPF cũng có thể hoạt động trên nền Windows XP Service Pack 2

hoặc mới hơn, và cả Windows Server 2003.

WPF được xây dựng nhằm vào ba mục tiêu cơ bản: 1) Cung cấp một nền tảng thống nhất để xây

dựng giao diện người dùng; 2) Cho phép người lập trình và người thiết kế giao diện làm việc cùng nhau

một cách dễ dàng; 3) Cung cấp một công nghệ chung để xây dựng giao diện người dùng trên cả Windows

và trình duyệt Web.

Page 3: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team |WPF – Bài mở đầu: Tổng quan về WPF 3

1.1 Nền tảng thống nhất để xây dựng giao diện người dùng

Trước khi WPF ra đời, việc tạo giao diện người dùng theo những yêu cầu mô tả ở ví dụ trên đòi

hỏi sử dụng rất nhiều công nghệ khác nhau (xem Bảng 2.1). Để tạo form, các control và các tính năng

kinh điển khác của một giao diện đồ họa Windows, thông thường lập trình viên sẽ chọn Windows Forms,

một phần của .NET Framework. Nếu cần hiển thị văn bản, Windows Forms có một số tính năng hỗ trợ

văn bản trực tiếp hoặc có thể sử dụng Adobe’s PDF để hiển thị văn bản có khuôn dạng cố định. Đối với

hình ảnh và đồ họa 2 chiều, lập trình viên sẽ dùng GDI+, một mô hình lập trình riêng biệt có thể truy

nhập qua Windows Forms. Để hiển thị video hay phát âm thanh, lập trình viên lại phải sử dụng Windows

Media Player, và với đồ họa 3 chiều, anh ta lại phải dùng Direct3D, một thành phần chuẩn khác của

Windows. Tóm lại, quá trình phát triển giao diện người dùng theo yêu cầu trở nên phức tạp, đòi hỏi lập

trình viên quá nhiều kỹ năng công nghệ.

Windows

Forms

PDF Windows

Forms/

GDI+

Windows

Media

Player

Direct3D WPF

Giao diện đồ

họa (form và các

control)

x x

On-screen văn

bản

x x

Fixed-format

văn bản

x x

Hình ảnh x x

Video và âm

thanh

x x

Đồ họa 2 chiều x x

Đồ họa 3 chiều x x

Bảng 0.1 – Thành phần giao diện theo yêu cầu và những công nghệ chuyên biệt cần thiết để tạo

chúng.

WPF là giải pháp hợp nhất nhằm giải quyết tất cả những vấn đề công nghệ nêu trên, hay nói cách

khác, WPF cung cấp nhiều tính năng lập trình giao diện trong cùng một công nghệ đơn nhất. Điều này

giúp cho quá trình tạo giao diện người dùng trở nên dễ dàng hơn đáng kể. Hình 2.2 cho thấy một giao

diện quản lý và theo dõi bệnh nhân có sự kết hợp của hình ảnh, text, đồ họa 2 chiều/3 chiều và nhiều

Page 4: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team |WPF – Bài mở đầu: Tổng quan về WPF 4

thông tin trực quan khác. Tất cả đều được tạo ra bằng WPF – lập trình viên không cần viết code để sử

dụng các công nghệ chuyên biệt như GDI+ hay Direct3D.

Hình 0.1 – Một giao diễn người dùng quản lý và theo dõi bệnh nhân sử dụng WPF có thể kết hợp

hình ảnh, text, đồ họa 2 chiều/3chiều và nhiều tính năng trực quan khác

Tuy nhiên, WPF ra đời không có nghĩa là tất cả những công nghệ nêu trên bị thay thế. Windows

Forms vẫn có giá trị, thậm trí trong WPF, một số ứng dụng mới vẫn sẽ sử dụng Windows Forms.

Windows Media Player vẫn đóng một vai trò công cụ độc lập để chơi nhạc và trình chiếu video. PDF cho

văn bản vẫn tiếp tục được sử dụng. Direct3D vẫn là công nghệ quan trọng trong games và các dạng ứng

dụng khác (Trong thực tế, bản thân WPF dựa trên Direct3D để thực hiện mọi biểu diễn đồ họa).

Việc tạo ra một giao diện người dùng hiện đại không chỉ là việc hợp nhất các công nghệ sẵn có

khác nhau. Nó còn thể hiện ở việc tận dụng lợi điểm của card đồ họa hiện đại. Để giải phóng những hạn

chế của đồ họa bitmap, WPF dựa hoàn toàn trên đồ họa vector, cho phép hình ảnh tự động thay đổi kích

thước để phù hợp với kích thước và độ phân giải của màn hình mà nó được hiển thị.

Bằng việc hợp nhất tất cả các công nghệ cần thiết để tạo ra một giao diện người dùng vào một

nền tảng đơn nhất, WPF đơn giản hóa đáng kể công việc của lập trình viên giao diện. Với việc yêu cầu

lập trình viên học một môi trường phát triển duy nhất, WPF góp phần làm giảm chi phí cho việc xây

dựng và bảo trì ứng dụng. Và bằng việc cho phép tích hợp đa dạng nhiều cách biểu diễn thông tin trên

Page 5: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team |WPF – Bài mở đầu: Tổng quan về WPF 5

giao diện người dùng, WPF góp phần nâng cao chất lượng, và theo đó là giá trị công việc, của cách thức

người dùng tương tác với ứng dụng trên Windows.

1.2 Khả năng làm việc chung giữa người thiết kế giao diện

và lập trình viên

Trong thực tế, việc xây dựng một giao diện người dùng phức hợp như trong ví dụ về ứng dụng

quản lý bệnh nhân trên đòi hỏi những kỹ năng ít thấy ở những lập trình viên đơn thuần, mà chỉ có thể tìm

thấy ở những người thiết kế giao diện chuyên nghiệp. Nhưng câu hỏi đặt ra là làm sao để người thiết kế

và lập trình viên có thể làm việc cùng nhau?

Thông thường, người thiết kế giao diện sử dụng một công cụ đồ họa để tạo ra những ảnh tĩnh về

cách bố trí giao diện trên màn hình. Những hình ảnh này sau đó được chuyển tới lập trình viên với nhiệm

vụ tạo ra mã trình để hiện thực hóa giao diện đã thiết kế. Đôi lúc vẽ ra một giao diện thì đơn giản với

người thiết kế, nhưng để biến nó thành hiện thực có thể là khó khăn hoặc bất khả thi với lập trình viên.

Hạn chế về công nghệ, sức ép tiến độ, thiếu kỹ năng, hiểu nhầm hoặc đơn giản là bất đồng quan điểm có

thể khiến lập trình viên không đáp ứng được đầy đủ yêu cầu từ người thiết kế. Do vậy, điều cần thiết ở

đây là một cách thức để hai nhóm công tác độc lập này có thể làm việc với nhau mà không làm thay đổi

chất lượng của giao diện đã thiết kế.

Để thực hiện được điều này, WPF đưa ra ngôn ngữ đặc tả eXtensible Application Markup

Language (XAML). XAML định ra một tập các phần tử XML như Button, TextBox, Label…, nhằm định

nghĩa các đối tượng đồ họa tương ứng như nút bấm, hộp thoại, nhãn…, và nhờ đó cho phép mô tả chính

xác diện mạo của giao diện người dùng. Các phần tử XAML cũng chứa các thuộc tính, cho phép thiết lập

nhiều tính chất khác nhau của đối tượng đồ họa tương ứng. Ví dụ, đoạn mã sau sẽ tạo ra một nút bấm

màu đỏ có nhan đề “Bỏ qua”.

<Button Background="Red">No</Button>

Mỗi phần tử XAML lại tương ứng với một lớp WPF, và mỗi thuộc tính của phần tử đó lại tương

ứng với thuộc tính hay sự kiện của lớp này. Chẳng hạn, nút bấm màu đỏ trong ví dụ trên có thể tạo bằng

C# code như sau:

Button btn = new Button();

btn.Background = Brushes.Red;

btn.Content = "No";

Nếu như mọi thứ có thể biểu diễn bằng XAML thì cũng có thể biểu diễn bằng đoạn mã, thì câu

hỏi đặt ra là XAML có ý nghĩa gì? Câu trả lời là việc xây dựng các công cụ sinh và sử dụng các đặc tả

Page 6: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team |WPF – Bài mở đầu: Tổng quan về WPF 6

bằng XML dễ dàng hơn nhiều so với xây dựng một công cụ tương tự làm việc với đoạn mã. Bởi vậy,

XAML mở ra một cách thức tốt hơn để lập trình viên và người thiết kế làm việc với nhau. Hình 2.3 minh

họa quá trình này.

Hình 0.2 – XAML hỗ trợ lập trình viên và người thiết kế làm việc chung.

Người thiết kế có thể mô tả giao diện người dùng và tương tác với nó thông qua một công cụ,

chẳng hạn như Microsoft Expression Interactive Designer. Chỉ tập trung vào việc định ra diện mạo và

cảm quan cho giao diện đồ họa WPF, công cụ này sinh các đoạn mô tả giao diện thể hiện qua ngôn ngữ

XAML. Lập trình viên sau đó sẽ nhập đoạn mô tả XAML đó vào môi trường lập trình, chẳng hạn như

Microsoft Visual Studio. Thay vì lập trình viên phải tái tạo lại giao diện từ đầu dựa trên một ảnh tĩnh mà

người thiết kế cung cấp, bản thân các đoạn XAML này sẽ được Microsoft Visual Studio biên dịch để tái

tạo thành giao diện đồ họa đúng theo mô tả. Lập trình viên chỉ tập trung vào việc viết mã trình cho giao

diện được sinh ra, chẳng hạn như xử lý các sự kiện, theo những chức năng đề ra của ứng dụng.

Việc cho phép người thiết kế và lập trình viên làm việc chung như vậy sẽ hạn chế những lỗi phát

sinh khi hiện thực hóa giao diện từ thiết kế. Thêm vào đó, nó còn cho phép hai nhóm công tác này làm

việc song song, khiến mỗi bước lặp trong quy trình phát triển phần mềm ngắn đi và việc phản hồi được

tốt hơn. Vì cả hai môi trường đều có khả năng hiểu và sử dụng XAML, ứng dụng WPF có thể chuyển qua

lại giữa hai môi trường phát triển để sửa đổi hay bổ sung giao diện. Với tất cả những lợi điểm này, vai trò

của người thiết kế trong việc xây dựng giao diện được đặt lên hàng đầu.

1.3 Công nghệ chung cho giao diện trên Windows và trên

trình duyệt Web

Trong thời đại bùng nổ của Internet, các ứng dụng Web ngày một phát triển. Việc trang bị giao

diện người dùng với đầy đủ tính năng như một ứng dụng desktop sẽ thu hút nhiều người sử dụng;, và do

Page 7: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team |WPF – Bài mở đầu: Tổng quan về WPF 7

đó góp phần làm tăng giá trị doanh nghiệp. Tuy nhiên, như đã nêu trong phần đầu, với những công nghệ

truyền thống, để phát triển một giao diện đồ họa vừa hoạt động trên desktop vừa trên trình duyệt Web,

đòi hỏi phải sử dụng những công nghệ hoàn toàn khác nhau, giống như việc xây dựng hai giao diện hoàn

toàn độc lập. Điều này tạo ra chi phí không cần thiết để phát triển giao diện.

WPF là một giải pháp cho vấn đề này. Lập trình viên có thể tạo ra một ứng dụng trình duyệt

XAML (XBAP) sử dụng WPF chạy trên Internet Explore. Trên thực tế, cùng đoạn code này có thể được

dùng để sinh ứng dụng WPF chạy độc lập trên Windows. Hình 0.4 minh họa một ứng dụng dịch vụ tài

chính hoạt động như một ứng dụng WPF độc lập. Trong khi đó, hình 0.4 minh họa giao diện của cùng

ứng dụng chạy trên Internet Explore dưới dạng XBAP.

Hình 0.3. Úng dụng WPF độc lập cung cấp dịch vụ tài chính chạy trong cửa sổ riêng.

Page 8: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team |WPF – Bài mở đầu: Tổng quan về WPF 8

Hình 0.4. Giao diện của cùng ứng dụng nêu trên dưới dạng một XBAP chạy trên Internet

Explore.

Như đã thấy trong Hình 0.4, phần giao diện của ứng dụng dạng XBAP được trình duyệt chia

thành các frame thay vì chạy trên các cửa sổ riêng, ngoài ra, các chức năng đều được bảo toàn. Cùng một

đoạn mã được sử dụng chung cho cả hai trường hợp sẽ làm giảm khối lượng công việc cần thiết để phát

triển hai dạng giao diện. Thêm vào đó, sử dụng cùng một đoạn mã cũng có nghĩa là sử dụng cùng kỹ

năng của lập trình viên. Do đó, lập trình viên chỉ cần có học một kiến thức chung là có thể sử dụng trong

cả hai trường hợp. Một lợi điểm nữa của việc dùng chung công nghệ cho cả giao diện Windows và giao

diện Web là người xây dựng ứng dụng không nhất thiết phải quyết định trước loại giao diện nào được sử

dụng. Miễn là máy client đáp ứng được những yêu cầu hệ thống để chạy XBAP, một ứng dụng có thể

cung cấp cả giao diện Windows và giao diện Web, mà chỉ sử dụng phần lớn những đoạn mã giống nhau.

Mỗi ứng dụng XBAP được download khi cần từ một Web server, nên nó phải tuân theo những

yêu cầu về an ninh khắt khe hơn đối với một ứng dụng Windows độc lập. Theo đó, XBAP chạy trong

phạm vi sandbox an ninh do hệ thống an ninh truy nhập mã của .NET Framework cung cấp. XBAP chỉ

chạy với các hệ thống Windows có cài đặt WPF và chỉ với Internet Explore phiên bản 6 và 7 trở lên.

Page 9: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team |WPF – Bài mở đầu: Tổng quan về WPF 9

3. Các thành phần của WPF

Giống như các thành phần khác của .NET Framework, WPF tổ chức các chức năng theo một

nhóm namespace cùng trực thuộc namespace System.Windows. Bất kể chức năng nào được sử dụng,

cấu trúc cơ bản của mọi ứng dụng WPF đều gần như nhau. Là ứng dụng Windows độc lập hay là một

XBAP, một ứng dụng WPF điển hình bao giờ cũng gồm một tập các trang XAML và phần code tương

ứng được viết bằng C# hoặc Visual Basic, còn gọi là các file code-behind. Tất cả các ứng dụng đều kế

thừa từ lớp chuẩn Application của WPF. Lớp này cung cấp những dịch vụ chung cho mọi ứng dụng,

chẳng hạn như các biến lưu trữ trạng thái của ứng dụng, các phương thức chuẩn để kích hoạt hay kết thúc

ứng dụng.

Mặc dù WPF cung cấp một nền tảng thống nhất để tạo giao diện người dùng, những công nghệ

mà WPF chứa đựng có thể phân chia thành những thành phần độc lập. Nhân của WPF là cơ chế tạo sinh

đồ họa dựa trên vector và độc lập với độ phân giải nhằm tận dụng những lợi thế của phần cứng đồ họa

hiện đại. WPF được mở rộng với các tập tính năng phát triển ứng dụng bao gồm XAML, các control, cơ

chế móc nối dữ liệu, layout, đồ họa 2 chiều, ba chiều, hoạt họa, style, khuôn dạng mẫu, văn bản, media,

text và in ấn. WPF nằm trong .NET Framework, nên ngoài ra, ứng dụng WPF có thể kết hợp các thành

phần khác có trong thư viện lớp của .NET Framework.

Hình 0.5. Các thành phần cơ bản của WPF

Application Services

Deployment Services

Databinding

USER INTERFACE SERVICES

XAML

Accessibility

Property System

Input & Eventing

BASE SERVICES

DOCUMENT SERVICES

Packaging Services

XPS Documents

Animation

2D

3D

Audio Imaging

Text

Video Effects

Composition Engine

MEDIA INTEGRATION LAYER

Controls

Layout

Win

dow

s Pre

senta

tion F

oundati

on

XPS V

iew

er

Page 10: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team |WPF – Bài mở đầu: Tổng quan về WPF 10

Phần tiếp theo sẽ giới thiệu sơ lược những thành phần và khái niệm quan trọng của WPF.

3.1 Layout và Control

Để sắp đặt các thành phần khác nhau trên giao diện, ứng dụng WPF sử dụng panel. Mỗi panel có

thể chứa các thành phần con, bao gồm các control như nút bấm hay hộp thoại, hay bản than những panel

khác. Những loại panel khác nhau cho phép sắp xếp thành phần con theo những cách khác nhau. Ví dụ,

DockPanel cho phép các thành phần con có thể được đặt dọc theo cạnh của panel đó, trong khi Grid cho

phép sắp đặt các thành phần con của nó trên một lưới tọa độ.

Giống như bất kỳ một công nghệ giao diện người dùng nào, WPF cung cấp một số lượng lớn các

control. Ngoài ra, người dùng có thể tùy ý định nghĩa các control theo ý mình. Các control chuẩn gồm

Button, Label, TextBox, ListBox, Menu, Slider, hay phức tạp hơn có SpellCheck, PasswordBox…

Các sự kiện do người dùng tạo ra, như di chuyển chuột hay ấn phím, có thể được các control nắm bắt và

xử lý. Trong khi các control và các thành phần giao diện khác có thể được đặc tả đầy đủ bằng XAML,

các sự kiện bắt buộc phải được xử lý bằng mã trình.

3.2 Style và Template

Giống như sử dụng Cascading Style Sheets (CSS) đối với HTML, việc định ra thuộc tính đồ họa

cho các đối tượng giao diện một lần, rồi sau đó áp dụng lại cho các đối tượng khác cùng loại thường rất

tiện lợi. WPF cũng cung cấp tính năng tương tự bằng việc sử dụng thành phần Style của XAML. Ví dụ,

kiểu ButtonStyle có thể được định nghĩa như sau:

<Style x:Key="ButtonStyle">

<Setter Property="Control.Background" Value="Red"/>

<Setter Property="Control.FontSize" Value="16"/>

</Style>

Bất kỳ nút bấm nào sử dụng kiểu này sẽ có nền màu đỏ và sử dụng font chữ kích thước 16. Ví

dụ:

<Button Style="{StaticResource ButtonStyle}">

Click Here

</Button>

Một Style có thể được dẫn xuất từ một Style khác, thừa kế hoặc chồng lên những thuộc tính đã

thiết lập. Mỗi style có thể định nghĩa các trigger cho phép tạo ra những hiệu ứng tương tác đặc biệt,

chẳng hạn như khi lướt chuột qua nút bấm, nút bấm chuyển thành màu vàng.

Page 11: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team |WPF – Bài mở đầu: Tổng quan về WPF 11

WPF cũng hỗ trợ sử dụng template. Mỗi template tương tự như một style, và ở hai dạng:

- Template cho dữ liệu: sử dụng thành phần DataTemplate của XAML để thiết

lập một nhóm thuộc tính hiển thị của dữ liệu như màu sắc, phương thức căn lề...

- Template cho control: sử dụng thành phần ControlTemplate của XAML để

định ra diện mạo của một control.

3.3 Text

Giao diện người dùng ít nhiều đều hiển thị chữ hay text. Đối với phần lớn mọi người, đọc text

trên màn hình thường khó hơn đọc trên giấy in. Đó là do chất lượng hiển thị text trên màn hình kém hơn

so với khi in ra giấy. WPF tập trung giải quyết vấn đề này, làm chất lượng text hiển thị trên màn hình

tương đương trên giấy in. Cụ thể, WPF hỗ trợ các font chữ OpenType chuẩn, cho phép sử dụng các thư

viện font đã có. WPF cũng hỗ trợ công nghệ font chữ mới ClearType, cho phép hiển thị các ký tự mịn

hơn đối với mắt người, đặc biệt là trên màn hình tinh thể lỏng (LCD).

Để nâng cao hơn nữa chất lượng hiển thị text, WPF cho phép một số công nghệ khác như chữ

ghép, theo đó một nhóm ký tự được thay thế bằng một ảnh đơn nhất, tạo tâm lý thoải mái hơn khi đọc đối

với người dùng.

3.4 Văn bản

WPF hỗ trợ ba dạng văn bản: văn bản cố định (fixed), văn bản thích nghi (flow/adaptive) và văn

bản XPS (XML Paper Specification). Kèm theo đó, WPF cũng cung cấp các dịch vụ để tạo, xem, quản

lý, ghi chú, đóng gói và in ấn văn bản.

Văn bản cố định trông không đổi bất kể chúng được hiển thị trên màn hình hay in ra máy in.

Trong WPF, những văn bản dạng này được định nghĩa bằng phần tử FixedDocument trong XAML và

được hiển thị bằng control DocumentViewer.

Trong khi đó, văn bản thích nghi thường chỉ dùng để đọc trên màn hình, và có khả năng tự động

thay đổi các thuộc tính hiển thị ảnh và text cho phù hợp với kích thước cửa số hay các yếu tố môi trường

khác nhằm nâng cao chất lượng đọc cho người dùng. Văn bản thích nghi được định nghĩa bằng phần tử

FlowDocument. Để hiển thị văn bản thích nghi, WPF sử dụng một số control khác nhau, chẳng hạn như

FlowDocumentPageViewer, FlowDocumentScrollViewer, FlowDocumentReader…

Page 12: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team |WPF – Bài mở đầu: Tổng quan về WPF 12

Hình 0.6. – Một minh họa về văn bản thích nghi trong WPF.

Văn bản XPS xây dựng trên cơ sở văn bản bất động của WPF. XPS là một định dạng mở theo

đặc tả XML, có khả năng sử dụng trên nhiều nền tảng khác nhau, được thiết kế nhằm tạo thuận lợi cho

việc xây dựng, chia sẻ, in ấn và lưu trữ văn bản. Cũng như văn bản cố định, văn bản XPS được hiển thị

bằng DocumentViewer.

Hình 0.7. Một minh họa về văn bản XPS trong WPF.

3.5 Hình ảnh

Trong WPF, hình ảnh được hiển thị nhờ control Image, ví dụ:

<Image

Width="200"

Source="C:\Documents and Settings\All Users\Documents\My

Pictures\Ava.jpg" />

Page 13: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team |WPF – Bài mở đầu: Tổng quan về WPF 13

Control Image có thể hiển thị hình ảnh lưu trữ dưới nhiều khuôn dạng khác nhau, bao gồm

JPEG, BMP, TIFF, GIF và PNG. Nó cũng có thể hiển thị hình ảnh dạng Windows Media Photo mới

được sử dụng trong Windows Vista. Bất kể ở khuôn dạng nào, WPF sử dụng Windows Imaging

Component (WIC) để tạo ra hình ảnh. Cùng với các codec dùng cho các khuôn dạng ảnh kể trên, WIC

cũng cung cấp một nền tảng chung để bổ sung codec khác.

3.6 Video và Âm thanh

Khi tốc độ của các bộ xử lý và truyền thông mạng ngày một nâng cao, video trở thành một phần

tương tác lớn của người dùng với phần mềm. Người dùng cũng sử dụng nhiều thời gian để nghe nhạc và

các dạng âm thanh khác trên máy tính. Do đó, WPF cung cấp tính năng hỗ trợ cả hai dạng media này

thông qua phần tử MediaElement. Control này có thể chơi các định dạng video WMV, MPEG và AVI,

và nhiều định dạng âm thanh khác nhau. Việc lập trình để chạy một đoạn video trở nên khá đơn giản, như

trong ví dụ sau:

<MediaElement

Source="C:\Documents and Settings\All Users\Documents\

My Videos\Ruby.wmv" />

3.7 Đồ họa hai chiều

Trong 20 năm gần đây, việc tạo ra đồ họa hai chiều trên Windows dựa trên Graphics Device

Interface (GDI) và phiên bản sau của nó GDI+. Các ứng dụng Windows Forms phải sử dụng chức năng

này thông qua một namespace khác hoàn toàn, bởi bản thân Windows Forms không tích hợp đồ họa 2

chiều. Đối với đồ họa 3 chiều thì càng tồi hơn, Windows Forms phải dựa trên công nghệ hoàn toàn biệt

lập là Direct3D. Với WPF, vấn đề trở nên đơn giản hơn nhiều. Cả đồ họa 2 chiều và 3 chiều đều có thể

được tạo ra trực tiếp trong XAML hoặc trong code sử dụng thư viện WPF tương ứng.

Đối với đồ họa 2 chiều, WPF định ra nhóm control của các khuôn hình (shapes) mà ứng dụng có

thể sử dụng để tạo nên hình ảnh, gồm:

Line: vẽ đường thẳng qua 2 điểm.

Elllipse: vẽ ellipse.

Rectangle: vẽ chữ nhật.

Polygon: vẽ đa giác.

Polyline: vẽ đa giác mở.

Path: vẽ hình theo một đường bất kỳ.

Page 14: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team |WPF – Bài mở đầu: Tổng quan về WPF 14

Mỗi khuôn hình đều có các thuộc tính phong phú cho phép hiển thị với nhiều tính chất khác

nhau: màu nền, màu biên… Một đặc điểm quan trọng trong WPF là: vì mọi thứ đều được xây dựng trên

một nền chung, việc kết hợp các đặc tính và đối tượng khác nhau, chẳng hạn, lồng một ảnh vào một hình

chữ nhật, trở nên đơn giản. Điểm thú vị nữa là các đối tượng hình học này còn có thể thu nhận các sự

kiện từ phía người dùng như một control, chẳng hạn sự kiện nhắp chuột.

Ngoài ra, WPF cũng cung cấp một nhóm chức năng hình học khác, gọi là geometries, để làm

việc với đồ họa hai chiều, như LineGeometry, RectangleGeometry, EllipseGeometry, và

PathGeometry. Dạng hình học này có nhiều thuộc tính và chức năng tương tự như các khuôn hình đã

nêu trên. Điểm khác biệt quan trọng nhất là các geometries không được dùng để hiển thị, chúng được

dùng chủ yếu để tính toán hình học, ví dụ như để định ra các vùng miền, theo dõi vị trí bấm chuột...

Thêm vào đó, WPF cung cấp lớp Transform cho phép thực hiện các biến đổi hình học như xoay,

dịch chuyển, co giãn đối tượng đồ họa; hoặc cho phép thực hiện các hiệu ứng hoạt họa theo thời gian

thông qua các lớp Animation và Timing.

3.8 Đồ họa ba chiều

WPF hỗ trợ đồ họa 3 chiều bằng việc gói các lời gọi API của Direct3D, và do vậy, việc sử dụng

chúng trở nên thống nhất và đơn giản hơn đáng kể. Để hiển thị đồ họa ba chiều, ứng dụng WPF sử dụng

control Viewport3D. Để tạo ra các cảnh ba chiều, lập trình viên mô tả một hay nhiều mô hình, sau đó,

phân định cách thức các mô hình này được chiếu sáng hay hiển thị. Như thường lệ, điều này được thực

hiện bằng XAML, bằng code hay trộn cả hai. Để mô tả mô hình, WPF cung cấp lớp GeometryModel3D

để tạo ra hình dạng của mô hình. Khi mô hình đã được định hình, diện mạo bên ngoài của nó có thể được

điều khiển bằng việc phủ lên các vật liệu (material). Chẳng hạn, lớp SpecularMaterial cho phép tạo

bóng trên bề mặt mô hình.

Bất kể được làm từ vật liệu gì, một mô hình có thể được chiếu sáng theo nhiều cách. Lớp

DirectionalLight cho phép ánh sáng tới từ một hướng xác định, trong khi lớp AmbientLight tạo ra ánh

sáng đồng đều trên mọi vật trong cảnh. Cuối cùng, để định ra cách nhìn cảnh, lập trình viên phải định ra

một camera. Ví dụ, PerspectiveCamera cho phép phân định khoảng cách từ vị trí nhìn tới vật thể và

kiểu nhìn phối cảnh (tuân theo luật gần xa).

Page 15: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team |WPF – Bài mở đầu: Tổng quan về WPF 15

Hình 0.8. Tạo lập đối tượng đồ họa ba chiều với WPF.

Xây dựng cảnh ba chiều trực tiếp bằng XAML hay mã trình đều không đơn giản. Do đó, chỉ nên

dùng ứng dụng WPF để hiển thị cảnh ba chiều, việc xây dựng cảnh nên được thực hiện bằng những công

cụ đồ họa chuyên biệt.

3.9 Móc nối dữ liệu

Phần lớn các ứng dụng được tạo ra đều cung cấp cho người dùng phương tiện để xem và sửa đổi

dữ liệu. Trong các ứng dụng WPF, việc lưu trữ và truy xuất dữ liệu đã được thực hiện bởi các công nghệ

như Microsoft SQL Server và ADO.NET. Sau khi dữ liệu được truy xuất và tải vào các đối tượng quản lý

dữ liệu trên ứng dụng, phần việc khó khăn của ứng dụng WPF mới bắt đầu. Về cơ bản, có hai công việc

phải thực hiện:

1) Sao chép dữ liệu từ các đối tượng quản lý dữ liệu vào các control trên giao diện,

qua đó, dữ liệu có thể được hiển thị hay sửa đổi.

2) Đảm bảo rằng những thay đổi trên dữ liệu từ các control được cập nhật trở lại

các đối tượng quản lý dữ liệu.

Để đơn giản hóa quá trình phát triển ứng dụng, WPF cung cấp một cơ chế móc nối dữ liệu để

thực hiện tự động những bước này. Phần nhân của cơ chế móc nối dữ liệu là lớp Binding mà nhiệm vụ

của nó là liên kết control trên giao diện (đích) với đối tượng quản lý dữ liệu (nguồn). Mối quan hệ này

được minh họa trong hình dưới đây:

Page 16: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team |WPF – Bài mở đầu: Tổng quan về WPF 16

Hình 0.9. Quan hệ giữa đối tượng dữ liệu và đối tượng phụ thuộc.

Việc hỗ trợ móc nối dữ liệu được xây dựng ngay từ nhân của WPF. Tất cả các đối tượng đồ họa

trong WPF đều kế thừa từ DependencyObject, chúng là các đối tượng phụ thuộc. Chức năng mà lớp cơ

sở này hỗ trợ cho phép thực hiện hiệu ứng hoạt họa, tạo kiểu mẫu (styling) và móc nối dữ liệu. Các đối

tượng này đều mang một thuộc tính đặc biệt gọi là DependencyProperty, thuộc tính phụ thuộc. Phần lớn

các thuộc tính hay dùng như Text, Content, Width, Height, vân vân đều là các thuộc tính phụ thuộc. Tất

cả các thuộc tính phụ thuộc đều có thể tạo hiệu ứng hoạt họa, tạo kiểu và kết nối dữ liệu.

Cơ chế móc nối dữ liệu trong WPF còn cung cấp thêm những tính năng như xác thực tính hợp lệ,

sắp xếp, lọc và phân nhóm dữ liệu. Thêm vào đó, tính năng móc nối dữ liệu cũng hỗ trợ sử dụng khuôn

mẫu dữ liệu (data template) để tạo ra các đối tượng giao diện tùy biến có kết nối dữ liệu, khi các control

chuẩn không phù hợp. Móc nối dữ liệu và khuôn dạng dữ liệu có thể được coi là tính năng mạnh nhất của

WPF.

4. Công cụ phát triển WPF

Như đã trình bày ở trên, WPF cung cấp rất nhiều tính năng cho những lập trình viên. Tuy nhiên,

một công nghệ dù có hữu dụng đến đâu cũng cần một công cụ và môi trường tốt để phát huy những lợi

điểm của nó. Đối với WPF, Microsoft cung cấp một công cụ chuyên dùng cho lập trình viên, và một công

cụ khác phục vụ người thiết kế giao diện. Phần dưới đây đề cập ngắn gọn về những công cụ này.

4.1 Microsoft Visual Studio - Công cụ cho lập trình viên

Visual Studio là công cụ chủ đạo của Microsoft dành cho lập trình viên phần mềm. Microsoft

cung cấp thành phần mở rộng cho Visual Studio 2005 cho phép lập trình viên có thể tạo ra các ứng dụng

WPF. Phiên bản tiếp theo của Visual Studio (2008) có bổ sung thêm các tính năng phát triển ứng dụng

WPF, trong đó bao gồm Visual Designer, môi trường thiết kế giao diện cho WPF. Sử dụng công cụ này,

lập trình viên có thể tạo ra giao diện WPF một cách trực quan, trong khi sản sinh các đặc tả XAML tương

ứng một cách tự động.

Page 17: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team |WPF – Bài mở đầu: Tổng quan về WPF 17

4.2 Microsoft Expression Design – Công cụ cho người thiết

kế

Như đã giới thiệu trong phần trước, mục tiêu cơ bản của WPF là nâng cao vị thế của người thiết

kế trong việc tạo giao diện người dùng. Để đạt mục tiêu này, ngoài XAML là công nghệ cốt lõi,

Microsoft cũng đưa ra một công cụ mới cho phép người thiết kế làm việc thuận tiện hơn, đó là Microsoft

Expression Design (Hình 0.10).

Hình 0.10. Giao diện của công cụ Microsoft Expression Design.

Microsoft Expression Design cung cấp những tính năng truyền thống của một công cụ thiết kế,

cho phép người dùng làm việc theo cách quen thuộc. Ngoài ra, công cụ này đặc biệt tập trung vào việc hỗ

trợ tạo giao diện cho các ứng dụng WPF. Tất cả các tính năng WPF mô tả ở trên đều sẵn có trong môi

trường thiết kế này, và cho phép người dùng thiết kế một cách trực quan. Kết quả thiết kế được biểu diễn

dưới dạng file XAML do công cụ này sinh ra, và sau đó có thể được nhập vào môi trường Visual Studio.

Page 18: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team |WPF – Bài mở đầu: Tổng quan về WPF 18

5. Ứng dụng đầu tiên với WPF – Hello World

Phần này giúp các bạn làm quen với lập trình WPF thông qua một ví dụ kinh điển: Hello World.

Ứng dụng chỉ bao gồm một nút bấm có nhãn ban đầu là Hello World. Khi nhắp chuột vào nút, nút sẽ đổi

tên thành “From Hanoi, Vietnam”. Môi trường lập trình ở đây là bộ Visual Studio 2008 với .NET

Framework 3.5.

Quy trình thực hiện như sau:

5.1 Tạo ứng dụng WPF

Ở đây, ta chú ý chọn .Net Framework 3.5.

Giao diện thiết kế ứng dụng:

Page 19: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team |WPF – Bài mở đầu: Tổng quan về WPF 19

Chúng ta sẽ làm 2 phương pháp, một là viết code C# trực tiếp trong ứng dụng, hai là viết bằng

mã XAML.

5.2 Tạo ứng dụng Hello World bằng code C#

Khai báo Button trong lớp Window1. Lớp Button được tạo ra từ namespace:

System.Windows.Controls;

namespace Helloworld

{

public partial class Window1:Window

{

// khai báo 1 button

Button button;

public Window1()

{

InitializeComponent();

}

}

}

Ở phương thức khởi tạo, ta sẽ lần lượt đặt thuộc tính cho nút bấm:

Page 20: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team |WPF – Bài mở đầu: Tổng quan về WPF 20

//tạo mới button

button = new Button();

//xác định thuộc tính cho button

button.Content = "Hello World";

button.LayoutTransform = new ScaleTransform(3, 3);

button.Margin = new System.Windows.Thickness(10);

//thêm phương thức xử lý sự kiện Click cho button

button.Click += new RoutedEventHandler(button_Click);

//đưa button vao Window

this.Content = button;

Để tạo phương thức xử lý sự kiện Click cho button, chúng ta chỉ thêm lệnh button.Click +=

và nhấn Tab 2 lần. Code tự sinh ra như sau:

void button_Click(object sender, RoutedEventArgs e)

{

// xử lý button khi người dùng click

}

Trong phương thức này, ta thêm vào dòng lệnh:

button.Content = "From Hanoi, Vietnam";//đổi nội dung (caption) của

button

Nhấn F5 để biên dịch project và chạy kết quả.

5.3 Tạo ứng dụng Hello World bằng XAML

WPF hỗ trợ trên nền tảng XAML nên ta có thể tạo đối tượng hoàn toàn bằng ngôn ngữ XAML.

Lưu ý, với cùng project trên, muốn viết đặc tả bằng XAML tương đương ta cần xóa bỏ phần mã trình C#

cũ đi, vì C# và XAML không thể cùng sinh một đối tượng.

Cách thực hiện:

Mở file Window1.xaml tương ứng với file code Window1.xaml.cs ở trên.

Thêm đoạn mã đặc tả XAML tương đương sau:

<Window x:Class="Helloworld.Window1"

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

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

Title="Window1" Height="300" Width="300">

<Button Name="button" Margin="10">

<Button.LayoutTransform>

Page 21: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team |WPF – Bài mở đầu: Tổng quan về WPF 21

<ScaleTransform ScaleX="3" ScaleY="3">

</ScaleTransform>

</Button.LayoutTransform>

Hello World

</Button>

</Window>

Giờ ta gán tên phương thức xử lý sự kiện Click cho nút bấm này:

F5 để chạy ứng dụng. Ta sẽ thu được kết quả tương tự như phần 5.2.

Câu hỏi ôn tập

1. WPF là gì?

WPF, viết tắt của Windows Presentation Foundation, là hệ thống API mới hỗ trợ việc

xây dựng giao diện đồ hoạ trên nền Windows. WPF tăng cường khả năng lập trình giao diện của

lập trình viên bằng cách cung cấp các API cho phép tích hợp nhiều dịch vụ trực quan trên cùng

một ứng dụng: các form, control, hình ảnh 2 chiều, 3 chiều, văn bản cố định và thích nghi, in ấn,

đồ họa vector, hoạt họa, móc nối dữ liệu, âm thanh và video. Thêm vào đó, các ứng dụng WPF

còn có khả năng triển khai như một chương trình độc lập hoặc trên trình duyệt Web mà không

phải thay đổi code giao diện. Sử dụng ngôn ngữ markup XAML có bản chất XML, WPF tạo cơ

sở làm việc chung giữa người thiết kế giao diện và người lập trình.

Là một bộ phận của .NET Framework 3.0, WPF sẵn có trong Windows Vista và

Windows Server 2008. Đồng thời, WPF cũng có thể hoạt động trên nền Windows XP Service

Pack 2 hoặc mới hơn, và cả Windows Server 2003.

Page 22: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team |WPF – Bài mở đầu: Tổng quan về WPF 22

2. Mục tiêu cơ bản của WPF là gì?

A. Cung cấp một nền tảng thống nhất để xây dựng giao diện người dùng.

B. Cho phép người lập trình và người thiết kế giao diện làm việc cùng nhau

một cách dễ dàng.

C. Cung cấp một công nghệ chung để xây dựng giao diện người dùng trên

cả Windows và trình duyệt Web.

D. Cả ba mục tiêu trên.

Câu trả lời: D

3. XAML là gì?

XAML, viết tắt của Extensible Application Markup Language, là ngôn ngữ đặc tả dựa

trên XML được dùng để định nghĩa các đối tượng và thuộc tính của chúng, mối quan hệ cũng

như sự tương tác. XAML đặc biệt được dùng trong các công nghệ của .NET Framework 3.0

(trong đó có WPF) như ngôn ngữ đặc tả giao diện người dùng (user interface - UI) nhằm mô tả

cấu trúc và đặc tính của các phần tử UI, sự liên kết dữ liệu, các sự kiện và các đặc tính khác.

XAML là mô hình có tính đột phá trong lĩnh vực tính toán trên Internet được chấp nhận rộng rãi

trong nhiều hệ thống và bởi nhiều nhà cung cấp phần mềm.

4. Vai trò của XAML trong việc tạo môi trường làm việc chung giữa người thiết

kế giao diện và người lập trình.

XAML đóng vai trò một ngôn ngữ chung giữa môi trường thiết kế giao diện và môi

trường lập trình.

Đối với người thiết kế, XAML không những cho phép người thiết kế dễ dàng mô tả,

chỉnh sửa các đối tượng UI và các đặc tính của chúng, mà còn cho phép họ tương tác với các đối

tượng này ở mức độ nhất định. Do vậy, tăng khả năng cảm quan của người thiết kế đối với giao

diện. Ngoài ra, nó cũng giúp người thiết kế hạn chế những ý tưởng đồ họa không khả thi khi lập

trình.

Đối với người lập trình, nhờ một môi trường lập trình có khả năng tự động tái tạo giao

diện đã thiết kế dựa trên file đặc tả bằng XAML do người thiết kế chuyển sang, người lập trình

không cần tự mình tái tạo lại giao diện. Điều này giảm đi nhiều công sức và thời gian để phát

triển giao diện, cũng như tránh những sai lệch giữa giao diện do người thiết kế và giao diện do

người lập trình tái tạo.

Vì cả môi trường thiết kế và lập trình đều có khả năng hiểu và sử dụng XAML, ứng dụng

WPF có thể chuyển qua lại giữa hai môi trường phát triển để sửa đổi hay bổ sung giao diện một

Page 23: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team |WPF – Bài mở đầu: Tổng quan về WPF 23

cách dễ dàng. Với tất cả những lợi điểm này, vị thế của người thiết kế trong việc xây dựng giao

diện được nâng cao.

5. Trong ứng dụng WPF, phần giao diện người dùng có thể được viết bằng:

A. XAML

B. Mã trình (C# hoặc Visual Basic)

C. Cả A và B

Câu trả lời: C

6. Trong ứng dụng WPF, việc xử lý các sự kiện có thể được viết bằng:

A. XAML

B. Mã trình (C# hoặc Visual Basic)

C. Cả A và B

Câu trả lời: B

Tài liệu tham khảo

1. Windows Presentation Foundation, URL: http://msdn.microsoft.com/en-

us/library/ms754130.aspx.

2. Introducing Windows Presentation Foundation, URL: http://msdn.microsoft.com/en-

us/library/aa663364.aspx.

3. WPF Architecture, URL: http://msdn.microsoft.com/en-us/library/ms750441.aspx.

4. WPF Tutorial, URL: http://dotnetslackers.com/articles/silverlight/WPFTutorial.aspx.

5. WPF Tutorials, URL: http://www.wpftutorial.net/.

Page 24: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team | WPF – Bài 2: Các điều khiển (Control) cơ bản của WPF 1

Bài 2

CÁC ĐIỀU KHIỂN (CONTROL) CƠ BẢN CỦA WPF

Trong lập trình giao diện người dùng, điều kiển (Control) là các nhân tố quan trọng cấu thành

nên giao diện người dùng, cho phép họ giao tiếp với ứng dụng. Control có thể được hiểu một cách đơn

giản là các phần tử trên một cửa sổ như các nhãn (Label), hộp soạn thảo (TextBox), nút bẩm (Button),

hộp danh sách (ListBox, ComboBox),.. để hiển thị các thông tin tới người dùng và cho phép người dùng

nhập thông tin cần thiết cho chương trình. Phần này giới thiệu cách tạo lập và sử dụng các Control cơ bản

nhất của cửa sổ xây dựng bằng công nghệ WPF.

1. Tổng quan về tạo lập các điều khiển với WPF

Điểm khác biệt cơ bản giữa mã lệnh tạo giao diện dựa trên WPF so với phương pháp cũ là ứng

dụng WPF sử dụng các đặc tả XAML (ngoài việc sử dụng mã lệnh C# hay VB.Net) để định nghĩa giao

diện, trong khi phương pháp cũ phải sử dụng trực tiếp mã lệnh của C# hay VB.Net để định nghĩa giao

diện. Ví dụ, để xây dựng giao diện cửa sổ đơn giản như Hình 2.1 dưới đây.

Đoạn mã trình bằng XAML:

<Grid>

<Label Height="30" HorizontalAlignment="Left" Margin="10,15,0,0" Name="label1"

VerticalAlignment="Top" Width="60">Họ đệm:</Label>

<Label Height="30" HorizontalAlignment="Left" Margin="10,50,0,0" Name="label2"

VerticalAlignment="Top" Width="60">Tên:</Label>

Label TextBox

Button

Hình 2.1 Một ví dụ về cửa số với các control đơn giản

Page 25: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team | WPF – Bài 2: Các điều khiển (Control) cơ bản của WPF 2

<TextBox Height="30" Margin="80,17,30,0" Name="textBox1" VerticalAlignment="Top" />

<TextBox Height="30" Margin="80,52,30,0" Name="textBox2" VerticalAlignment="Top" />

<Button Height="35" HorizontalAlignment="Left" Margin="16,0,0,27" Name="button1"

VerticalAlignment="Bottom" Width="110">Xem thông tin</Button>

<Button Height="35" HorizontalAlignment="Right" Margin="0,0,24,27" Name="button2"

VerticalAlignment="Bottom" Width="110">Nhập lại</Button>

</Grid>

Đoạn mã trình bằng C#:

// Tạo nhãn Họ đệm

this.label1 = new System.Windows.Forms.Label();

this.label1.AutoSize = true;

this.label1.Location = new System.Drawing.Point(17, 16);

this.label1.Name = "label1";

this.label1.Size = new System.Drawing.Size(48, 13);

this.label1.TabIndex = 0;

this.label1.Text = "Họ đệm:";

//Tạo nhãn Tên

this.label2 = new System.Windows.Forms.Label();

this.label2.AutoSize = true;

this.label2.Location = new System.Drawing.Point(17, 50);

this.label2.Name = "label2";

this.label2.Size = new System.Drawing.Size(29, 13);

this.label2.TabIndex = 0;

this.label2.Text = "Tên:";

//Tạo TextBox nhập Họ đệm

this.textBox1 = new System.Windows.Forms.TextBox();

this.textBox1.Location = new System.Drawing.Point(100, 16);

this.textBox1.Name = "textBox1";

this.textBox1.Size = new System.Drawing.Size(160, 20);

this.textBox1.TabIndex = 1;

//Tạo TextBox nhập Tên

Page 26: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team | WPF – Bài 2: Các điều khiển (Control) cơ bản của WPF 3

this.textBox2 = new System.Windows.Forms.TextBox();

this.textBox2.Location = new System.Drawing.Point(100, 50);

this.textBox2.Name = "textBox2";

this.textBox2.Size = new System.Drawing.Size(160, 20);

this.textBox2.TabIndex = 1;

// Tạo nút bấm Xem thông tin

this.button1 = new System.Windows.Forms.Button();

this.button1.Location = new System.Drawing.Point(20, 114);

this.button1.Name = "button1";

this.button1.Size = new System.Drawing.Size(99, 38);

this.button1.TabIndex = 2;

this.button1.Text = "Xem thông tin";

this.button1.UseVisualStyleBackColor = true;

//Tạo nút bấm nhập lại

this.button2 = new System.Windows.Forms.Button();

this.button2.Location = new System.Drawing.Point(161, 114);

this.button2.Name = "button2";

this.button2.Size = new System.Drawing.Size(99, 38);

this.button2.TabIndex = 2;

this.button2.Text = "Nhập lại";

this.button2.UseVisualStyleBackColor = true;

Như vậy, điều chúng ta cần là tìm hiểu các thẻ XAML để mô tả các Control cần thiết. Tuy nhiên

bạn không cần phải lo lắng nếu như chưa quen với các mã lệnh XAML (dựa trên XML) này vì bộ công

cụ Visual Studio.Net 2008 đã hỗ trợ thiết kế giao diện trực quan và tự động sinh mã XAML tương ứng.

2. Các điều khiển cơ bản trong WPF

Chúng ta sẽ tìm hiểu chi tiết một số điều khiển cơ bản của cửa sổ:

- Label: Nhãn.

- TextBox: Hộp soạn thảo.

- Button: Nút bấm.

- CheckBox: Hộp chọn.

- RadioButton: Hộp chọn radio (chỉ được phép chọn 1 mục trong mỗi nhóm).

- ListBox: Hộp danh sách

Page 27: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team | WPF – Bài 2: Các điều khiển (Control) cơ bản của WPF 4

- ComboBox: Hộp danh sách thả xuống..

1.1 LABEL - Nhãn

Nhãn (Label) là các điều kiển để hiển thị các văn bãn tĩnh, thường được sử dụng để làm nhãn

cho các control khác như Textbox, ListBox, ComboBox,….

Các Label đuợc mô tả bằng đoạn mã XAML sau:

<Grid>

<Label Height="30" HorizontalAlignment="Left" Margin="10,15,0,0" Name="label1"

VerticalAlignment="Top" Width="60">Họ đệm:</Label>

</Grid>

Nhãn đuợc bắt đầu <Label> và kết thúc là </Label>, nội dung cũa nhãn là đoạn văn bản đặt giữa

cặp thẻ này. Trong ví dụ này “Họ đệm:” là nội dung của nhãn.

Bên trong thẻ <Label> có rất nhiều đặc tính để mô tả về thẻ, trong đó:

- Height="30” : Độ cao của khung nhãn là 30px

- HorizontalAlignment="Left" : Nhãn được căn trái trong cửa sổ

- Margin="10,15,0,0" : có 4 giá trị là Left,Top,Right,Bottom

- Name="label1" : Tên của nhãn là lablel1

- VerticalAlignment="Top" :Nhãn được căn theo đỉnh của cửa sổ.

- Width="60": Chiều rộng của nhãn là 60px

Trên đây là một số đặc tính cơ bản của nhãn, ngoài ra còn có nhiều đặc tính khác áp dụng cho

nhãn như màu nền, màu chữ,….

Label

Label

Hình 2.2 Minh họa về label

Page 28: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team | WPF – Bài 2: Các điều khiển (Control) cơ bản của WPF 5

1.2 TextBox – Hộp soạn thảo

Hộp soạn thảo (TextBox) là control cho phép người dùng nhập dữ liệu dạng văn bản.

Dưới đây là đoạn mã XAML của hộp soạn thảo

<Grid>

<TextBox Height="30" Margin="80,17,30,0" Name="textBox1" VerticalAlignment="Top" /> Hộp soạn

thảo

</TextBox>

<TextBox Height="30" Margin="80,52,30,0" Name="textBox2" VerticalAlignment="Top" />

</Grid>

Hộp soạn thảo được tạo nên bởi thẻ <TextBox/>. Nếu muốn thiết lập sẵn nội dung mặc định cho

hộp soạn thảo, ta đặt nội dung này vào giữa cặp thẻ <TextBox/> Nội dung </TextBox>. Nếu không muốn

đặt giá trị mặc định thì không cần thẻ đóng </TextBox>.

Thẻ <TextBox/> cũng có nhiều đặc tính, trong đó:

- Margin="80,17,30,0": Cách lề trái 80, đỉnh cửa sổ 17, cạnh phải 30

- Name="textBox1": Tên của hộp soạn thảo là textBox1

- VerticalAlignment="Top": Căn theo đỉnh cửa sổ

Đặc điểm của hộp soạn thảo với các đặc tính trên là khi người dùng co dãn, thay đổi kích thước

cửa sổ, chiều rộng của hộp soạn thảo tự động co dãn theo.

Hình 2.3 Minh họa về TextBox

TextBox

TextBox

Page 29: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team | WPF – Bài 2: Các điều khiển (Control) cơ bản của WPF 6

1.3 Button – Nút bấm

Nút bấm (Button) là loại điều khiển cho phép người dùng nhấn chuột để chọn lệnh, khi nhấn vào

nút bấm, nó sẽ sinh ra sự kiện Click và sẽ chạy các lệnh gắn với sự kiện này.

Dưới đây là đoạn mã sinh ra các nút bấm trên

<Grid>

<Button Height="35" HorizontalAlignment="Left" Margin="16,0,0,27" Name="button1"

VerticalAlignment="Bottom" Width="110" Click="button1_Click">Xem thông tin</Button>

<Button Height="35" HorizontalAlignment="Right" Margin="0,0,24,27" Name="button2"

VerticalAlignment="Bottom" Width="110">Nhập lại</Button>

</Grid>

Nút bấm được bắt đầu bằng thẻ <Button> và kết thúc bằng thẻ </Button>. Nhãn của nút bấm được

đặt trong cặp thẻ <Button> Nhãn nút bấm </Button>.

Nút bấm có nhiều đặc tính, trong đó:

- Height="35": Chiều cao nút bấm là 35

- Width="110": Chiều rộng là 110

- HorizontalAlignment="Left": Căn theo lề trái

- VerticalAlignment="Bottom": Căn theo đáy cửa sổ

- Margin="16,0,0,27": Cách lề trái 16, cách đáy 27

- Name="button1": Tên nút bấm là button1

Hình 2.4 Minh họa về nút bấm

Button

Button

Page 30: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team | WPF – Bài 2: Các điều khiển (Control) cơ bản của WPF 7

- Click="button1_Click": Khi nhấn chuột vào nút sẽ kích hoạt phương thức

button1_Click()

Vi dụ về đoạn mã lệnh gắn với cửa sổ chứa các điều khiển trên:

namespace WpfControlSample1

{

public partial class Window1 : Window

{

public Window1()

{

InitializeComponent();

}

private void button1_Click(object sender, RoutedEventArgs e)

{

String strHoTen;

strHoTen = textBox1.Text + " " + textBox2.Text;

MessageBox.Show("Xin chào: " + strHoTen);

}

}

}

1.4 Radio Button và CheckBox

Radio Button và CheckBox đều là điều khiển dạng hộp chọn. Tuy nhiên, điểm khác biệt cơ bản

giữa hai loại điều khiển này là:

Radio Button: là hộp chọn theo nhóm, nghĩa là các hộp trong cùng một nhóm sẽ loại trừ nhau,

tại một thời điểm người dùng chỉ được chọn một trong các mục. Vi dụ như hộp chọn giới tính, ta phải sử

dụng radio vì tại một thời điểm chỉ cho phép chọn Nam hoặc Nữ

CheckBox: là hộp chọn mà người dùng có thể chọn một hoặc nhiều mục cùng một lúc. Vi dụ

như mục chọn Ngoại ngữ, cho phép người dùng chọn đồng thời nhiều mục.

Page 31: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team | WPF – Bài 2: Các điều khiển (Control) cơ bản của WPF 8

Dưới đây là đoạn mã tạo Radio Button và CheckBox.

<Grid>

<Label Height="30" HorizontalAlignment="Left" Margin="10,94,0,0" Name="label3"

VerticalAlignment="Top" Width="60">Giới tính:</Label>

<RadioButton Height="22" Margin="80,99,0,0" Name="radioButton1" VerticalAlignment="Top"

HorizontalAlignment="Left" Width="79" GroupName="GioiTinh" IsChecked="True">Nam</RadioButton>

<RadioButton Height="22" HorizontalAlignment="Right" Margin="0,99,30,0" Name="radioButton2"

VerticalAlignment="Top" Width="79" GroupName="GioiTinh">Nữ</RadioButton>

<Label HorizontalAlignment="Left" Margin="10,127,0,105" Name="label4" Width="69">Ngoại

ngữ:</Label>

<CheckBox Margin="84,0,119,110" Name="checkBox1" Height="20" VerticalAlignment="Bottom"

IsChecked="True">Tiếng Anh</CheckBox>

<CheckBox HorizontalAlignment="Right" Margin="0,0,24,110" Name="checkBox2" Width="85"

Height="20" VerticalAlignment="Bottom">Tiếng Trung</CheckBox>

</Grid>

Radio Button được tạo bởi thẻ <RadioButton> và kết thúc bởi</RadioButton>, giữa cặp thẻ này là

nhãn của Radio Button <RadioButton> Nhãn </RadioButton>.

Hình 2.5 Minh họa về hộp chọn Radio và CheckBox

Radio Button CheckBox

Page 32: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team | WPF – Bài 2: Các điều khiển (Control) cơ bản của WPF 9

CheckBox được tạo bởi thẻ <CheckBox > và kết thúc bởi</CheckBox>, giữa cặp thẻ này là nhãn

của CheckBox <CheckBox> Nhãn </CheckBox>.

Cả hai thẻ này đều có đặc tính IsChecked="True" hoặc IsChecked="False". Mục nào có thuộc tính

này sẽ được tự động chọn khi cửa sổ bắt đầu hiển thị.

Đối với Radio Button, vì là hộp chọn loại trừ, nếu trong một cửa sổ có nhiều nhóm Radio Button

khác nhau thì các Radio Button của mỗi nhóm đuợc phân biệt bởi đặc tính GroupName="TenNhom". Ví

dụ, trên cùng một cửa sổ có hai Radio Button chọn Giới tính (Nam; Nữ) và ba Radio Button khác chọn

nghề nghiệp (Kinh doanh; Kỹ Thuật; Marketing) thì các Radio Button Nam, Nu phải có cùng

GroupName với nhau, ba Radio Button Kinh doanh, Ky Thuat, Marketting phải có cùng GroupName và

khác với GroupName của nhóm giới tính.

Đoạn mã lệnh minh họa kiểm tra mục chọn Radio

private void button1_Click(object sender, RoutedEventArgs e)

{

String strMessage, strHoTen, strTitle, strNgoaiNgu="";

strHoTen = textBox1.Text + " " + textBox2.Text;

if (radioButton1.IsChecked==true)

strTitle = "Mr.";

else

strTitle = "Miss/Mrs.";

strMessage = "Xin chào: " + strTitle + " " + strHoTen;

MessageBox.Show(strMessage);

}

Đoạn mã lệnh minh họa kiểm tra mục chọn CheckBox

private void button1_Click(object sender, RoutedEventArgs e)

{

String strMessage, strNgoaiNgu="";

if (checkBox1.IsChecked == true)

{

strNgoaiNgu = "Tiếng Anh";

}

if (checkBox2.IsChecked == true)

{

strNgoaiNgu = (strNgoaiNgu.Length==0) ? "Tiếng Trung": (strNgoaiNgu+ " và Tiếng Trung");

Page 33: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team | WPF – Bài 2: Các điều khiển (Control) cơ bản của WPF 10

}

strMessage = "Ngoại ngữ: " + strNgoaiNgu;

MessageBox.Show(strMessage);

}

1.5 ListBox - Hộp danh sách

Hộp danh sách (ListBox) và là điều khiển hiển thị một danh sách các mục theo từng dòng và cho

phép người dùng chọn một hay nhiều phẩn tử của danh sách.

Ví dụ về hộp danh sách chọn Quê quán:

Dưới đây là đoạn mã tạo ListBox

<Grid>

<Label Height="30" HorizontalAlignment="Left" Margin="10,0,0,126" Name="label5"

VerticalAlignment="Bottom" Width="69">Quê quán:</Label>

<ListBox Height="68" Margin="80,0,33,77" Name="listBox1" VerticalAlignment="Bottom"

SelectedIndex="0">

<ListBoxItem>Hà nội</ListBoxItem>

<ListBoxItem>TP. Hồ Chí Minh</ListBoxItem>

<ListBoxItem>Hải Phòng</ListBoxItem>

Hình 2.6 Minh họa về hộp chọn ListBox

ListBox

Page 34: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team | WPF – Bài 2: Các điều khiển (Control) cơ bản của WPF 11

<ListBoxItem>Đà Nẵng</ListBoxItem>

</ListBox>

</Grid>

ListBox được tạo bởi thẻ <ListBox> và kết thúc bằng thẻ đóng </ListBox> .

Mỗi phần tử của danh sách nằm trong cặp thẻ <ListBoxItem> Nhãn </ListBoxItem> lồng bên trong

cặp thẻ trên.

Đặc tính SelectedIndex="k" để yêu cầu tự động chọn phần thử thứ n trong danh sách khi mở cửa

sổ. Phần tử đầu tiên của danh sách có giá trị là 0, phần tử cuối cùng là n-1. Nếu muốn khi mở cửa sổ

không chọn phần tử nào thì đặt giá trị k bằng -1.

Đoạn mã lệnh lấy mục chọn từ ListBox

private void button1_Click(object sender, RoutedEventArgs e)

{

String strMessage;

if (listBox1.SelectedIndex >= 0)//Nếu đã có một mục trong danh sách được chọn

{

strMessage = “Quê Quán: " + listBox1.Text;

}

MessageBox.Show(strMessage);

}

1.6 ComboBox - hộp danh sách thả xuống

Hộp danh sách thả xuống (ComboBox) là các điều khiển hiển thị một danh sách theo từng dòng

cho người dùng chọn. Tuy nhiên, khác với ListBox, ComboBox gọn gàng hơn bởi vì nó chỉ hiển thị 1

dòng và khi nhấn vào biểu tượng tam giác bên cạnh thì danh sách mới được mở ra. Combox chỉ cho phép

chọn 1 dòng tại 1 thời điểm.

Ví dụ về hộp danh sách thả xuống chọn Quê quán:

Page 35: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team | WPF – Bài 2: Các điều khiển (Control) cơ bản của WPF 12

Dưới đây là đoạn mã tạo ComboBox

<Grid>

<Label Height="30" HorizontalAlignment="Left" Margin="10,0,0,126" Name="label5"

VerticalAlignment="Bottom" Width="69">Quê quán:</Label>

<ComboBox Height="26" Margin="84,0,27,126" Name="comboBox1" VerticalAlignment="Bottom"

SelectedIndex="0">

<ComboBoxItem>Hà nội</ComboBoxItem>

<ComboBoxItem>TP. Hồ Chí Minh</ComboBoxItem>

<ComboBoxItem>Hải Phòng</ComboBoxItem>

<ComboBoxItem>Đà Nẵng</ComboBoxItem>

</ComboBox>

</Grid>

ComboBox được tạo bởi thẻ <ComboBox> và kết thúc bằng thẻ đóng </ComboBox> .

Mỗi phần tử của danh sách nằm trong cặp thẻ <ComboBoxItem> Nhãn </ComboBoxItem> Lồng bên

trong cặp thẻ trên.

Hình 2.7 Minh họa về hộp chọn ComboBox

ComboBox

Page 36: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team | WPF – Bài 2: Các điều khiển (Control) cơ bản của WPF 13

Đặc tính SelectedIndex="k" để yêu cầu tự động chọn phần thử thứ n trong danh sách khi mở cửa

sổ. Phần tử đầu tiên của danh sách có giá trị là 0, phần tử cuối cùng là n-1. Nếu muốn khi mở cửa sổ

không chọn phần tử nào thì đặt giá trị k bằng -1.

Đoạn mã lệnh lấy mục chọn từ ComboBox

private void button1_Click(object sender, RoutedEventArgs e)

{

String strMessage;

if (comboBox1.SelectedIndex >= 0)//Nếu đã có một mục trong danh sách được chọn

{

strMessage = “Quê Quán: " + comboBox1.Text;

}

MessageBox.Show(strMessage);

}

3. Ví dụ xây dựng các control trong WPF

Yêu cầu: Viết chương trình hiển thị cửa sổ như sau

Khi nhấn Xem thông tin, nếu chọn Nam thì hiện lời chào “Xin chào Mr. Barack Obama”, nếu

chọn Nữ thì hiện lời chào “Xin chào Miss/Mrs. Barack Obama”, như sau:

Page 37: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team | WPF – Bài 2: Các điều khiển (Control) cơ bản của WPF 14

Nếu chọn Nhập lại, đưa trạng thái các control trên cửa sổ về trạng thái ban đầu.

Các bước thực hiện như sau:

3.1 Tạo ứng dụng WPF

Ở đây, ta chú ý chọn .Net Framework 3.5.

Giao diện thiết kế ứng dụng:

Page 38: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team | WPF – Bài 2: Các điều khiển (Control) cơ bản của WPF 15

3.2 Mã XAML của giao diện

Mở file Window1.xaml tương ứng với file code Window1.xaml.cs.

<Window x:Class="WpfControlSample1.Window1"

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

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

Title="Ví dụ Control - WPF" Height="352" Width="300">

<Grid>

<Label Height="30" HorizontalAlignment="Left" Margin="10,15,0,0" Name="label1"

VerticalAlignment="Top" Width="60" Focusable="False">Họ đệm:</Label>

<Label Height="30" HorizontalAlignment="Left" Margin="10,50,0,0" Name="label2"

VerticalAlignment="Top" Width="60">Tên:</Label>

<TextBox Height="30" Margin="80,17,30,0" Name="textBox1" VerticalAlignment="Top">Hộp soạn

thảo</TextBox>

<TextBox Height="30" Margin="80,52,30,0" Name="textBox2" VerticalAlignment="Top" />

Page 39: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team | WPF – Bài 2: Các điều khiển (Control) cơ bản của WPF 16

<Button Height="35" HorizontalAlignment="Left" Margin="16,0,0,27" Name="button1"

VerticalAlignment="Bottom" Width="110" Click="button1_Click">Xem thông tin</Button>

<Button Height="35" HorizontalAlignment="Right" Margin="0,0,24,27" Name="button2"

VerticalAlignment="Bottom" Width="110" Click="button2_Click">Nhập lại</Button>

<Label Height="30" HorizontalAlignment="Left" Margin="10,94,0,0" Name="label3"

VerticalAlignment="Top" Width="60">Giới tính:</Label>

<RadioButton Height="22" Margin="80,99,0,0" Name="radioButton1" VerticalAlignment="Top"

HorizontalAlignment="Left" Width="79" GroupName="GioiTinh" IsChecked="True">Nam</RadioButton>

<RadioButton Height="22" HorizontalAlignment="Right" Margin="0,99,30,0" Name="radioButton2"

VerticalAlignment="Top" Width="79" GroupName="GioiTinh">Nữ</RadioButton>

<Label HorizontalAlignment="Left" Margin="10,127,0,154" Name="label4" Width="69">Ngoại

ngữ:</Label>

<CheckBox Margin="84,132,119,0" Name="checkBox1" Height="20" VerticalAlignment="Top"

IsChecked="True">Tiếng Anh</CheckBox>

<CheckBox HorizontalAlignment="Right" Margin="0,132,24,0" Name="checkBox2" Width="85"

Height="20" VerticalAlignment="Top">Tiếng Trung</CheckBox>

<Label Height="30" HorizontalAlignment="Left" Margin="10,0,0,126" Name="label5"

VerticalAlignment="Bottom" Width="69">Quê quán:</Label>

<ComboBox Height="26" Margin="84,0,27,126" Name="comboBox1" VerticalAlignment="Bottom"

SelectedIndex="0">

<ComboBoxItem>Hà nội</ComboBoxItem>

<ComboBoxItem>TP. Hồ Chí Minh</ComboBoxItem>

<ComboBoxItem>Hải Phòng</ComboBoxItem>

<ComboBoxItem>Đà Nẵng</ComboBoxItem>

</ComboBox>

</Grid>

</Window>

Gán tên phương thức button1_Click xử lý sự kiện Click cho nút bấm button1 này, khi nhấn chuột

vào nút “Xem thông tin” thì phương thức button1_Click sẽ được thực thi.

<Button Height="35" HorizontalAlignment="Left" Margin="16,0,0,27" Name="button1"

VerticalAlignment="Bottom" Width="110" Click="button1_Click">Xem thông tin</Button>

Gán tên phương thức button2_Click xử lý sự kiện Click cho nút bấm button2 này, khi nhấn chuột

vào nút “Nhập lại” thì phương thức button2_Click sẽ được thực thi.

<Button Height="35" HorizontalAlignment="Right" Margin="0,0,24,27" Name="button2"

VerticalAlignment="Bottom" Width="110" Click="button2_Click">Nhập lại</Button>

Page 40: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team | WPF – Bài 2: Các điều khiển (Control) cơ bản của WPF 17

3.3 Mã lệnh C# xử lý các sự kiện nhấn nút

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Windows;

using System.Windows.Controls;

using System.Windows.Data;

using System.Windows.Documents;

using System.Windows.Input;

using System.Windows.Media;

using System.Windows.Media.Imaging;

using System.Windows.Navigation;

using System.Windows.Shapes;

namespace WpfControlSample1

{

/// <summary>

/// Interaction logic for Window1.xaml

/// </summary>

public partial class Window1 : Window

{

public Window1()

{

InitializeComponent();

}

private void button1_Click(object sender, RoutedEventArgs e)

{

String strMessage, strHoTen, strTitle, strNgoaiNgu="";

strHoTen = textBox1.Text + " " + textBox2.Text;

if (radioButton1.IsChecked==true)

strTitle = "Mr.";

else

strTitle = "Miss/Mrs.";

strMessage = "Xin chào: " + strTitle + " " + strHoTen;

if (checkBox1.IsChecked == true)

{

Page 41: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team | WPF – Bài 2: Các điều khiển (Control) cơ bản của WPF 18

strNgoaiNgu = "Tiếng Anh";

}

if (checkBox2.IsChecked == true)

{

strNgoaiNgu = (strNgoaiNgu.Length==0) ? "Tiếng Trung": (strNgoaiNgu+" và Tiếng Trung");

}

strMessage += "\n Ngoại ngữ: " + strNgoaiNgu;

if (comboBox1.SelectedIndex >= 0)//Nếu đã có một mục trong danh sách được chọn

{

strMessage += "\n Quê Quán: " + comboBox1.Text;

}

MessageBox.Show(strMessage);

}

private void button2_Click(object sender, RoutedEventArgs e)

{

textBox1.Text = "";

textBox2.Text = "";

radioButton1.IsChecked = true;

radioButton2.IsChecked = false;

checkBox1.IsChecked = false;

checkBox2.IsChecked = false;

comboBox1.SelectedIndex = 0;

}

}

}

F5 để chạy ứng dụng. Ta sẽ thu được kết quả tương tự như yêu cầu.

Câu hỏi ôn tập

1. Control là gì?

Control phần tử trên một cửa sổ như các nhãn (Label), hộp soạn thảo (TextBox), nút bẩm

(Button), hộp danh sách (ListBox, ComboBox),.. để hiển thị các thông tin tới người dùng và cho

phép người dùng nhập thông tin cần thiết cho chương trình.

2. Các control trong WPF được định bằng?

A. Đặc tả XAML

B. Mã trình (C# hoặc Visual Basic)

Page 42: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team | WPF – Bài 2: Các điều khiển (Control) cơ bản của WPF 19

C. Cả A và B

Câu trả lời: C

3. Các control cơ bản trong WPF bao gồm? (chọn nhiều)

A. Label, TextBox, Button

B. Radio Button, CheckBox, ListBox và ComboBox

C. Grid, WrapPanel

D. Cả A, B và C

Câu trả lời: A và B

4. Được phép chọn đồng thời nhiều Radio Button trong cùng nhóm:

A. Đúng

B. Sai

Câu trả lời: B

5. Được phép chọn đồng thời nhiều CheckBox:

A. Đúng

B. Sai

Câu trả lời: A

6. Khi dùng thẻ <TextBox> để tạo hộp soạn thảo, bắt buộc phải kết thúc bằng

thẻ đóng </TextBox>:

A. Đúng

B. Sai

Câu trả lời: B

Tài liệu tham khảo

1. Windows Presentation Foundation, URL: http://msdn.microsoft.com/en-

us/library/ms754130.aspx.

2. Introducing Windows Presentation Foundation, URL: http://msdn.microsoft.com/en-

us/library/aa663364.aspx.

3. WPF Architecture, URL: http://msdn.microsoft.com/en-us/library/ms750441.aspx.

4. WPF Tutorial, URL: http://dotnetslackers.com/articles/silverlight/WPFTutorial.aspx.

Page 43: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team | WPF – Bài 2: Các điều khiển (Control) cơ bản của WPF 20

5. WPF Tutorials, URL: http://www.wpftutorial.net/.

6. Windows Presentation Foundation Control, http://msdn.microsoft.com/en-

us/library/ms752069.aspx

Page 44: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team |WPF –Bài 3: Các điều khiển nâng cao trong WPF 1

Bài 3

CÁC ĐIỀU KHIỂN NÂNG CAO TRONG ỨNG DỤNG WPF

Không dừng lại ở việc cung cấp những điều khiển UI như ComboBox, ListBox,

TextBox,…, với những chức năng cơ bản và đặc tính text đơn điệu như trong Windows Form, WPF

còn cho phép người lập trình tùy biến thuộc tính của những điều khiển trên để biến chúng thành

những điều khiển UI phức hợp, với nhiều đặc tính giao diện phong phú, tinh tế, kết hợp text, hình

ảnh,… Để đạt được hiệu quả tương tự, với những công nghệ trước đây như MFC, cần tiêu tốn nhiều

công sức lập trình. Qua các ví dụ cụ thể trong bài giảng này, chúng ta sẽ thấy WPF tạo ra chúng đơn

giản như thế nào.

1 Hộp lựa chọn phông chữ (Font Chooser)

Mục tiêu của phần này là tạo lập một điều khiển dạng ComboBox, trong đó, liệt kê danh

sách các phông chữ hệ thống. Tên của mỗi phông chữ lại được hiển thị dưới dạng chính phông chữ

đó. Điều này cho phép người dùng xem trước định dạng phông chữ trước khi chọn chúng. Bạn có

thể đã quen thuộc với dạng Combox này khi sử dụng các ứng dụng gần đây của Microsoft Office

như Word, Excel, PowerPoint,…

Và sau đây là mã XAML để tạo ra điều khiển này:

<!--Sử dụng stack panel theo chiều dọc làm layout chính của form-->

<StackPanel Width="250" Orientation="Vertical" Height="100" >

<!--Khai báo tạo lập tiêu đề của điều khiển-->

<TextBlock FontFamily="Verdana" FontWeight="DemiBold">

Danh mục phông hệ thống:

</TextBlock>

<!--Khai báo tạo điều khiển Combox-->

<ComboBox ItemsSource="{x:Static Fonts.SystemFontFamilies}"

SelectedIndex="0">

<!--Khai báo định nghĩa thuộc tính dữ liệu gắn với mỗi mục chọn-->

<ComboBox.ItemTemplate>

<DataTemplate>

Page 45: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team |WPF –Bài 3: Các điều khiển nâng cao trong WPF 2

<TextBlock Text="{Binding}" FontFamily="{Binding}"/>

</DataTemplate>

</ComboBox.ItemTemplate>

</ComboBox>

</StackPanel>

Trong phần khai báo tạo điều khiển ComboBox, ta khai báo nguồn dữ liệu được dùng cho

các mục trong hộp danh sách thông qua thuộc tính ItemsSource. Bằng việc gán

ItemsSource="{x:Static Fonts.SystemFontFamilies}" ta định nghĩa nguồn dữ liệu này là

danh sách các phông chữ hiện có của hệ thống máy tính hiện thời. Thuộc tính SelectedIndex cho

phép định ra chỉ số của chỉ mục ngầm định được chọn ban đầu trong danh sách phông, cụ thể trong

trường hợp này là phông chữ đầu tiên (SelectedIndex="0").

Trong phần khai báo định nghĩa thuộc tính dữ liệu của mỗi chỉ mục trong ComboBox (phần

tử <ComboBox.ItemTemplate>), ta lồng vào một điều khiển TextBlock, trong đó, nội dung hiển

thị là phông chữ tương ứng (Text="{Binding}") và dạng phông hiển thị nội dung cũng chính là

phông chữ tương ứng với chỉ mục này (FontFamily="{Binding}"). Những vấn đề liên quan đến

kết nối nguồn dữ liệu sẽ được đề cập chi tiết hơn trong các bài giảng tiếp sau.

Biên dịch và chạy chương trình, ta có kết quả như minh họa ở Hình 3.1. Như vậy, chỉ với

không hơn 20 dòng mã XAML, chúng ta đã có thể tạo ra một điều khiển rất hữu dụng.

Page 46: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team |WPF –Bài 3: Các điều khiển nâng cao trong WPF 3

Hình 3.1 – Hộp lựa chọn phông chữ được xây dựng bằng WPF

2 Hộp danh mục ảnh (Image ListBox)

Trong phần này, ta xây dựng một hộp danh mục (ListBox) các đồ uống có kèm theo ảnh. Rõ

ràng tính trực quan của giao diện người dùng sẽ tăng hơn nhiều so với một danh sách dạng text đơn

điệu.

2.1 Thêm dữ liệu ảnh vào tài nguyên của project

Trước hết, ta thêm các ảnh đồ uống cần thiết vào tài nguyên của project theo các bước sau:

- Ở cửa sổ Solution Explorer, ta nhắp chuột phải vào tên project Xuất hiện bảng

chọn chức năng.

- Chọn mục Add…>Existing Item Xuất hiện cửa sổ cho phép lựa chọn file.

- Trong hộp danh sách Files of type, ta chọn Image Files Các file ảnh trong

thư mục hiện thời sẽ xuất hiện.

- Tìm đến các file ảnh cần hiển thị trong danh sách và chọn OK.

- Kết quả, trong cửa sổ Solution Explorer, ta thấy xuất hiện các file ảnh tương ứng.

Page 47: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team |WPF –Bài 3: Các điều khiển nâng cao trong WPF 4

2.2 Xây dựng mã XAML

Giả thiết rằng các file ảnh đã được nạp vào project, sau đây là mã XAML tạo lập hộp danh

mục đồ uống theo yêu cầu:

<!--Khai báo tạo lập một hộp danh mục với các thuộc tính về căn lề,

chiều rộng,…-->

<ListBox Margin="10,10,0,13" Width="280" Name="listBox1"

HorizontalAlignment="Left"

VerticalAlignment="Top">

<!--Khai báo tạo lập một chỉ mục con trong hộp danh mục với thuộc tính

màu nền-->

<ListBoxItem Background="Beige">

<!--Lồng vào chỉ mục này một StackPanel để có thể chứa nhiều hơn 1

phần tử UI con theo chiều ngang-->

<StackPanel Orientation="Horizontal">

<!--Khai báo tạo lập một ảnh đồ uống ở đầu mỗi chỉ mục-->

<Image Width="50" Height="50" Source="orange_juice.jpg"></Image>

<!--Khai báo tạo lập một dòng chữ chỉ tên đồ uống-->

<TextBlock Margin="5" VerticalAlignment="Center" FontFamily="Times

New Roman" FontStyle="Italic" FontSize="18">Nước cam tươi</TextBlock>

</StackPanel>

</ListBoxItem>

<!--Khai báo chỉ mục 2 tương tự như trên-->

<ListBoxItem>

<StackPanel Orientation="Horizontal">

<Image Width="50" Height="50" Source="kiwi_juice.jpg"></Image>

<TextBlock Margin="5" VerticalAlignment="Center" FontFamily="Times

New Roman" FontStyle="Italic" FontSize="18">Nước kiwi ép</TextBlock>

</StackPanel>

</ListBoxItem>

<!--Khai báo chỉ mục 3 tương tự như trên-->

<ListBoxItem Background="Beige">

Page 48: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team |WPF –Bài 3: Các điều khiển nâng cao trong WPF 5

<StackPanel Orientation="Horizontal">

<Image Width="50" Height="50" Source="mango_juice.jpg"></Image>

<TextBlock Margin="5" VerticalAlignment="Center" FontFamily="Times

New Roman" FontStyle="Italic" FontSize="18">Nước soài ép</TextBlock>

</StackPanel>

</ListBoxItem>

<!--Khai báo chỉ mục 4 tương tự như trên-->

<ListBoxItem>

<StackPanel Orientation="Horizontal">

<Image Width="50" Height="50" Source="milk.jpg"></Image>

<TextBlock Margin="5" VerticalAlignment="Center" FontFamily="Times

New Roman" FontStyle="Italic" FontSize="18">Sữa tươi tiệt trùng</TextBlock>

</StackPanel>

</ListBoxItem>

<!--Khai báo chỉ mục 5 tương tự như trên-->

<ListBoxItem Background="Beige">

<StackPanel Orientation="Horizontal">

<Image Width="50" Height="50" Source="coffee.jpg"></Image>

<TextBlock Margin="5" VerticalAlignment="Center" FontFamily="Times

New Roman" FontStyle="Italic" FontSize="18">Cà phê Espresso</TextBlock>

</StackPanel>

</ListBoxItem>

</ListBox>

Như vậy, điểm mấu chốt để bổ sung thêm các thuộc tính giao diện như ảnh, text,

checkbox,…, vào mỗi chỉ mục của hộp danh sách chính là việc kết hợp các phần tử UI riêng lẻ

tương ứng vào cùng một phần tử Panel nằm trong khai báo chỉ mục. Trong trường hợp này, với mỗi

khai báo chỉ mục <ListBoxItem> ta thêm vào một <StackPanel Orientation="Horizontal">

theo chiều ngang, trong đó, chứa một phần tử <Image> và 1 phần tử <TextBlock> . Nguồn dữ liệu

ảnh được xác định qua thuộc tính Source="<tên ảnh đã được bổ sung vào project>".

Kết quả của đoạn code được minh hoạ trong hình 3.2.

Page 49: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team |WPF –Bài 3: Các điều khiển nâng cao trong WPF 6

Hình 3.2 – Danh mục đồ uống có kèm ảnh minh hoạ xây dựng bằng WPF

3 Hộp mở rộng (Expander)

Hộp mở rộng Expander là một trong những điều khiển UI mới được giới thiệu trong WPF như một

điều khiển cơ bản. Expander cho phép thu gọn hoặc mở rộng một nội dung nào đó chứa trong nó, giống như

một node trong TreeView, bằng việc click vào biểu tượng mũi tên (hướng lên, nếu điều khiển đang ở trạng

thái mở rộng; hướng xuống, nếu đang ở trạng thái thu gọn). Điều khiển này rất tiện lợi: Khi diện tích form

chính quá chật hẹp vì nhiều chức năng được trình bày trên cùng giao diện, ta có thể sử dụng Expander để

chứa một số chức năng ít dùng có thể tạm thời được ẩn dưới một tên nhóm chung.

Trong ví dụ sau đây, ta sẽ làm một menu chứa 2 mục là đồ uống và đồ ăn, mỗi mục sẽ chứa danh

sách các sản phẩm tương ứng mà nhà hàng cung cấp. Ta sử dụng Expander để có thể mở rộng/thu gọn từng

mục nêu trên. Sau đây là mã XAML của ứng dụng:

<Window x:Class="Lesson3.Window2"

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

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

Title="Lesson 3 - Expander WPF Sample" Height="250" Width="200">

Page 50: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team |WPF –Bài 3: Các điều khiển nâng cao trong WPF 7

<!--Sử dụng stackpanel làm layout chính-->

<StackPanel>

<!--Khai báo tạo lập Expander chứa danh mục đồ uống-->

<Expander FontFamily="Times New Roman" FontSize="14"

FontStyle="Oblique" Header="Đồ uống" Background="#acbf43" >

<!---Khai báo ListBox chứa danh mục đồ uống-->

<ListBox >

<!--Khai báo các chỉ mục con trong danh mục đồ uống-->

<!--Mỗi mục con được trang trí bằng một biểu tượng đi kèm text-->

<ListBoxItem>

<StackPanel Orientation="Horizontal">

<Image Source="glass.jpg" Height="20"></Image>

<TextBlock VerticalAlignment="Center">Nước chanh

leo</TextBlock>

</StackPanel>

</ListBoxItem>

<ListBoxItem>

<StackPanel Orientation="Horizontal">

<Image Source="glass.jpg" Height="20"></Image>

<TextBlock VerticalAlignment="Center">Nước cam vắt</TextBlock>

</StackPanel>

</ListBoxItem>

<ListBoxItem>

<StackPanel Orientation="Horizontal">

<Image Source="glass.jpg" Height="20"></Image>

<TextBlock VerticalAlignment="Center">Nước mơ muối</TextBlock>

</StackPanel>

</ListBoxItem>

<ListBoxItem>

<StackPanel Orientation="Horizontal">

<Image Source="glass.jpg" Height="20"></Image>

<TextBlock VerticalAlignment="Center">Sữa chua đánh

đá</TextBlock>

</StackPanel>

</ListBoxItem>

Page 51: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team |WPF –Bài 3: Các điều khiển nâng cao trong WPF 8

</ListBox>

</Expander>

<!--Khai báo Expander chứa danh mục đồ ăn-->

<Expander FontFamily="Times New Roman" FontSize="14" FontStyle="Oblique"

Header="Đồ ăn" Background="DarkOrange">

<ListBox>

<!--Khai báo các chỉ mục con trong danh mục đồ ăn-->

<ListBoxItem>

<StackPanel Orientation="Horizontal">

<Image Source="bowl.gif" Height="20"></Image>

<TextBlock VerticalAlignment="Center">Phở tái gàu</TextBlock>

</StackPanel>

</ListBoxItem>

<ListBoxItem>

<StackPanel Orientation="Horizontal">

<Image Source="bowl.gif" Height="20"></Image>

<TextBlock VerticalAlignment="Center">Bún bò giò

heo</TextBlock>

</StackPanel>

</ListBoxItem>

<ListBoxItem>

<StackPanel Orientation="Horizontal">

<Image Source="bowl.gif" Height="20"></Image>

<TextBlock VerticalAlignment="Center">Bánh cuốn tôm

nõn</TextBlock>

</StackPanel>

</ListBoxItem>

<ListBoxItem>

<StackPanel Orientation="Horizontal">

<Image Source="bowl.gif" Height="20"></Image>

<TextBlock VerticalAlignment="Center">Bánh đa cua</TextBlock>

</StackPanel>

</ListBoxItem>

</ListBox>

Page 52: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team |WPF –Bài 3: Các điều khiển nâng cao trong WPF 9

</Expander>

</StackPanel>

</Window>

Như ta thấy việc sử dụng Expander trong WPF rất đơn giản, với cùng nguyên tắc như các điều khiển

UI cơ bản khác. Ở đây ta sử dụng 2 hộp mở rộng Expander: một chứa danh mục đồ uống được đặt trong một

ListBox; một chứa danh mục đồ ăn trong một ListBox. Kết quả của đoạn code được minh hoạ trong Hình

3.3.

a) b) c)

Hình 3.3 – Tạo lập và sử dụng hộp mở rộng bằng WPF: a) Hai danh mục cùng thu gọn; b) Danh

mục Đồ ăn được mở rộng; c) Cả hai danh mục được mở rộng (Danh mục trên đẩy danh mục dưới xuống)

Page 53: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team |WPF –Bài 3: Các điều khiển nâng cao trong WPF 10

4 Hộp soạn văn bản đa năng (RichTextBox)

Hộp soạn văn bản đa năng RichTextBox là một trong những điều khiển có chức năng phong

phú. Không chỉ cho phép soạn sửa và hiển thị các nội dung text đơn thuần, RichTextBox còn cho

phép thay đổi phông chữ (Verdana, Times New Roman,…), kiểu chữ (nghiêng, đậm, gạch chân),…

Đặc biệt, điều khiển RichTextBox trong WPF/.NET 3.0 còn cho phép kiểm tra/gợi ý sửa đổi lỗi

chính tả tiếng Anh của nội dung văn bản chứa trong đó. RichTextBox trong WPF/.NET 3.0 là phần

tử được cải tiến về cơ bản so với phiên bản trước của điều khiển RichTextBox trong .NET 2.0. Tuy

nhiên, cùng với sự mở rộng về chức năng là việc bổ sung các API mới cũng như những cách thức

sử dụng khác.

4.1 Chức năng cơ bản

Để thêm mới một hộp soạn thảo đa năng vào form, ta dùng mã XAML như sau:

<RichTextBox x:Name="XAMLRichBox" SpellCheck.IsEnabled="True"

MinHeight="100"></RichTextBox>

Thuộc tính x:Name là từ khoá xác định danh tính của RichTextBox được tạo. Thuộc tính này

đóng vai trò là tham chiếu cho phép ta sau này buộc mã lệnh C# vào điều khiển. Thuộc tính

MinHeight xác định số dòng có thể thấy được của hộp soạn thảo, giá trị này ngầm định bằng 1.

Thuộc tính SpellCheck.IsEnabled="True" kích hoạt tính năng kiểm tra lỗi chính tả tiếng

Anh trong nội dung văn bản và gợi ý những từ đúng có thể để thay thế, giống như Microsoft Word.

Tuy nhiên, nếu chỉ với một RichTextBox, ta không có cách nào để sửa đổi định dạng của

văn bản trong RichTextBox như đánh chữ nghiêng, chữ đậm, đổi phông chữ, vân vân. Muốn đạt

được điều này, ta phải buộc mã lệnh vào giao diện Command của RichTextBox.

4.2 Giao diện Command

Microsoft chủ trương để người phát triển làm việc với RichTextBox thông qua giao diện

Command. Mặc dù khái niệm này không mới đối với phần lớn người phát triển giao diện đồ hoạ

người dùng, việc cài đặt và cú pháp trong XAML có chút khác biệt.

Page 54: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team |WPF –Bài 3: Các điều khiển nâng cao trong WPF 11

Ta cần thêm một ToolBar và một số nút bấm hai trạng thái (ToggleButton) để gắn lệnh điều

khiển RichTextBox đã tạo. Thuộc tính Command trên mỗi điểu khiển kể trên sẽ xác định chức năng

mà ta muốn kích hoạt trên RichTextBox,. Trong khi đó, thuộc tính CommandTarget xác định

RichTextBox nào ta muốn chức năng kích hoạt của các nút bấm nhằm vào. Sau đây là đoạn mã

XAML bổ sung thêm một ToolBar và 3 nút bấm hai trạng thái:

<!--Khai báo sử dụng Toolbar-->

<ToolBar>

<!--Khai báo nút bấm kích hoạt lệnh tô đậm đoạn chữ được chọn

trong RichTextBox-->

<ToggleButton MinWidth="40"

Command="EditingCommands.ToggleBold"

CommandTarget="{Binding ElementName=XAMLRichBox}"

TextBlock.FontWeight="Bold">B</ToggleButton>

<!--Khai báo nút bấm kích hoạt lệnh in nghiêng đoạn chữ được chọn

trong RichTextBox-->

<ToggleButton MinWidth="40"

Command="EditingCommands.ToggleItalic"

CommandTarget="{Binding ElementName=XAMLRichBox}"

TextBlock.FontStyle="Italic">I</ToggleButton>

<!--Khai báo nút bấm kích hoạt lệnh gạch chân đoạn chữ được chọn

trong RichTextBox-->

<ToggleButton MinWidth="40"

Command="EditingCommands.ToggleUnderline"

CommandTarget="{Binding ElementName=XAMLRichBox}">

<TextBlock TextDecorations="Underline">U</TextBlock>

</ToggleButton>

</ToolBar>

Mặc dù đoạn mã ví dụ chỉ bao gồm một số ít các nút lệnh

(Command="EditingCommands.ToggleBold", Command="EditingCommands.ToggleBold",

Command="EditingCommands.ToggleItalic"), có tổng cộng 47 lệnh khác nhau mà ta có thể lựa

chọn (có thể xem chúng bằng cách khảo sát lớp EditingCommands).

Page 55: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team |WPF –Bài 3: Các điều khiển nâng cao trong WPF 12

Dưới đây là đoạn mã XAML đầy đủ cho phép ta xây dựng một hộp soạn thảo văn bản có thể thay đổi

được kiểu chữ (đậm, nghiêng, gạch chân):

<Window x:Class="Lesson3.Window3"

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

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

Title="Lesson3 - Rich Text Box" Height="300" Width="300"

>

<!--Sử dụng StackPanel làm layout chính-->

<StackPanel Orientation="Vertical">

<!--Khai báo toolbar-->

<ToolBar>

<!--Nút tô đậm-->

<ToggleButton MinWidth="40"

Command="EditingCommands.ToggleBold"

CommandTarget="{Binding ElementName=XAMLRichBox}"

TextBlock.FontWeight="Bold">B

</ToggleButton>

<!--Nút in nghiêng-->

<ToggleButton MinWidth="40"

Command="EditingCommands.ToggleItalic"

CommandTarget="{Binding ElementName=XAMLRichBox}"

TextBlock.FontStyle="Italic">I

</ToggleButton>

<!--Nút gạch chân-->

<ToggleButton MinWidth="40"

Command="EditingCommands.ToggleUnderline"

CommandTarget="{Binding ElementName=XAMLRichBox}">

<TextBlock

TextDecorations="Underline">U</TextBlock>

</ToggleButton>

</ToolBar>

<!--Khai báo tạo lập RichTextBox-->

Page 56: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team |WPF –Bài 3: Các điều khiển nâng cao trong WPF 13

<RichTextBox x:Name="XAMLRichBox" SpellCheck.IsEnabled="True"

MinHeight="100">

</RichTextBox>

</StackPanel>

</Window>

Kết quả được minh hoạ trong Hình 3.4.

Hình 3.4 – Xây dựng hộp soạn thảo đa năng đơn giản với các chức năng thay đổi kiểu chữ bằng

WPF

Câu hỏi ôn tập

1. Thuộc tính nào của một ComboBox cho phép khai báo nguồn dữ liệu?

A. SelectedIndex

B. ItemSource

C. Text

D. FontFamily

Trả lời: B

Page 57: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team |WPF –Bài 3: Các điều khiển nâng cao trong WPF 14

2. Trong hộp chọn phông chữ, danh sách phông chữ hệ thống được lấy từ

lớp nào?

A. Fonts.SystemFontFamilies

B. Fonts.SystemTypefaces

C. FontFamilyMapCollection

Trả lời: A

3. Trong ví dụ về danh mục ảnh ở mục 3, phát triển thêm tính năng mới bằng

việc bổ sung các check box vào đầu mỗi mục đồ uống. Một nút bấm có trách nhiệm hiển

thị những đồ uống mà người dùng đã chọn bằng việc đánh dấu checkbox (chú ý có thể

nhiều hơn 1 lựa chọn). Kết quả như trong hình minh hoạ dưới đây:

Gợi ý:

- Thêm điều khiển CheckBox vào mỗi chỉ mục con của ListBox đang dùng. Với mỗi

điều khiển CheckBox, thêm hàm xử lý sự kiện Uncheck và Check. Viết mã C# các hàm tương

ứng trong file code-behind của form chứa.

- Thêm điều khiển Button vào sau ListBox (ví dụ, dùng StackPanel). Thêm hàm xử lý

sự kiện Click của Button.

Page 58: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team |WPF –Bài 3: Các điều khiển nâng cao trong WPF 15

Sau đây là mã ví dụ:

Phần mã XAML:

<Window x:Class="Lesson3.Window4"

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

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

Title="Lesson3 - Cau hoi on tap 3" Height="400" Width="300"

>

<StackPanel>

<!--Khai báo tạo lập một hộp danh mục với các thuộc tính về căn lề,

chiều rộng, tên gọi,...-->

<ListBox Margin="10,10,0,13" Width="280" Name="listBox1"

HorizontalAlignment="Left"

VerticalAlignment="Top">

<!--Khai báo tạo lập một chỉ mục con trong hộp danh mục với thuộc tính

màu nền-->

<ListBoxItem Background="Beige">

<!--Lồng vào chỉ mục này một StackPanel để có thể chứa nhiều hơn 1

phần tử UI con theo chiều ngang-->

<StackPanel Orientation="Horizontal">

<CheckBox x:Name="chkOrangeJuice" Checked="HandleChecked"

Unchecked="HandleUnchecked"></CheckBox>

<!--Khai báo tạo lập một ảnh đồ uống ở đầu mỗi chỉ mục-->

<Image Width="50" Height="50" Source="orange_juice.jpg"></Image>

<!--Khai báo tạo lập một dòng chữ chỉ tên đồ uống-->

<TextBlock Margin="5" VerticalAlignment="Center" FontFamily="Times

New Roman" FontStyle="Italic" FontSize="18">Nước cam tươi</TextBlock>

</StackPanel>

</ListBoxItem>

<!--Khai báo chỉ mục 2 tương tự như trên-->

<ListBoxItem>

<StackPanel Orientation="Horizontal">

<CheckBox x:Name="chkKiwiJuice" Checked="HandleChecked"

Unchecked="HandleUnchecked"></CheckBox>

<Image Width="50" Height="50" Source="kiwi_juice.jpg"></Image>

Page 59: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team |WPF –Bài 3: Các điều khiển nâng cao trong WPF 16

<TextBlock Margin="5" VerticalAlignment="Center" FontFamily="Times

New Roman" FontStyle="Italic" FontSize="18">Nước kiwi ép</TextBlock>

</StackPanel>

</ListBoxItem>

<!--Khai báo chỉ mục 3 tương tự như trên-->

<ListBoxItem Background="Beige">

<StackPanel Orientation="Horizontal">

<CheckBox x:Name="chkMangoJuice" Checked="HandleChecked"

Unchecked="HandleUnchecked"></CheckBox>

<Image Width="50" Height="50" Source="mango_juice.jpg"></Image>

<TextBlock Margin="5" VerticalAlignment="Center" FontFamily="Times

New Roman" FontStyle="Italic" FontSize="18">Nước soài ép</TextBlock>

</StackPanel>

</ListBoxItem>

<!--Khai báo chỉ mục 4 tương tự như trên-->

<ListBoxItem>

<StackPanel Orientation="Horizontal">

<CheckBox x:Name="chkMilk" Checked="HandleChecked"

Unchecked="HandleUnchecked"></CheckBox>

<Image Width="50" Height="50" Source="milk.jpg"></Image>

<TextBlock Margin="5" VerticalAlignment="Center" FontFamily="Times

New Roman" FontStyle="Italic" FontSize="18">Sữa tươi tiệt trùng</TextBlock>

</StackPanel>

</ListBoxItem>

<!--Khai báo chỉ mục 5 tương tự như trên-->

<ListBoxItem Background="Beige">

<StackPanel Orientation="Horizontal">

<CheckBox x:Name="chkCafe" Checked="HandleChecked"

Unchecked="HandleUnchecked"></CheckBox>

<Image Width="50" Height="50" Source="coffee.jpg"></Image>

<TextBlock Margin="5" VerticalAlignment="Center" FontFamily="Times

New Roman" FontStyle="Italic" FontSize="18">Cà phê Espresso</TextBlock>

</StackPanel>

</ListBoxItem>

</ListBox>

Page 60: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team |WPF –Bài 3: Các điều khiển nâng cao trong WPF 17

<!--Khai báo nút bấm kích hoạt hiển thị đồ uống được chọn -->

<Button Click="DislayCustomerChoices" Width="70">Gọi đồ uống</Button>

</StackPanel>

</Window>

Phần mã C#:

public partial class Window4 : System.Windows.Window

{

//Đặt các cờ xác định lựa chọn tương ứng với các loại đồ uống

bool selectedOrange;

bool selectedKiwi;

bool selectedMango;

bool selectedMilk;

bool selectedEspesso;

public Window4()

{

InitializeComponent();

//

//Khởi tạo biến đánh dấu chọn

selectedOrange = false;

selectedKiwi = false;

selectedMango = false;

selectedMilk = false;

selectedEspesso = false;

}

//Hàm xử lý sự kiện bỏ chọn (Unchecked) của mỗi checkbox

//Lưu ý: Ở đây ta chỉ sử dụng một hàm duy nhất xử lý sự kiện này

cho mọi checkbox

//Để phân biệt checkbox nào phát động sự kiện, ta dựa vào tham số

sender và so sánh nó với các checkbox

private void HandleUnchecked(object sender, RoutedEventArgs e)

{

if (sender.Equals(chkCafe))selectedEspesso = false;

if (sender.Equals(chkKiwiJuice)) selectedKiwi = false;

Page 61: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team |WPF –Bài 3: Các điều khiển nâng cao trong WPF 18

if (sender.Equals(chkMangoJuice)) selectedMango = false;

if (sender.Equals(chkMilk)) selectedMilk = false;

if (sender.Equals(chkOrangeJuice)) selectedOrange = false;

}

//Hàm xử lý sự kiện bỏ chọn (checked) của mỗi checkbox (tương tự

như trên)

private void HandleChecked(object sender, RoutedEventArgs e)

{

if (sender.Equals(chkCafe)) selectedEspesso = true;

if (sender.Equals(chkKiwiJuice)) selectedKiwi = true;

if (sender.Equals(chkMangoJuice)) selectedMango = true;

if (sender.Equals(chkMilk)) selectedMilk = true;

if (sender.Equals(chkOrangeJuice)) selectedOrange = true;

}

//Hàm xử lý sự kiện hiển thị các đồ uống được chọn

private void DislayCustomerChoices(object sender, RoutedEventArgs

e)

{

String choices = "Ban da chon ";

bool selected = false;

//

if (selectedOrange)

{

choices += "Nuoc cam; ";

selected = true;

}

//

if (selectedMilk)

{

choices += "Sua tuoi; ";

selected = true;

}

//

if (selectedMango)

{

Page 62: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team |WPF –Bài 3: Các điều khiển nâng cao trong WPF 19

choices += "Nuoc soai ep; ";

selected = true;

}

//

if (selectedEspesso)

{

choices += "Cafe Espresso;";

selected = true;

}

//

if (!selected) choices = "Ban chua chon do uong nao";

//

MessageBox.Show(choices);

}

}

4. Trong điều khiển Expander, ta thường tạo dòng text mô tả nội dung bên trong của

Expander, luôn xuất hiện trên Expander bên cạnh mũi tên chỉ trạng thái của Expander. Muốn

thiết lập nội dung của dòng text này, ta dùng thuộc tính gì của điều khiển Expander?

A. Content

B. Text

C. Header

D. Source

Trả lời: C

5. Trong ToolBar kết hợp với RichTextBox, thuộc tính nào của nút bấm hai trạng thái

ToggleButton xác định chức năng sửa đổi văn bản cần kích hoạt?

A. Command

B. CommandTarget

C. Cả hai thuộc tính trên

Trả lời: A

6. Trong ToolBar kết hợp với RichTextBox, thuộc tính nào của nút bấm hai trạng thái

ToggleButton xác định đối tượng RichTextBox có chức năng sửa đổi văn bản cần kích hoạt?

Page 63: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team |WPF –Bài 3: Các điều khiển nâng cao trong WPF 20

A. Command

B. CommandTarget

C. Cả hai thuộc tính trên

Trả lời: B

Tài liệu tham khảo

1.Creating ImageListBox in WPF, URL:

http://windowsclient.net/blogs/ilves/archive/2008/02/14/creating-imagelistbox-in-wpf.aspx

2.WPF ListBox Tutorial, URL:

http://www.longhorncorner.com/UploadFile/lheditor/WPfLB08262008103013AM/WPfLB.aspx

3. WPF Sample Series - Expander Control With Popup Content. URL:

http://karlshifflett.wordpress.com/2008/02/05/wpf-sample-series-expander-control-with-popup-content/

4. WPF RichTextBox, URL: http://www.c-

sharpcorner.com/UploadFile/mahesh/WPFRichTextBox09072008194855PM/WPFRichTextBox.aspx

5. Mastering the WPF RichTextBox, URL: http://www.devx.com/dotnet/Article/34644

Page 64: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team |WPF – Bài 4: Thực đơn (Menu) và thanh công cụ

(Toolbar) - WPF

1

Bài 4

THỰC ĐƠN (MENU) VÀ THANH CÔNG CỤ (TOOLBAR) -

WPF

Thực đơn (Menu) và thanh công cụ (Toolbar) là một trong những thành phần quan trọng của cửa

sổ, chúng chứa đựng các chức năng chính của chương trình mà người dùng có thể thực hiện. Thanh thực

đơn chứa hầu hết tất cả chức năng chính của chương trình, tổ chức theo dạng phân cấp, trong khi thanh

công cụ thường chứa một số chức năng thiết yếu mà người dùng hay quan tâm dưới dạng các biểu tượng

hình ảnh để người dùng có thể thao tác một cách nhanh chóng. Hình 4.1 minh họa cửa sổ chương trình

với thanh thực đơn và thanh công cụ.

Bài này giới thiệu phương pháp xây dựng và sử dụng Thực đơn và Thanh công cụ bằng ngôn ngữ

XAML trong ứng dụng WPF từ những ví dụ đơn giản là các mục menu (Menu Item) thông thường

(không có biểu tượng và trạng thái) đến những ví Menu Item với biểu tượng hình ảnh (Icon) và các trạng

thái (Checked và UnChecked).

1. Xây dựng thực đơn và sử dụng thực đơn

Thực đơn (Menu) là điều khiển gồm nhiều phần tử được tổ chức dưới dạng phân cấp. Thanh

thực đơn thường nằm trên đỉnh cửa số (dưới thanh tiêu đề). Các phẩn tử thực đơn (Menu Item) xuất hiện

Thanh công cụ

với các biểu

tượng bằng

hình ảnh

Thực đơn

Hình 4.1. Một ví dụ về cửa số với thực đơn và thanh công cụ

Page 65: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team |WPF – Bài 4: Thực đơn (Menu) và thanh công cụ

(Toolbar) - WPF

2

trên thanh thực đơn còn được gọi là Menu Item mức đỉnh. Mỗi Menu Item mức đỉnh có thể chứa nhiều

Menu Item cấp dưới (Sub Menu) hoặc được gắn trực tiếp với các bộ quản lý sự kiện (Event handler) như

sự kiện Click hay các lệnh của hệ thống được xây dựng sẵn (như Copy, Cut, Paste,..). Tương tự như vậy,

mỗi Menu Item cấp dưới lại có thể chứa nhiều Menu Item cấp dưới của chính nó.

Khi một Menu Item chứa các Menu Item cấp dưới thì thường được gọi là Popup Menu, các

Menu Item cấp dưới sẽ xuất hiện khi người dùng nhấn chuột lên Popup Menu. Nếu Menu Item được gắn

trực tiếp với với bộ quản lý sự kiện hay một lệnh của hệ thống thì được gọi là Command Menu, nó sẽ

thực thi một câu lệnh mong muốn khi người dùng nhấn chuột hoặc nhấn phím tắt (ký tự trên bàn phím

gắn với Menu Item) để chọn nó.

Ta sẽ tìm hiểu từng bước xây dựng và sử dụng menu bắt đầu từ Menu với các Menu Item đơn

giản, tiếp đến là các Menu Item có trạng thái (Checked, UnChecked) và Menu Item có biểu tượng hình

ảnh.

1.1 Xây dựng thực đơn với các Menu Item đơn giản

Trong phần này ta tìm hiểu từng bước xây dựng một thanh menu đơn giản gồm 3 Menu Item

mức đỉnh là Thực đơn 1, Thực đơn 2 và Thực đơn 3. Trong đó, Thực đơn 1 và Thực đơn 2 là các Menu

Popup (có chứa các menu con), Thực đơn 3 là loại Command Menu Item, nó không chứa Menu con mà

sẽ thực thi một câu lệnh khi ta nhấn chuột vào nó như minh họa ở Hình 4.2.

Hình 4.3 minh họa Thực đơn 2 có hai Menu Item cấp dưới là Thực đơn 21 và Thực đơn 22.

Trong đó, Thực đơn 21 là Popup Menu chứa hai thực đơn cấp dưới của nó là Thực đơn 211 và Thực đơn

212, Thực đơn 22 là Command Menu Item.

Thực đơn 1 là một

Popup Menu

Các menu

Item mức đỉnh

Hình 4.2. Một ví dụ về cửa số với thanh thực đơn

Thực đơn 3 là

một Command

Menu

Page 66: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team |WPF – Bài 4: Thực đơn (Menu) và thanh công cụ

(Toolbar) - WPF

3

Ta có thể tạo menu bằng công cụ trực quan hoặc gõ trực tiếp mã lệnh XAML.

Tạo Menu bằng công cụ trực quan.

Xem hình 4.4 minh họa công cụ tạo Menu cho ứng dụng WPF bằng Visual Studio 2008. Chọn

menu từ ToolBox, sau đó Nhấn chuột lên cửa sổ để tạo Menu mới. Tiếp đến nhấn nút

trên thanh Properties của Menu để mở hộp thoại quản lý các Menu Item như hình 4.5.

Thực đơn 22 là một

Command Menu

Hình 4.3. Ví dụ về thực đơn cấp dưới là một Popup Menu

Thực đơn 21 là

một Popup Menu

Thực đơn 2 là một

Popup Menu

Page 67: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team |WPF – Bài 4: Thực đơn (Menu) và thanh công cụ

(Toolbar) - WPF

4

Hình 4.5 là hộp thoại quản lý các Menu Item, nhấn nút để thêm Menu Item và nhập tiêu

đề cho Menu Item ở mục Header. Nếu muốn thêm các Menu Item cấp dưới của Menu Item hiện tại, nhấn

nút .

Hình 4.4 Công cụ soạn thảo Menu cho ứng dụng WPF trong Visual Studio 2008

Tiêu đề của Menu

Chọn mục Menu từ

Toolbox

Thêm các Menu Item

cấp dưới

Page 68: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team |WPF – Bài 4: Thực đơn (Menu) và thanh công cụ

(Toolbar) - WPF

5

Sử dụng mã XAML để tạo thực đơn

Đoạn mã trình Menu trên bằng XAML:

<Grid>

<Menu Height="22" Name="menu1" VerticalAlignment="Top" >

<MenuItem Header="Thực đơn _1" Name="Menu1">

<MenuItem Header="_Copy" Command="ApplicationCommands.Copy"

ToolTip="Copy văn bản"/>

<MenuItem Header="_Cut" Command="ApplicationCommands.Cut"

ToolTip="Cắt văn bản"/>

<MenuItem Header="_Paste"

Command="ApplicationCommands.Paste" ToolTip="Dán văn bản"/>

</MenuItem>

<MenuItem Header="Thực đơn _2" Name="Menu2">

<MenuItem Header="Thực đơn 21">

<MenuItem Header="Thực đơn 211"

Click="MenuItem211_Click" />

<MenuItem Header="Thực đơn 212"

Click="MenuItem212_Click" />

</MenuItem>

<MenuItem Header="Thực đơn 22" Click="MenuItem22_Click" />

</MenuItem>

Tiêu đề của Menu Item

Hình 4.5. Hộp thoại Quản lý các Menu Item

Thêm Menu Item

cấp dưới

Thêm một Menu

Item mới

Page 69: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team |WPF – Bài 4: Thực đơn (Menu) và thanh công cụ

(Toolbar) - WPF

6

<MenuItem Header="Thực đơn _3" Click="MenuItem3_Click"

Name="Menu3" />

</Menu>

</Grid>

Thanh Menu được bắt đầu bằng thẻ <Menu> và kết thúc bằng thẻ đóng </Menu> . Có nhiều

thuộc tính của thẻ này, trong ví dụ trên thì

Height="22" : Chiều cao menu là 22 pixel.

Name="menu1" : Tên của menu là menu1. Tên menu được mã trình C# sử dụng để quản lý nó.

VerticalAlignment="Top" : Menu được căn để nằm bên trên của Grid chứa nó.

Các Popup Menu được tạo bởi thẻ <MenuItem> và kết thúc bằng thẻ đóng </MenuItem> .

Giữa cặp thẻ này là các thẻ <MenuItem> khác để tạo nên các Menu Item cấp dưới của nó.

Các Command Menu thì được tạo bởi thẻ <MenuItem/>, không có thẻ đóng. Một số thuộc tính

cơ bản của Menu Item bao gồm

Header="Thực đơn _1": Tiêu đề hay nhãn của Menu Item. Dấu gạch dưới đặt trước ký tự sẽ

được sử dụng làm phím tắt khi kết hợp với phím Alt để gọi Menu Item bằng bàn phím. Trong ví dụ này

thì ký tự 1 được dùng làm phím tắt cho Menu Item “Thực đơn 1”, ký tự được dùng làm phím tắt sẽ được

hiển thị với dấu gạch chân khi người dùng nhấn phím Alt để mở Menu.

Name="Menu1": Tên của Menu Item, cần thiết cho mã trình C# có thể can thiệp vào Menu Item.

ToolTip="Cắt văn bản": Lời chú thích cho Menu Item khi di chuột qua.

Đối với các Command Menu, có hai cơ chế thực thi lệnh khi chọn Menu.

Nếu muốn gắn Command Menu với các lệnh có sẵn của hệ thống như: Copy, Cut, Paste,.. thì ta

sử dụng thuộc tính Command của Menu Item. Ví dụ, lệnh <MenuItem Header="_Copy"

Command="ApplicationCommands.Copy"/> làm cho Menu Item Copy này sẽ thực hiện công việc

copy dòng văn bản đang được chọn trong cửa sổ vào bộ nhớ đệm. Chú ý, các lệnh của hệ thống bắt đầu

bằng ApplicationCommands.

Nếu muốn gắn Command Menu với các hàm xử lý sự kiện tự định nghĩa thì sử dụng thuộc tính

Click của Menu Item. Ví dụ, <MenuItem Header="Thực đơn 211"

Click="MenuItem211_Click" /> để yêu cầu khi chọn "Thực đơn 211" thì sẽ gọi hàm

MenuItem211_Click.

Dưới đây là đoạn mã trình C# khai báo các hàm xử lý sự kiện khi nhấn vào các Menu khai báo

bằng XAML trên. Khi các menu được chọn thì các hàm tương ứng được khai báo trong thuộc tính Click

sẽ được gọi và làm nhiệm vụ đơn giản làm hiển thị hộp thông báo.

Page 70: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team |WPF – Bài 4: Thực đơn (Menu) và thanh công cụ

(Toolbar) - WPF

7

namespace WpfApplication1

{

/// <summary>

/// Interaction logic for Window1.xaml

/// </summary>

public partial class Window1 : Window

{

public Window1()

{

InitializeComponent();

}

//Hàm xử lý sự kiện nhấn Menu "Thực đơn 211"

private void MenuItem211_Click(object sender, RoutedEventArgs

e)

{

MessageBox.Show("Bạn chọn Menu 211");

}

//Hàm xử lý sự kiện nhấn Menu "Thực đơn 212"

private void MenuItem212_Click(object sender, RoutedEventArgs

e)

{

MessageBox.Show("Bạn chọn Menu 212");

}

//Hàm xử lý sự kiện nhấn Menu "Thực đơn 22"

private void MenuItem22_Click(object sender, RoutedEventArgs e)

{

MessageBox.Show("Bạn chọn Menu 22");

}

//Hàm xử lý sự kiện nhấn Menu "Thực đơn 3"

private void MenuItem3_Click(object sender, RoutedEventArgs e)

{

MessageBox.Show(" Bạn chọn Menu 3");

}

}

}

Page 71: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team |WPF – Bài 4: Thực đơn (Menu) và thanh công cụ

(Toolbar) - WPF

8

1.2 Menu Item với trạng thái Checked và Unchecked

Khi làm việc với Menu, đôi khi ta có những chức năng với đặc thù có hai trạng thái On/Off. Ví

dụ như chương cần có một Menu Item để làm cho một Textbox hiển thị ở dạng chữ đậm và chữ thường,

người dùng mong muốn Menu Item thể hiện được trạng thái On/Off tương ứng với kiểu chữ (chữ

đậm/chữ thường) trên Textbox. Điều khiển Menu của WPF cung cấp cho chúng ta loại Menu Item với

hai trạng thái Checked và UnChecked.

Để tạo ra Menu Item có trạng thái, ta sử dụng thuộc tính IsCheckable="True" của Menu

Item.

Mã lệnh tạo "Thực đơn 23" như sau:

<MenuItem Header="Thực đơn _2" Name="Menu2">

<MenuItem Header="Thực đơn 21">

<MenuItem Header="Thực đơn 211" Click="MenuItem211_Click" />

<MenuItem Header="Thực đơn 212" Click="MenuItem212_Click" />

</MenuItem>

<MenuItem Header="Thực đơn 22" Click="MenuItem22_Click" />

<!--Thực đơn có trạng thái Checked và UnChecked-->

<MenuItem Header="Thực đơn 23" IsCheckable="True"

Checked="Menu23_Checked" Unchecked="Menu23_Unchecked"/>

</MenuItem>

Đối với Menu Item có trạng thái, mỗi khi Menu được chọn sẽ phát sinh một trong hai sự kiện

Checked và Uncheked tương ứng. Ta sử dụng các thuộc tính Checked và Unchecked để gắn các hàm

xử lý sự kiện cần được thực thi. Ở ví dụ trên, hàm Menu23_Checked sẽ được gọi khi Menu "Thực đơn

Hình 4.6. Minh họa Menu Item với trạng thái Checked và UnChecked

Khi thực đơn không được chọn

thì có biểu tượng

Khi thực đơn không được chọn thì

không có biểu tượng

Page 72: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team |WPF – Bài 4: Thực đơn (Menu) và thanh công cụ

(Toolbar) - WPF

9

23" được đánh dấu, và hàm Menu23_Unchecked được gọi khi Menu "Thực đơn 23" được bỏ dấu

chọn.

Mã nguồn minh họa của hai hàm trên như sau:

public partial class Window1 : Window

{

public Window1()

{

InitializeComponent();

}

//...........................................................

//Hàm xử lý sự kiện khi Menu "Thực đơn 23" được đánh dấu chọn

private void Menu23_Checked(object sender, RoutedEventArgs e)

{

//Thiết lập thuộc tính chữ đậm cho textBox1

textBox1.FontWeight = FontWeights.Bold;

}

//Hàm xử lý sự kiện khi Menu "Thực đơn 23" được bỏ dấu chọn

private void Menu23_Unchecked(object sender, RoutedEventArgs e)

{

//Thiết lập thuộc tính chữ thường cho textBox1

textBox1.FontWeight = FontWeights.Normal;

}

}

1.3 Menu Item với biểu tượng hình ảnh

Với các ứng dụng xây dựng trên nền .Net 2.0, công việc xây dựng Thực đơn và Thanh công cụ

với biểu tượng hình là khá đơn giản, có thể sử dụng công cụ thiết kế giao diện trực quan. Ví dụ, muốn tạo

menu có biểu tượng hình ảnh (icon), chúng ta thêm Menu Trip vào form, sau đó thêm các mục cho thực

đơn (Menu Item). Nhấn chuột phải lên từng mục và chọn “Set Image” là thành công. Tuy nhiên, khi xây

dựng ứng dụng WPF, không có mục chọn “Set Image” khi nhấn chuột phải vào một mục trong thực đơn.

Chúng ta phải nạp các tệp hình ảnh, biểu tượng và tài nguyên (Resource) của ứng dụng và viết một số

lệnh XAML để gắn biểu tượng cho Menu Item.

Nạp các tệp hình ảnh, biểu tượng vào tài nguyên của ứng dụng.

i. Trên thanh thực đơn Visual Studio, chọn Project → Properties sẽ hiện

ra bảng cài đặt các thông số cài đặt cho ứng dụng.

Page 73: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team |WPF – Bài 4: Thực đơn (Menu) và thanh công cụ

(Toolbar) - WPF

10

ii. Chọn mục resources.

iii. Trong mục Add Resource chọn Add Existing File nếu đã có sẵn File

biểu tượng hình ảnh trên máy hoặc chọn New Images hay Add New Icon tùy ý.

iv. Chú ý, sau khi thêm được các File hình ảnh biểu tượng vào tài nguyên,

để các điều khiển trên cửa sổ như Menu, Toolbar sử dụng được chúng, ta phải thiết lập

thuộc tính 'Build Action : Resource' và 'Copy to Output Directory : Do not copy'.

Viết mã lệnh XAML gắn biểu tượng cho các Menu Item.

<StackPanel>

<!--Khai bao Tài nguyên cần sử dụng-->

<StackPanel.Resources>

<Image x:Key="copy" Source="Resources/Copy.png" Width="16"

Height="16"></Image>

<Image x:Key="cut" Source="Resources/Cut.png" Width="16"

Height="16"></Image>

<Image x:Key="paste" Source="Resources/Paste.png" Width="16"

Height="16"></Image>

</StackPanel.Resources>

<Menu Height="22" Name="menu1" VerticalAlignment="Top" >

<MenuItem Header="Thực đơn _1" Name="Menu1">

<!—Ba Menu Copy, Cut, Paste được gắn biểu tượng-->

<MenuItem Header="_Copy" Command="ApplicationCommands.Copy"

ToolTip="Copy văn bản" Icon="{StaticResource copy}" />

Thiết lập thuộc

tính cho tài

nguyên hình ảnh

Hình 4.7. Thêm tài nguyên và thiết lập thuộc tính cho tài nguyên ảnh

Page 74: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team |WPF – Bài 4: Thực đơn (Menu) và thanh công cụ

(Toolbar) - WPF

11

<MenuItem Header="_Cut" Command="ApplicationCommands.Cut"

ToolTip="Cắt văn bản" Icon="{StaticResource cut}" />

<MenuItem Header="_Paste" Command="ApplicationCommands.Paste"

ToolTip="Dán văn bản" Icon="{StaticResource paste}" />

</MenuItem>

<MenuItem Header="Thực đơn _2" Name="Menu2">

<MenuItem Header="Thực đơn 21">

<MenuItem Header="Thực đơn 211" Click="MenuItem211_Click" />

<MenuItem Header="Thực đơn 212" Click="MenuItem212_Click" />

</MenuItem>

<MenuItem Header="Thực đơn 22" Click="MenuItem22_Click" />

<!--Thực đơn có trạng thái Checked và UnChecked-->

<MenuItem Header="Thực đơn 23" IsCheckable="True"

Checked="Menu23_Checked" Unchecked="Menu23_Unchecked"/>

</MenuItem>

<MenuItem Header="Thực đơn _3" Click="MenuItem3_Click" Name="Menu3" />

</Menu>

<TextBox Name="textBox1" TextWrapping="Wrap" Margin="2">

Chú cào cào nhỏ, ngồi trong đám cỏ.

</TextBox>

</StackPanel>

Như vậy, chúng ta đã xây dựng thành công thanh menu với các biểu tượng đẹp mắt hay menu với

trạng thái Checked/UnChecked cũng như biết cách gắn các hàm xử lý sự kiện cho các menu. Phần tiếp

theo ta sẽ tìm hiểu về thanh công cụ.

2. Xây dựng và sử dụng thanh công cụ (Toolbar)

Thanh công cụ (Toolbar) là thanh chứa các chức năng dưới dạng các dãy hình ảnh biểu tượng,

mỗi biểu tượng gắn với một mục chức năng cụ thể. Thông thường các Toolbar chứa những chức năng

thiết yếu mà người dùng hay quan tâm nhất, bởi vì thanh Toolbar có ưu điểm là dễ dàng thao tác. Một

cửa số có thể có một hoặc nhiều thanh Toolbar. Trong phần này ta tìm hiểu phương pháp xây dựng thanh

Toolbar với các nút chức năng thông thường và các nút chức năng có trạng thái (Checked/UnChecked).

Page 75: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team |WPF – Bài 4: Thực đơn (Menu) và thanh công cụ

(Toolbar) - WPF

12

2.1. Nút công cụ thông thường

Chúng ta bắt đầu tìm hiểu các bước xây dựng thanh Toolbar với các nút bấm đơn giản như ví dụ

minh họa ở hình 4.9. Thanh công cụ bao gồm năm nút: Copy , Cut, Paste thực hiện các chức năng có sẵn

của hệ thống, Nút A và a gắn với hàm xử lý sự kiện tự xây dựng, làm nhiệm vụ tăng/giảm cỡ chữ của

Textbox bên dưới.

Thanh công cụ được xây dựng bằng đoạn mã XAML sau:

<StackPanel>

<ToolBar Height="26" Name="toolBar1" Width="280"

HorizontalAlignment="Left" >

<Button Height="23" Name="button1" Width="23"

Command="ApplicationCommands.Copy" ToolTip="Copy văn bản">

<Image Source="Resources/Copy.png" Width="16" Height="16"

HorizontalAlignment="Left" />

</Button>

Copy

Hình 4.9. Ví dụ minh họa Toolbar

Cut

Paste

Tăng cỡ chữ

Giảm cỡ chữ

Toolbar nằm ngang Toolbar nằm dọc

Hình 4.8.Thanh công cụ - Toolbar

Page 76: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team |WPF – Bài 4: Thực đơn (Menu) và thanh công cụ

(Toolbar) - WPF

13

<Button Height="23" Name="button2" Width="23"

Command="ApplicationCommands.Cut" ToolTip="Cắt văn bản">

<Image Source="Resources/Cut.png" Width="16" Height="16"

HorizontalAlignment="Left" />

</Button>

<Button Height="23" Name="button3" Width="23"

Command="ApplicationCommands.Paste" ToolTip="Dán văn bản">

<Image Source="Resources/Paste.png" Width="16" Height="16"

HorizontalAlignment="Left" />

</Button>

<Separator/>

<Button Height="23" Name="button4" ToolTip="Tăng cỡ chữ" Width="23"

Click="IncreaseFont_Click">

<Image Source="Resources/inc.ico" Width="16" Height="16"

HorizontalAlignment="Left" />

</Button>

<Button Height="23" Name="button5" ToolTip="Giảm cỡ chữ"

Width="23" Click="DecreaseFont_Click">

<Image Source="Resources/Dec.ico" Width="16" Height="16"

HorizontalAlignment="Left" />

</Button>

</ToolBar>

</StackPanel>

Mã XAML tạo thanh công cụ được được bắt đầu bằng thẻ <ToolBar> và kết thúc bằng thẻ đóng

</ToolBar>.

Các nút lệnh (Button) của thanh công cụ được tạo bởi thẻ <Button> và kết thúc bằng thẻ đóng

</Button> .

Name=" button1": Tên của Button, cần thiết cho mã trình C# có thể can thiệp vào Button.

ToolTip="Copy văn bản ": Lời chú thích cho Button khi di chuột qua.

Có hai cơ chế thực thi lệnh khi chọn nút lệnh trong Toolbar.

Nếu muốn gắn nút lệnh với các lệnh có sẵn của hệ thống như: Copy, Cut, Paste,.. thì ta sử dụng

thuộc tính Command của Button. Ví dụ, lệnh <Button Height="23" Name="button1"

Width="23" Command="ApplicationCommands.Copy" ToolTip="Copy văn bản"> làm cho

Page 77: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team |WPF – Bài 4: Thực đơn (Menu) và thanh công cụ

(Toolbar) - WPF

14

nút lệnh Copy này sẽ thực hiện công việc copy dòng văn bản đang được chọn trong cửa sổ vào bộ nhớ

đệm. Chú ý, các lệnh của hệ thống bắt đầu bằng ApplicationCommands.

Nếu muốn gắn nút lệnh với các hàm xử lý sự kiện tự định nghĩa thì sử dụng thuộc tính Click

của Button. Ví dụ, <Button Height="23" Name="button4" ToolTip="Tăng cỡ chữ"

Width="23" Click="IncreaseFont_Click"> để yêu cầu khi chọn "button4" thì sẽ gọi hàm

IncreaseFont_Click.

Giữa cặp thẻ <Button> và </Button> là thẻ <Image Source="Resources/Copy.png"

Width="16" Height="16" HorizontalAlignment="Left" /> để định nghĩa hình ảnh biểu

tượng của nút bấm.

Thẻ <Separator/> dùng để tạo ra vạch phân cách giữa cách nút bấm.

Mã nguồn minh họa của hai hàm xử lý sự kiện nhấn nút button4và button5như sau:

public partial class Window1 : Window

{

public Window1()

{

InitializeComponent();

}

//...........................................................

//Hàm xử lý sự kiện khi button4 được nhấn

private void IncreaseFont_Click(object sender, RoutedEventArgs

e)

{

if (textBox1.FontSize < 18)

{

textBox1.FontSize += 2;//Tăng cỡ font chữ của textBox1

lên 2 đơn vị

}

}

//Hàm xử lý sự kiện khi button5 được nhấn

private void DecreaseFont_Click(object sender, RoutedEventArgs

e)

{

if (textBox1.FontSize > 10)

{

textBox1.FontSize -= 2;//Giảm cỡ font chữ của textBox1

lên 2 đơn vị

Page 78: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team |WPF – Bài 4: Thực đơn (Menu) và thanh công cụ

(Toolbar) - WPF

15

}

}

}

2.2. Nút công cụ có trạng thái

Ngoài các nút bấm thông thường, thanh công cụ còn cho phép tạo ra các nút bấm có trạng thái,

khi ở trang thái được chọn (Checked) thì sẽ có màu nền khác và có đường viền để người dùng có thể

nhận biết được trạng thái của nút đó (xem minh họa hình 4.10).

Thanh công cụ với các nút có trạng thái được xây dựng bằng đoạn mã XAML sau:

<StackPanel>

<ToolBar Height="26" Name="toolBar1" Width="280"

HorizontalAlignment="Left" >

<Button Height="23" Name="button1" Width="23"

Command="ApplicationCommands.Copy" ToolTip="Copy văn bản">

<Image Source="Resources/Copy.png" Width="16" Height="16"

HorizontalAlignment="Left" />

</Button>

<Button Height="23" Name="button2" Width="23"

Command="ApplicationCommands.Cut" ToolTip="Cắt văn bản">

<Image Source="Resources/Cut.png" Width="16" Height="16"

HorizontalAlignment="Left" />

</Button>

Hình 4.10. Ví dụ minh họa Toolbar với trạng thái Checked và UnCheked

Nút B đang ở trạng

thái Checked

Nút I đang ở trạng

thái UnChecked

Page 79: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team |WPF – Bài 4: Thực đơn (Menu) và thanh công cụ

(Toolbar) - WPF

16

<Button Height="23" Name="button3" Width="23"

Command="ApplicationCommands.Paste" ToolTip="Dán văn bản">

<Image Source="Resources/Paste.png" Width="16" Height="16"

HorizontalAlignment="Left" />

</Button>

<Separator/>

<Button Height="23" Name="button4" ToolTip="Tăng cỡ chữ" Width="23"

Click="IncreaseFont_Click">

<Image Source="Resources/inc.ico" Width="16" Height="16"

HorizontalAlignment="Left" />

</Button>

<Button Height="23" Name="button5" ToolTip="Giảm cỡ chữ"

Width="23" Click="DecreaseFont_Click">

<Image Source="Resources/Dec.ico" Width="16" Height="16"

HorizontalAlignment="Left" />

</Button>

<Separator/>

<!--Nút bấm với trạng thái Checked và UnChecked-->

<CheckBox Name="check1" ToolTip="Chữ đậm" Checked="Bold_Checked"

Unchecked="Bold_Unchecked">

<Image Source="Resources/bold.png" Width="16" Height="16"

HorizontalAlignment="Left" />

</CheckBox>

<CheckBox Name="check2" ToolTip="Chữ nghiêng"

Checked="Italic_Checked" Unchecked="Italic_Unchecked">

<Image Source="Resources/Italic.png" Width="16" Height="16"

HorizontalAlignment="Left" />

</CheckBox>

</ToolBar>

</StackPanel>

Khác với các nút lệnh thông thường được tạo bởi thẻ <Button> và kết thúc bằng thẻ đóng

</Button>, các nút lệnh có trạng thái được tạo nên bởi thẻ <CheckBox Name="check1"

ToolTip="Chữ đậm" Checked="Bold_Checked" Unchecked="Bold_Unchecked"> và kết thúc

bằng thẻ đóng </CheckBox>.

Nút lệnh có trạng thái phát sinh hai sự kiện Checked và Unchecked,tương ứng với trạng thái

của nút là được chọn hay bỏ chọn khi người dùng nhấn nút. Trong ví dụ trên, khi nút check1 được chọn

Page 80: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team |WPF – Bài 4: Thực đơn (Menu) và thanh công cụ

(Toolbar) - WPF

17

thì hàm Bold_Checked được gọi và nút check1bỏ chọn thì hàm Bold_Unchecked được gọi. Hai

hàm này do ta tự xây dựng với mã lệnh như sau.

Mã nguồn minh họa của hai hàm xử lý sự kiện nhấn nút check1:

public partial class Window1 : Window

{

public Window1()

{

InitializeComponent();

}

//...........................................................

private void Bold_Checked(object sender, RoutedEventArgs e)

{

//Thiết lập thuộc tính chữ đậm cho textBox1

textBox1.FontWeight = FontWeights.Bold;

}

private void Bold_Unchecked(object sender, RoutedEventArgs e)

{

//Thiết lập thuộc tính chữ thường cho textBox1

textBox1.FontWeight = FontWeights.Normal;

}

private void Italic_Checked(object sender, RoutedEventArgs e)

{

//Thiết lập thuộc tính chữ nghiêng cho textBox1

textBox1.FontStyle = FontStyles.Italic;

}

private void Italic_Unchecked(object sender, RoutedEventArgs e)

{

//Thiết lập thuộc tính chữ thẳng đứng cho textBox1

textBox1.FontStyle = FontStyles.Normal;

}

}

Ngoài các nút bấm có trạng thái kiểu này, thanh công cụ còn cho phép tạo ra các nút bấm có

trạng thái loại trừ nhau bằng cách sử dụng thẻ <RadioButton> để tạo ra nút bẩm.

Page 81: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team |WPF – Bài 4: Thực đơn (Menu) và thanh công cụ

(Toolbar) - WPF

18

Như vậy, chúng ta đã hoàn tất bài học xây dựng và quản lý thanh Thực đơn và thanh công cụ.

Tiếp theo là ví dụ tổng hợp lại các kiến thức trên.

3. Ví dụ tổng hợp về xây dựng Menu và Toolbar

Xây dựng cửa sổ bao gồm một thanh thực đơn, một thanh công cụ và một hộp soạn thảo như

minh họa ở hình 4.11

Các bước thực hiện như sau:

Hình 4.11. Ví dụ minh họa tổng hợp về Menu và Toolbar

Page 82: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team |WPF – Bài 4: Thực đơn (Menu) và thanh công cụ

(Toolbar) - WPF

19

3.1 Tạo ứng dụng WPF

Ở đây, ta chú ý chọn .Net Framework 3.5.

3.2 Mã XAML của giao diện

Mở file Window1.xaml tương ứng với file code Window1.xaml.cs.

<Window x:Class="WpfMenuAndToolbar.Window1"

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

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

Title="Ví dụ về Menu và Toolbar" Height="300" Width="300"

>

<StackPanel >

<!--Khai báo Tài nguyên cần sử dụng-->

<StackPanel.Resources>

<Image x:Key="inc" Source="Resources/inc.ico" Width="16"

Height="16"></Image>

Page 83: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team |WPF – Bài 4: Thực đơn (Menu) và thanh công cụ

(Toolbar) - WPF

20

<Image x:Key="dec" Source="Resources/dec.ico" Width="16"

Height="16"></Image>

<Image x:Key="copy" Source="Resources/Copy.png" Width="16"

Height="16"></Image>

</StackPanel.Resources>

<!--Khai báo thanh thực đơn-->

<Menu Name="Menu_main">

<MenuItem Header="_Edit" >

<MenuItem Command="ApplicationCommands.Copy"

ToolTip="Copy văn bản" Icon="{StaticResource copy}"/>

<MenuItem Command="ApplicationCommands.Cut"

ToolTip="Cắt văn bản"/>

<MenuItem Command="ApplicationCommands.Paste"

ToolTip="Dán văn bản"/>

</MenuItem>

<MenuItem Header="_Font" >

<MenuItem Header="_Bold" Name="Menu21" ToolTip="Chữ

đậm" IsCheckable="True"

Checked="Bold_Checked"

Unchecked="Bold_Unchecked"/>

<MenuItem Header="_Italic" Name="Menu22" ToolTip="Chữ

nghiêng" IsCheckable="True"

Checked="Italic_Checked"

Unchecked="Italic_Unchecked"/>

<Separator/>

<MenuItem Header="I_ncrease Font Size" ToolTip="Tăng cỡ

chữ" Icon="{StaticResource inc}"

Click="IncreaseFont_Click"/>

<MenuItem Header="_Decrease Font Size" ToolTip="Giảm cỡ

chữ" Icon="{StaticResource dec}"

Click="DecreaseFont_Click"/>

</MenuItem>

</Menu>

<!--Khai báo thanh công cụ-->

<ToolBar Height="26" Name="toolBar1" Width="280"

HorizontalAlignment="Left" >

<Button Height="23" Name="button1" Width="23"

Command="ApplicationCommands.Copy" ToolTip="Copy văn bản">

<Image Source="Resources/Copy.png" Width="16"

Height="16" HorizontalAlignment="Left" />

</Button>

Page 84: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team |WPF – Bài 4: Thực đơn (Menu) và thanh công cụ

(Toolbar) - WPF

21

<Button Height="23" Name="button2" Width="23"

Command="ApplicationCommands.Cut" ToolTip="Cắt văn bản">

<Image Source="Resources/Cut.png" Width="16" Height="16"

HorizontalAlignment="Left" />

</Button>

<Button Height="23" Name="button3" Width="23"

Command="ApplicationCommands.Paste" ToolTip="Dán văn bản">

<Image Source="Resources/Paste.png" Width="16" Height="16"

HorizontalAlignment="Left" />

</Button>

<Separator/>

<CheckBox Name="check1" ToolTip="Chữ đậm"

Checked="Bold_Checked" Unchecked="Bold_Unchecked">

<Image Source="Resources/bold.png" Width="16"

Height="16" HorizontalAlignment="Left" />

</CheckBox>

<CheckBox Name="check2" ToolTip="Chữ nghiêng"

Checked="Italic_Checked" Unchecked="Italic_Unchecked">

<Image Source="Resources/Italic.png" Width="16" Height="16"

HorizontalAlignment="Left" />

</CheckBox>

<Separator/>

<Button Height="23" Name="button6" ToolTip="Tăng cỡ chữ"

Width="23" Click="IncreaseFont_Click">

<Image Source="Resources/inc.ico" Width="16" Height="16"

HorizontalAlignment="Left" />

</Button>

<Button Height="23" Name="button7" ToolTip="Giảm cỡ chữ"

Width="23" Click="DecreaseFont_Click">

<Image Source="Resources/Dec.ico" Width="16" Height="16"

HorizontalAlignment="Left" />

</Button>

</ToolBar>

<!--Khai báo hộp soạn thảo-->

<TextBox Name="textBox1" TextWrapping="Wrap"

Margin="2">

Chú cào cào nhỏ, ngồi trong đám cỏ.

</TextBox>

</StackPanel>

</Window>

Page 85: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team |WPF – Bài 4: Thực đơn (Menu) và thanh công cụ

(Toolbar) - WPF

22

3.3 Mã lệnh C# xử lý các sự kiện

using System;

using System.Collections.Generic;

using System.Text;

using System.Windows;

using System.Windows.Controls;

using System.Windows.Data;

using System.Windows.Documents;

using System.Windows.Input;

using System.Windows.Media;

using System.Windows.Media.Imaging;

using System.Windows.Shapes;

namespace WpfMenuAndToolbar

{

/// <summary>

/// Interaction logic for Window1.xaml

/// </summary>

public partial class Window1 : System.Windows.Window

{

public Window1()

{

InitializeComponent();

}

private void Bold_Checked(object sender, RoutedEventArgs e)

{

textBox1.FontWeight = FontWeights.Bold;

check1.IsChecked = true;

Menu21.IsChecked = true;

}

private void Bold_Unchecked(object sender, RoutedEventArgs e)

{

Page 86: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team |WPF – Bài 4: Thực đơn (Menu) và thanh công cụ

(Toolbar) - WPF

23

textBox1.FontWeight = FontWeights.Normal;

check1.IsChecked = false;

Menu21.IsChecked = false;

}

private void Italic_Checked(object sender, RoutedEventArgs e)

{

textBox1.FontStyle = FontStyles.Italic;

check2.IsChecked = true;

Menu22.IsChecked = true;

}

private void Italic_Unchecked(object sender, RoutedEventArgs e)

{

textBox1.FontStyle = FontStyles.Normal;

check2.IsChecked = false;

Menu22.IsChecked = false;

}

private void IncreaseFont_Click(object sender, RoutedEventArgs

e)

{

if (textBox1.FontSize < 18)

{

textBox1.FontSize += 2;

}

}

private void DecreaseFont_Click(object sender, RoutedEventArgs

e)

{

if (textBox1.FontSize > 10)

{

textBox1.FontSize -= 2;

}

}

}

}

F5 để chạy ứng dụng. Ta sẽ thu được kết quả tương tự như yêu cầu.

Page 87: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team |WPF – Bài 4: Thực đơn (Menu) và thanh công cụ

(Toolbar) - WPF

24

4. Câu hỏi ôn tập

1. Trên thanh Menu, các Menu Item mức đỉnh chỉ có thể là Popup Menu ?

A. Đúng

B. Sai

Câu trả lời: B

2. Menu Item mức dưới của một Popup Menu cũng có thể là một Popup Menu?

A. Đúng

B. Sai

Câu trả lời: A

3. Thuộc tính nào sau đây của Menu được dùng để gán nhãn (tiêu đề) cho

Menu?

A. Title

B. Header

C. Text

D. Tooltip

Câu trả lời: B

4. Các thuộc tính được có thể được sử dụng để gán lệnh cho một Menu Item

(Chọn nhiều)

A. Command

B. Click

C. OnClick

D. Checked và UnChecked

Câu trả lời: A, B và D

5. Thanh công cụ được phép nằm ở vị trí nào trên cửa sổ

A. Nằm ngang.

B. Nằm dọc

C. Được phép nằm cả theo chiều dọc và chiều ngang.

Câu trả lời: C

Page 88: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team |WPF – Bài 4: Thực đơn (Menu) và thanh công cụ

(Toolbar) - WPF

25

6. Những thẻ nào sau đây được dùng để tạo các nút trên thực đơn (Chọn nhiều):

A. <Button>

B. <Checkbox>

C. <ToolBar Item>

Câu trả lời: A và B

5. Tài liệu tham khảo

1. Windows Presentation Foundation, URL: http://msdn.microsoft.com/en-

us/library/ms754130.aspx.

2. WPF - XAML Introduction, URL:

http://homepage.ntlworld.com/herring1/xamlintro.html .

3. Menu Icons in WPF, URL: http://anuraj.wordpress.com/2008/06/23/menu-icons-in-wpf/

.

4. WPF Sample Series, URL: http://karlshifflett.wordpress.com/2008/01/23/wpf-sample-

series-stretch-toolbar-width-of-window/ .

Page 89: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team | WPF – Bài 5: Thực đơn ngữ cảnh (Context Menu) và

thanh trạng thái (Status Bar)

1

Bài 5

THỰC ĐƠN NGỮ CẢNH (CONTEXT MENU) VÀ THANH

TRẠNG THÁI (STATUS BAR)

Thực đơn ngữ cảnh (Context Menu) là loại thực đơn gắn với một điều khiển cụ thể nào đó, chẳng

hạn như một nút bấm hay một hộp soạn thảo,... Khi người dùng nhấn chuột phải vào điều khiển có gắn

thực đơn ngữ cảnh thì thực đơn ngữ cảnh của điều khiển đó sẽ hiện ra và cho phép người dùng chọn công

việc mong muốn từ thực đơn. Hình 5.1 minh họa thực đơn ngữ cảnh gắn với nút bấm.

Thanh trạng thái là thanh nằm ngang dưới đáy cửa sổ và hiển thị các thông tin về trạng thái hoạt

động của ứng dụng. Một thanh trạng thái có thể có nhiều mục trạng thái (StatusBar Item) khác nhau, mỗi

mục thể hiện một loại thông tin nào đó tới người dùng.

Nút bấm ở trạng thái

bình thường, chưa

nhấn chuột phải

Thực đơn ngữ cảnh xuất hiện khi

nhấn chuột phải lên nút bấm

Hình 5.1. Nút bấm với thực đơn ngữ cảnh (Context Menu)

Page 90: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team | WPF – Bài 5: Thực đơn ngữ cảnh (Context Menu) và

thanh trạng thái (Status Bar)

2

Bài này giới thiệu các sử dụng thực đơn ngữ cảnh và thanh trạng thái bằng ngôn ngữ XAML.

1. Xây dựng thực đơn ngữ cảnh

Tương tự như thực đơn (Menu) thông thường đã đề cập ở bài trước, thực đơn ngữ cảnh (Context

Menu) gồm nhiều phần tử thực đơn được tổ chức dưới dạng phân cấp. Mỗi phẩn tử thực đơn có thể là

Command Menu Item (được gắn trực tiếp với các bộ quản lý sự kiện - Event handler) hay Popup

Menu Item (chứa các phần tử thực đơn cấp dưới). Tuy nhiên, thực đơn ngữ cảnh không nằm trên đỉnh

cửa sổ, nó gắn với một điều khiển nào đó và xuất hiện khi người dùng nhấn chuột phải lên điều khiển

tương ứng.

Hai đoạn code dưới dây minh họa dựng thực đơn ngữ cảnh minh họa ở hình 5.1 bằng mã lệnh

XAML và bằng ngôn ngữ C# để tiện so sánh.

Đoạn mã trình xây dựng thực đơn ngữ cảnh bằng XAML:

<Grid>

<Button Name="cmButton" Height="30" Width="200">

Nút bấm với thực đơn ngữ cảnh

<Button.ContextMenu>

<ContextMenu Name="cm" >

<MenuItem Header="Menu 1"/>

<MenuItem Header="Menu 2"/>

<MenuItem Header="Menu 3">

<MenuItem Header="Menu 31"/>

<MenuItem Header="Menu 32"/>

</MenuItem>

</ContextMenu>

</Button.ContextMenu>

Mục trạng

thái 1

Hình 5.2. Thanh trạng thái gồm ba mục trạng thái khác nhau

Mục trạng

thái 2 Mục trạng

thái 3

Page 91: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team | WPF – Bài 5: Thực đơn ngữ cảnh (Context Menu) và

thanh trạng thái (Status Bar)

3

</Button>

</Grid>

Đoạn mã trình xây dựng thực đơn ngữ cảnh bằng C#:

//Tạo nút bấm

btn = new Button();

btn.Content = "Nút bấm với thực đơn ngữ cảnh";

//Tạo thực đơn ngữ cảnh

contextmenu = new ContextMenu();

//Gán thực đơn ngữ cảnh cho nút bấm

btn.ContextMenu = contextmenu;

//Tạo các phần tử thực đơn cho thực đơn ngữ cảnh

mi = new MenuItem();

mi.Header = "Thực đơn ngữ cảnh";

mi1 = new MenuItem();

mi1.Header = "Menu 1";

mi.Items.Add(mi1);

mi2 = new MenuItem();

mi2.Header = "Menu 2";

mi.Items.Add(mi2);

mib3 = new MenuItem();

mib3.Header = "Menu 3";

mib.Items.Add(mib3);

mib31 = new MenuItem();

mib31.Header = "Menu 31";

mib3.Items.Add(mib31);

mib32 = new MenuItem();

mib32.Header = "Menu 32";

mib3.Items.Add(mib32);

//Đưa các phần tử thực đơn vào thực đơn ngữ cảnh

contextmenu.Items.Add(mi);

Thực đơn ngữ cảnh có thể là loại thực đơn ngữ cảnh riêng biệt, gắn với một điều cụ thể, hoặc có

thể là loại thực đơn ngữ cảnh chia sẻ cho nhiều điều khiển dùng chung (Shared ContexMenu).

1.1 Xây dựng thực đơn ngữ cảnh riêng biệt

Thực đơn ngữ cảnh riêng biệt là thực đơn ngữ cảnh gắn với một điều khiển cụ thể, các trạng thái

của menu này chỉ dành riêng cho điều khiển chứa nó sử dụng. Mã lệnh tạo thực đơn ngữ cảnh loại này

Page 92: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team | WPF – Bài 5: Thực đơn ngữ cảnh (Context Menu) và

thanh trạng thái (Status Bar)

4

đặt trực tiếp bên trong cặp thẻ của điểu khiển chứa nó (như minh họa ở đoạn mã XAML trên). Xem ví

dụ minh họa ở hình 5.3, minh họa hai nút bấm, mỗi nút có một thực đơn ngữ cảnh riêng. Khi chọn Menu

“Đậm” của nút nào thì nội dung của nút đó hiển thị dạng chữ đậm và đồng thời Menu tương ứng cũng ở

trạng thái Checked và ngược lại. Trạng thái Checked của mục “Đậm” của menu ngữ cảnh thuộc nút bấm

1 không ảnh hưởng tới thực đơn của nút bấm 2.

Mã lệnh XAML của ví dụ trên như sau.

Đoạn mã trình Menu tạo thực đơn ngữ cảnh bằng XAML:

<Window x:Class="ContextMenuApp1.Window1"

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

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

Title="Context Menu" Height="142" Width="339">

<Grid>

<Button Name="cmButton1" Margin="13,44.5,0,32"

HorizontalAlignment="Left" Width="140">

Nút bấm 1

<Button.ContextMenu>

<ContextMenu Name="cm1" >

Menu “Đậm” của

Nút bấm 1 đang ở

trạng thái Checked

Hình 5.3. Ví dụ về thực đơn ngữ cảnh riêng biệt của từng điều khiển khác nhau

Menu “Đậm” của

Nút bấm 2 đang ở

trạng thái UnChecked

Page 93: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team | WPF – Bài 5: Thực đơn ngữ cảnh (Context Menu) và

thanh trạng thái (Status Bar)

5

<MenuItem Header="Đậm" ToolTip="Chữ đậm"

IsCheckable="True"

Checked="Bold_Checked1"

Unchecked="Bold_Unchecked1"/>

<MenuItem Header="Thông báo" Click="Message1"/>

</ContextMenu>

</Button.ContextMenu>

</Button>

<Button HorizontalAlignment="Right" Margin="0,44.5,10,32"

Name="cmButton2" Width="140">

Nút bấm 2

<Button.ContextMenu>

<ContextMenu Name="cm2">

<MenuItem Header="Đậm" IsCheckable="True" ToolTip="Chữ

đậm"

Checked="Bold_Checked2"

Unchecked="Bold_Unchecked2" />

<MenuItem Header="Thông báo" Click="Message2"/>

</ContextMenu>

</Button.ContextMenu>

</Button>

</Grid>

</Window>

Trong đoạn mã trên, ta có hai nút bấm với nhãn là “Nút bấm 1” và “Nút bấm 2”, mỗi nút bấm có

một thực đơn ngữ cảnh riêng.

Thực đơn ngữ cảnh của nút bấm được bắt đầu bằng <Button.ContextMenu> và kết thúc bằng

</Button.ContextMenu>. Trong cặp thẻ này là cặp thẻ <ContextMenu> và </ContextMenu>.

Trong cặp thẻ <ContextMenu> và </ContextMenu> chứa các thẻ <MenuItem> định nghĩa các mục

của thực đơn.

Các mục thực đơn của thực đơn ngữ cảnh hoạt động tương tự như thư đơn thông thường được đề

cập ở bài trước.

Dưới đây là đoạn mã trình C# khai báo các hàm xử lý xự kiện khi nhấn vào các mục thực

đơn trên.

namespace ContextMenuApp1

{

/// <summary>

Page 94: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team | WPF – Bài 5: Thực đơn ngữ cảnh (Context Menu) và

thanh trạng thái (Status Bar)

6

/// Interaction logic for Window1.xaml

/// </summary>

public partial class Window1 : Window

{

public Window1()

{

InitializeComponent();

}

private void Bold_Checked1(object sender, RoutedEventArgs e)

{

cmButton1.FontWeight = FontWeights.Bold;

}

private void Bold_Unchecked1(object sender, RoutedEventArgs e)

{

cmButton1.FontWeight = FontWeights.Normal;

}

private void Message1(object sender, RoutedEventArgs e)

{

MessageBox.Show("Bạn chọn thực đơn nút bấm 1");

}

private void Bold_Checked2(object sender, RoutedEventArgs e)

{

cmButton2.FontWeight = FontWeights.Bold;

}

private void Bold_Unchecked2(object sender, RoutedEventArgs e)

{

cmButton2.FontWeight = FontWeights.Normal;

}

private void Message2(object sender, RoutedEventArgs e)

{

MessageBox.Show("Bạn chọn thực đơn nút bấm 2");

}

}

}

Page 95: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team | WPF – Bài 5: Thực đơn ngữ cảnh (Context Menu) và

thanh trạng thái (Status Bar)

7

1.2 Thực đơn ngữ cảnh chia sẻ (Shared Context Menu)

Thực đơn ngữ cảnh chia sẻ là loại thực đơn ngữ cảnh có thể gắn với nhiều điều khiển khác nhau.

Khi một mục trên thực đơn ngữ cảnh của một điều khiện được Checked thì tất cả các điều khiển khác

cũng chia sẻ trạng thái này.

Ví dụ sau đây minh họa bốn điều khiển gồm hai Button và hai CheckBox cùng chia sẻ chung một

thực đơn ngữ cảnh, khi Check vào mục đầu tiên của thực đơn ngữ cảnh trên một Button hay một

CheckBox thì mục tương ứng của các thực đơn ngữ cảnh trên các Button hay CheckBox khác cũng có

trạng thái Check tương ứng.

Đoạn mã sau minh họa mã lệnh XAML của ví dụ này.

<Window

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

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

SizeToContent="WidthAndHeight" Title="Vi du Shared Context Menu">

<Window.Resources>

<ContextMenu x:Key="ContextMenuChiase" x:Shared="True">

<MenuItem Header="Đậy là mục thực đơn có trạng thái"

IsCheckable="True" />

<Separator/>

<MenuItem Header="Đây là mục thực đơn thông thường" />

</ContextMenu>

</Window.Resources>

<StackPanel Margin="5">

Hình 5.4. Ví dụ về thực đơn ngữ cảnh chia sẻ

Page 96: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team | WPF – Bài 5: Thực đơn ngữ cảnh (Context Menu) và

thanh trạng thái (Status Bar)

8

<TextBlock TextWrapping="WrapWithOverflow" Width="400"

FontSize="12">

<Run FontSize="24">Shared ContextMenu </Run>

<LineBreak/>

Có bốn điều khiển chia sẻ thực đơn ngữ cảnh.

x:Shared được gắn giá trị <Bold>True</Bold>, để cho phép các

điều khiển chia sẻ ContextMenu. Bạn có thể thử bằng cách

check vào mục thực đơn đầu tiên của một điều khiển

sau đó mở thực đơn ngữ cảnh ở các điều khiển khác để xem

trạng thái của mục đầu tiên của mỗi thực đơn ngữ cảnh.

</TextBlock>

<Button Margin="0,5,0,0" Background="LightBlue"

Content="Nút bấm này có một ContextMenu"

ContextMenu="{DynamicResource ContextMenuChiase}" />

<Button Background="Pink"

Content="Nút bấm này sử dụng ContextMenu tương tự"

ContextMenu="{DynamicResource ContextMenuChiase}" />

<CheckBox BorderBrush="Red"

Content="Check Box sử dụng ContextMenu tương tự"

ContextMenu="{DynamicResource ContextMenuChiase}" />

<CheckBox BorderBrush="Green"

Content="Check Box sử dụng ContextMenu tương tự"

ContextMenu="{DynamicResource ContextMenuChiase}" />

</StackPanel>

</Window>

Khác với thực đơn ngữ cảnh thông thường, vị trí câu lện tạo thực đơn ngữ cảnh được không nằm

giữa cặp thẻ của các điều khiển chứa nó mà được khai báo dưới dạng tài nguyên chung của Window.

<Window.Resources>

<ContextMenu x:Key="ContextMenuChiase" x:Shared="True">

<MenuItem Header="Đậy là mục thực đơn có trạng thái"

IsCheckable="True" />

<Separator/>

<MenuItem Header="Đây là mục thực đơn thông thường" />

</ContextMenu>

</Window.Resources>

Thuộc tính x:Key dùng để khai báo tên của ContextMenu, sẽ được dùng để gán cho các điều

khiển muốn sử dụng ContextMenu này. Các điều khiển sẽ gắn ContextMenu nhờ thuộc tính

ContextMenu="{DynamicResource ContextMenuChiase}".

Page 97: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team | WPF – Bài 5: Thực đơn ngữ cảnh (Context Menu) và

thanh trạng thái (Status Bar)

9

<Button Margin="0,5,0,0" Background="LightBlue"

Content="Nút bấm này có một ContextMenu"

ContextMenu="{DynamicResource ContextMenuChiase}" />

<Button Background="Pink"

Content="Nút bấm này sử dụng ContextMenu tương tự"

ContextMenu="{DynamicResource ContextMenuChiase}" />

<CheckBox BorderBrush="Red"

Content="Check Box sử dụng ContextMenu tương tự"

ContextMenu="{DynamicResource ContextMenuChiase}" />

<CheckBox BorderBrush="Green"

Content="Check Box sử dụng ContextMenu tương tự"

ContextMenu="{DynamicResource ContextMenuChiase}" />

Chú ý, phải đặt giá trị cho x:Shared="True" thì ContextMenu này mới có thể được chia sẻ

cho các điều khiển. Nếu đặt là x:Shared="False" thì các điều khiển vẫn sử dụng được MenuContext

này, nhưng mỗi điều khiển có một thể hiện riêng của ContextMenu (không chia sẻ chung).

2. Xây dựng và sử dụng thanh trạng thái (StatusBar)

Thanh công trạng thái là thanh nằm ngang, bên dưới đáy cửa sổ, gồm nhiều phần tử nhằm thể

hiện thông tin về các trạng thái hoạt động của ứng dụng. Mỗi phần tử có thể là một văn bản, một biểu

tượng hay một thanh tiến trình (Minh họa hình 5.5).

Phần tử dạng văn bản

Hình 5.5 .Thanh trạng thái

Thanh

trạng thái

Phần tử dạng thanh tiến trình Phần tử dạng ảnh

Page 98: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team | WPF – Bài 5: Thực đơn ngữ cảnh (Context Menu) và

thanh trạng thái (Status Bar)

10

2.1. Thanh trạng thái với các phần tử văn bản

Để bắt đầu tìm hiểu từng bước xây dựng thanh trạng thái, ta tìm hiểu mã lệnh XAML tạo thanh

trạng thái đơn giản với các phần tử dạng văn bản.

Thanh trạng thái đơn giản, chỉ gồm một phần tử dạng văn bản.

Thanh trạng thái đuợc xây dựng bằng đoạn mã XAML sau:

< StatusBar Grid.Row="1">

<StatusBarItem>

<TextBlock> Ready </TextBlock>

</StatusBarItem>

</StatusBar>

Thanh trạng thái gồm nhiều hơn một phần tử. Ví dụ, thanh trạng thái gồm hai mục Ready và Set

< StatusBar Grid.Row="1">

<StatusBarItem>

<TextBlock> Ready </TextBlock>

</StatusBarItem>

<StatusBarItem>

<TextBlock> Set </TextBlock>

</StatusBarItem>

</StatusBar>

Ở ví dụ trên phần tử thứ hai nằm ngay bên cạnh phần tử thứ nhất, nếu muốn phần tử thứ nhất có

rộng lớn hơn và phần tử thứ hai được căn bên phải cửa sổ cho thuận tiện (như hình dưới) ta phải sử dụng

kỹ thuật tạo layout (bố cục) trong thanh trạng thái để phân chi thanh trạng thái thành các vùng mong

muốn và đặt các phần tử cần thiết vào các vùng tương ứng

Page 99: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team | WPF – Bài 5: Thực đơn ngữ cảnh (Context Menu) và

thanh trạng thái (Status Bar)

11

<StatusBar>

<StatusBar.ItemsPanel>

<ItemsPanelTemplate>

<Grid>

<Grid.RowDefinitions>

<RowDefinition Height="*"/>

</Grid.RowDefinitions>

<Grid.ColumnDefinitions>

<ColumnDefinition Width="*"/>

<ColumnDefinition Width="Auto"/>

</Grid.ColumnDefinitions>

</Grid>

</ItemsPanelTemplate>

</StatusBar.ItemsPanel>

<StatusBarItem Grid.Column="0">

<TextBlock>Ready</TextBlock>

</StatusBarItem>

<StatusBarItem Grid.Column="1">

<TextBlock>Set</TextBlock>

</StatusBarItem>

</StatusBar>

Ở đoạn mã XAML trên ta sử dụng thẻ <StatusBar.ItemsPanel> và

</ItemsPanelTemplate> để định nghĩa bố cục của các phần tử trong thanh trạng thái. Trong đó là cặp

thẻ <ItemsPanelTemplate> và </ItemsPanelTemplate>. Kế tiếp, sử dụng thẻ <Grid> để định

nghĩa các vùng hiển thị kiểu ô lưới. Trong ví dụ này, lưới bao gồm một dòng và hai cột. Cột thứ nhất có

độ dài tự co dã đễ chiếm toàn bộ không gian trống nhờ thuộc tính Width="*". Cột thứ hai có độ dài

bằng độ dài nội dung mà nó chứa nhờ thuộc tính Width="Auto".

Phần tử thứ nhất được gắn với cột thứ đầu tiên của lưới (Grid.Column="0").

<StatusBarItem Grid.Column="0">

<TextBlock>Ready</TextBlock>

</StatusBarItem>

Cột thứ hai được gắn với cột thứ hai của lưới (Grid.Column="1").

<StatusBarItem Grid.Column="1">

<TextBlock>Set</TextBlock>

</StatusBarItem>

Page 100: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team | WPF – Bài 5: Thực đơn ngữ cảnh (Context Menu) và

thanh trạng thái (Status Bar)

12

2.2. Thanh trạng thái với phần tử là thanh tiến trình

(Progress Bar)

Đôi khi, trên thanh trạng thái ta muốn thể hiện trạng thái thực hiện của một công việc nào đó, ta

có thể đưa thanh tiến trình vào một phần tử trên thanh trạng thái (minh họa hình 5.6).

Mã lệnh minh họa thanh trạng thái trên như sau:

<StatusBar>

<StatusBar.ItemsPanel>

<ItemsPanelTemplate>

<Grid>

<Grid.RowDefinitions>

<RowDefinition Height="*"/>

</Grid.RowDefinitions>

<Grid.ColumnDefinitions>

<ColumnDefinition Width="*"/>

<ColumnDefinition Width="Auto"/>

<ColumnDefinition Width="Auto"/>

</Grid.ColumnDefinitions>

</Grid>

</ItemsPanelTemplate>

</StatusBar.ItemsPanel>

<StatusBarItem Grid.Column="0">

<TextBlock>Ready</TextBlock>

</StatusBarItem>

<StatusBarItem Grid.Column="1">

<ProgressBar Value="30" Width="80" Height="18"/>

</StatusBarItem>

<StatusBarItem Grid.Column="2">

Hình 5.6. Thanh trạng thái với phần tử là thanh tiến trình

Thanh tiến trình

Page 101: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team | WPF – Bài 5: Thực đơn ngữ cảnh (Context Menu) và

thanh trạng thái (Status Bar)

13

<TextBlock>Set</TextBlock>

</StatusBarItem>

</StatusBar>

Ở ví dụ trên, ta đặt thanh trạng thái vào mục thứ hai của thanh trạng thái.

<StatusBarItem Grid.Column="1">

<ProgressBar Value="30" Width="80" Height="18"/>

</StatusBarItem>

2.3. Thanh trạng thái với phần tử là hình ảnh

Hình 57. Minh họa phần tử thanh trạng thái là hình ảnh biểu tượng.

Mã lệnh minh họa thanh trạng thái trên như sau:

<StatusBar>

<StatusBar.ItemsPanel>

<ItemsPanelTemplate>

<Grid>

<Grid.RowDefinitions>

<RowDefinition Height="*"/>

</Grid.RowDefinitions>

<Grid.ColumnDefinitions>

<ColumnDefinition Width="*"/>

<ColumnDefinition Width="Auto"/>

<ColumnDefinition Width="Auto"/>

<ColumnDefinition Width="20"/>

</Grid.ColumnDefinitions>

Hình 5.7. Thanh trạng thái với phần tử là hình ảnh

Phần tử của thanh

trạng thái là hình ảnh

Page 102: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team | WPF – Bài 5: Thực đơn ngữ cảnh (Context Menu) và

thanh trạng thái (Status Bar)

14

</Grid>

</ItemsPanelTemplate>

</StatusBar.ItemsPanel>

<StatusBarItem Grid.Column="0">

<TextBlock>Ready</TextBlock>

</StatusBarItem>

<StatusBarItem Grid.Column="1">

<ProgressBar Value="30" Width="80" Height="18"/>

</StatusBarItem>

<StatusBarItem Grid.Column="2">

<TextBlock>Set</TextBlock>

</StatusBarItem>

<StatusBarItem Grid.Column="3">

<Image Source="Resources/Home.png" Width="16" Height="16"/>

</StatusBarItem>

</StatusBar>

Ở ví dụ trên, ta đặt thanh trạng thái vào mục thứ tư của thanh trạng thái.

<StatusBarItem Grid.Column="3">

<Image Source="Resources/Home.png" Width="16" Height="16"/>

</StatusBarItem>

2.4. Mã lệnh tổng hợp của thanh trạng thái

<Window x:Class="StatusBarApp1.Window1"

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

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

Title="Ví dụ Status Bar" Height="300" Width="300">

<Grid>

<Grid.RowDefinitions>

<RowDefinition Height="*"/>

<RowDefinition Height="30"/>

</Grid.RowDefinitions>

<StackPanel Grid.Row="0" >

<TextBlock FontSize="12" TextWrapping="WrapWithOverflow"

Width="400">

Ví dụ minh họa đặt các phần tử trên thanh trạng thái.

Mỗi phần tử có thể là văn bản, ảnh hay thanh tiến trình.

</TextBlock>

Page 103: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team | WPF – Bài 5: Thực đơn ngữ cảnh (Context Menu) và

thanh trạng thái (Status Bar)

15

</StackPanel>

<StatusBar Grid.Row="1">

<StatusBar.ItemsPanel>

<ItemsPanelTemplate>

<Grid>

<Grid.RowDefinitions>

<RowDefinition Height="*"/>

</Grid.RowDefinitions>

<Grid.ColumnDefinitions>

<ColumnDefinition Width="*"/>

<ColumnDefinition Width="Auto"/>

<ColumnDefinition Width="Auto"/>

<ColumnDefinition Width="20"/>

</Grid.ColumnDefinitions>

</Grid>

</ItemsPanelTemplate>

</StatusBar.ItemsPanel>

<StatusBarItem Grid.Column="0">

<TextBlock>Ready</TextBlock>

</StatusBarItem>

<StatusBarItem Grid.Column="1">

<ProgressBar Value="30" Width="80" Height="18"/>

</StatusBarItem>

<StatusBarItem Grid.Column="2">

<TextBlock>Set</TextBlock>

</StatusBarItem>

<StatusBarItem Grid.Column="3">

<Image Source="Resources/Home.png" Width="16"

Height="16"/>

</StatusBarItem>

</StatusBar>

</Grid>

</Window>

3. Câu hỏi ôn tập

1. Context Menu xuất hiện khi nhấn chuột nào lên điều khiển?

A. Chuột trái

Page 104: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team | WPF – Bài 5: Thực đơn ngữ cảnh (Context Menu) và

thanh trạng thái (Status Bar)

16

B. Chuột phải

Câu trả lời: B

2. Context Menu có thể chứa cả Command Menu Item và Popup Menu Item?

A. Đúng

B. Sai

Câu trả lời: A

3. Câu lệnh XAML định nghĩa thực đơn ngữ cảnh phải nằm trong cặp thẻ định

nghĩa điều khiển chứa thực đơn?

A. Đúng

B. Sai

Câu trả lời: B

4. Thực đơn ngữ cảnh có thể chia sẻ cho nhiều điều khiển dùng chung?

A. Đúng

B. Sai

Câu trả lời: A

5. Thanh trạng thái chỉ được phép chứa một phần tử trạng thái:

A. Đúng

B. Sai

Câu trả lời: B

6. Thanh trạng thái có thể chứa những các phần tử thuộc loại nào?

A. Văn bản.

B. Hình ảnh.

C. Các điều khiển khác như Button, ProgressBar,..

D. Cả ba loại trên.

Câu trả lời: D

4. Tài liệu tham khảo

1. Windows Presentation Foundation, URL: http://msdn.microsoft.com/en-

us/library/ms754130.aspx.

Page 105: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team | WPF – Bài 5: Thực đơn ngữ cảnh (Context Menu) và

thanh trạng thái (Status Bar)

17

2. MSDN – Context Menu, URL: http://msdn.microsoft.com/en-us/library/ms743670.aspx.

3. MSDN – Context Menu sample, URL: http://msdn.microsoft.com/en-

us/library/ms771470(VS.85).aspx .

4. MSDN – StatusBar, URL: http://msdn.microsoft.com/en-us/library/ms752075.aspx .

5. MSDN – StatusBar sample, URL: http://msdn.microsoft.com/en-

us/library/ms771396.aspx.

6. The Perfect WPF StatusBar, URL: http://kentb.blogspot.com/2007/10/perfect-wpf-

statusbar.html.

Page 106: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team| WPF – Bài 6: Xử lý sự kiện và lệnh trong WPF 1

Bài 6

XỬ LÝ SỰ KIỆN VÀ LỆNH TRONG WPF

Các bài giảng trước chủ yếu giới thiệu về các thành phần trực quan trong WPF và việc làm

thế nào để tạo lập giao diện đồ hoạ kết hợp những thành phần đó. Tuy nhiên, một giao diện đồ họa

không chỉ mang tính thẩm mỹ cao mà còn phải cho phép người dùng tương tác với các thành phần

trên đó. Việc tương tác với ứng dụng của người dùng thông qua giao diện đồ hoạ có liên quan nhiều

trên việc viết mã lệnh xử lý sự kiện (events) và lệnh (commands). Mặc dù các khái niệm này đã

được đề cập sơ bộ trong các bài giảng trước, bài giảng này giới thiệu một cách có hệ thống hơn về

hai khái niệm quan trọng này trong WPF.

1. Xử lý sự kiện trong WPF

1.1. Sự kiện

Mỗi khi bạn nhắp chuột vào một nút bấm hay gõ dòng văn bản nào đó vào một form, bạn

đang sử dụng sự kiện (events). Trong lập trình, có thể định nghĩa sự kiện là một hành động được

phát động bởi người dùng, bởi một thiết bị như đồng hồ đếm (timer) hay bàn phím, hoặc thậm chí là

bởi hệ điều hành, tại những thời điểm phần lớn là không theo chu trình nhất định. Ví dụ, với một

thiết bị định vị con trỏ như chuột, hành động nhắp phím chuột sẽ gây nên sự kiện “nhắp chuột”. Mỗi

khi một sự kiện xảy ra, thông thường dữ liệu liên quan đến sự kiện đó được thu thập và chuyển nó

tới một đơn vị xử lý sự kiện (event handler) để xử lý tiếp. Cũng có khi, sự kiện bị bỏ qua hay chuyển

tới nhiều hàm xử lý sự kiện một lúc nếu những hàm xử lý này cùng đồng thời lắng nghe sự kiện đó.

Dữ liệu tương ứng với một sự kiện ít nhất xác định loại sự kiện, nhưng đôi khi cũng bao gồm các

thông tin khác như sự kiện xảy ra tại thời điểm nào, đối tượng nào phát động nó...

Thông thường, ta hầu như không suy nghĩ về việc sự kiện xảy ra như thế nào, ví dụ làm sao

để máy tính nhận biết chuột trái được nhắp, hay một phím trên bàn phím được bấm… Lý do là vì

các chi tiết ở mức thấp này đã được framework đồ hoạ trong máy tính xử lý. Ngay cả đối với người

phát triển, công việc của ta với sự kiện phần lớn là xử lý phần bề nổi của nhiều vấn đề ở phía sau

Page 107: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team| WPF – Bài 6: Xử lý sự kiện và lệnh trong WPF 2

mỗi sự kiện. Ngay cả trong trường hợp đó, có rất nhiều phần “bề nổi” cần được xem xét. Trong

phần này, trước hết ta tìm hiểu cơ chế xử lý sự kiện trong WPF.

1.2. Đơn vị xử lý sự kiện

Mỗi đơn vị xử lý sự kiện (event handler) đơn giản là một phương thức (hàm) nhận đầu vào

từ một thiết bị như chuột hay bàn phím và thực hiện một việc nào đó để phản ứng lại với một sự

kiện xảy ra trên thiết bị đó. Ví dụ sau đây minh hoạ đoạn mã lệnh C# là một đơn vị xử lý sự kiện có

tên ButtonOkClicked có tác dụng xử lý sự kiện nút chuột được bấm:

private void ButtonOkClicked(object sender, RoutedEventArgs e)

{

this.Close(); //đóng cửa sổ hiện thời

}

Trong các phần tiếp theo, để đễ hiểu, ta dùng từ “hàm xử lý sự kiện” với nghĩa tương đương

“đơn vị xử lý sự kiện”

Thực chất, có hai bước cần thực hiện để xử lý một sự kiện:

1. Liên kết đơn vị xử lý sự kiện với điều khiển (nút bấm, trường văn bản, thực

đơn…), nơi sự kiện tương ứng được phát động.

2. Viết mã lệnh trong đơn vị xử lý sự kiện để lập trình các công việc phản ứng

lại với sự kiện.

Có hai cách để liên kết một sự kiện với một đơn vị xử lý sự kiện. Bạn có thể dùng (1) một

môi trường phát triển tích hợp (IDE) như Expression Blend hoặc WPF Designer của Visual Studio

(cách trực quan); hoặc (2) viết mã lệnh trực tiếp.

1.2.1. Cách liên kết trực quan

Để liên kết theo cách này, ta cần có các công cụ thiết kế giao diện GUI dành cho WPF chẳng

hạn như Expression Blend hoặc WPF Designer của Visual Studio. Với các công cụ này, với mỗi

phần từ UI trên giao diện ta có cửa sổ liệt kế các sự kiện. Với mỗi sự kiện, ta có thể phân định đơn

Page 108: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team| WPF – Bài 6: Xử lý sự kiện và lệnh trong WPF 3

vị xử lý sự kiện bằng cách khai báo tên hàm xử lý (không gồm đối số) bên cạnh sự kiện ta muốn bắt

và xử lý. Hình 6.1 minh hoạ việc khai báo hàm xử lý sự kiện ButtonOkClicked ứng với sự kiện

Click của nút bấm btnOK sử dụng Expression Blend.

Hình 6.1 - Phân định trực quan hàm ButtonOkClicked xử lý sự kiện Click

của nút btnOK trên Expression Blend

Sau khi khai báo, ta nhấn Enter, môi trường sẽ tự động tạo sinh và chuyển ta đến khuôn rỗng

của hàm xử lý sự kiện có tên giống với tên ta đã đặt cho đơn vị xử lý sự kiện khi khai báo, và với

danh sách tham số ngầm định tương ứng với loại sự kiện. Nhiệm vụ của người lập trình lúc này là

viết mã lệnh thực hiện các hành động phản ứng với sự kiện bên trong hàm xử lý này. Trong ví dụ về

nút bấm trên, khuôn dạng tự sinh của hàm xử lý sẽ là:

private void ButtonOkClicked(object sender, RoutedEventArgs e)

{

//viết mã xử lý vào đây

}

Khi nhìn lại mã XAML tương ứng, ta sẽ thấy WPF sử dụng XAML để khai báo liên kết giữa

sự kiện mà hàm xử lý sự kiện như thế nào:

<Button HorizontalAlignment="Left" Margin="130,92,0,86" x:Name="btnOK"

Width="80" Content="OK" Click="ButtonOkClicked"/>

Page 109: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team| WPF – Bài 6: Xử lý sự kiện và lệnh trong WPF 4

Như đã thấy, để gắn kết sự kiện Click với hàm xử lý ButtonOkClicked, ta có thể khai báo

Click="ButtonOkClicked" trong khai báo tạo lập nút bấm trong mã XAML.

1.2.2. Cách liên kết bằng mã lệnh trực tiếp

Ta cũng có thể liên kết sự kiện vào hàm xử lý bằng mã lệnh với kết quả không đổi. Bạn có

thể tự hỏi tại sao không chọn cách trực quan ở trên. Một lý do cơ bản là nếu ta muốn tạo ra các điều

khiển một cách linh động, ví dụ sinh ra một hay nhiều nút bấm trong thời gian chạy (runtime) chứ

không phải tạo lập sẵn trong thời gian thiết kế form (design-time), thì cách duy nhất để liên kết sự

kiện của các điều khiển đó vào hàm xử lý là thông qua mã lệnh. Xét ví dụ sau đây:

Giả sử ta có một nút bấm có tên là btnOK, và mục tiêu của ta là gắn kết một sự kiện của nó

với hàm xử lý mà chỉ dùng mã lệnh. Tất cả những việc phải làm là chọn tên sự kiện tương ứng mà

ta muốn bắt và liên kết nó với dòng lệnh new RoutedEventHandler với đối số là tên của hàm xử lý

của ta. Ví dụ:

btnOK.Click += new RoutedEventHandler(ButtonOkClicked);

Tiếp theo ta khai báo hàm xử lý với đối số tương ứng với sự kiện. Thông thường mỗi loại sự

kiện của mỗi loại điều khiển lại đòi hỏi hàm xử lý sự kiện tương ứng với nó có chứa danh sách tham

số xác định (có số lượng, thứ tự và kiểu tham số xác định trước), mặc dù tên gọi của hàm xử lý có

thể tuỳ ý. Nếu ta sử dụng cách trực quan, cấu trúc của hàm xử lý sự kiện sẽ được tự động tạo ra.

Việc của ta chỉ là viết nội dung xử lý bên trong hàm xử lý. Trong trường hợp viết mã lệnh, ta phải

tự viết phần khai báo hàm xử lý, trong đó, cần tuân theo quy tắc định nghĩa về cấu trúc tham số (số

lượng, thứ tự, kiểu tham số) tương ứng của sự kiện đó. Để biết được cấu trúc này, không gì khác

ngoài việc tìm đọc các tài liệu tham khảo về sự kiện tương ứng, mà MSDN là tài liệu đầy đủ và

chính xác nhất.

Trong ví dụ trên, phần nội dung hàm xử lý sự kiện Click trong mã C# sẽ là:

private void ButtonOkClicked(object sender, RoutedEventArgs e)

{

this.Close();

Page 110: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team| WPF – Bài 6: Xử lý sự kiện và lệnh trong WPF 5

}

Để ý rằng hàm xử lý sự kiện trong ví dụ chứa 2 tham số mà giá trị của chúng sẽ được lấy từ

sự kiện – sender tham chiếu đến đối tượng phát động sự kiện (ở đây là nút bấm btnOK) và event (e)

chỉ ra dạng tác động cụ thể để sự kiện bị kích hoạt, chẳng hạn như bấm phím hay nhắp chuột...

Trong nhiều trường hợp, bạn không cần phải quan tâm đến các tham số của hàm xử lý sự kiện. Ví

dụ, trong đoạn mã ví dụ ở trên, phần nội dung xử lý sự kiện không hề dùng tới tham số sender lẫn

tham số e. Tuy nhiên, sẽ có những trường hợp trong đó, bạn muốn sử dụng cùng một hàm xử lý ứng

với nhiều sự kiện có cùng bản chất hoặc cho một loại sự kiện của nhiều đối tượng cùng loại. Khi đó,

ta phải quan tâm đến điều khiển nào đã gửi sự kiện, lúc đó tham số sender và event có thể sẽ hữu

dụng.

1.3 Sự kiện có định tuyến

WPF mở rộng mô hình lập trình hướng sự kiện chuẩn của .NET, bằng việc đưa ra một loại

sự kiện mới gọi là sự kiện có định tuyến (routed event). Loại sự kiện này nâng cao tính linh hoạt

trong các tình huống lập trình hướng sự kiện. Việc thiết lập và xử lý một sự kiện có định tuyến có

thể thực hiện với cùng cú pháp với một sự kiện “thường” (CLR event).

1.3.1 Cây trực quan

Trước khi bàn luận thêm về sự kiện có định tuyến, một khái niệm quan trọng cần biết đó là

cây trực quan (visual tree). Một giao diện người dùng WPF được xây dựng theo phương thức phân

lớp, trong đó một phần tử trực quan không có hoặc có các phần tử con. Cấu trúc phân cấp của các

lớp phần tử trực quan như thế trên một giao diện người dùng được gọi là cây trực quan của giao

diện đó. Ví dụ, xét giao diện được định nghĩa bằng đoạn mã XAML sau:

<Border Height="50" Width="300" BorderBrush="Gray" BorderThickness="1">

<StackPanel Background="LightGray" Orientation="Horizontal"

Button.Click="CommonClickHandler">

<Button Name="YesButton" Width="Auto" >Yes</Button>

<Button Name="NoButton" Width="Auto" >No</Button>

<Button Name="CancelButton" Width="Auto" >Cancel</Button>

</StackPanel>

Page 111: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team| WPF – Bài 6: Xử lý sự kiện và lệnh trong WPF 6

</Border>

Kết quả khi chạy chương trình:

Cây trực quan tương ứng sẽ là:

Hình 6.2 – Ví dụ về cây trực quan

1.3.2 Sự kiện có định tuyến là gì?

Về mặt chức năng, sự kiện có định tuyến là một loại sự kiện có thể kích hoạt nhiều đơn vị

xử lý sự kiện thuộc về nhiều điều khiển khác nhau trên cây trực quan, chứ không chỉ trên đối tượng

đã phát động sự kiện.

Một ứng dụng WPF điển hình thường chứa nhiều phần tử UI. Bất kể được tạo ra bằng mã

lệnh hay được khai báo bằng XAML, các thành phần này tồn tại trong mối quan hệ kiểu cây trực

quan với nhau - tạo nên các tuyến quan hệ đi từ thành phần này tới thành phần kia. Theo các tuyến

quan hệ đó, có ba phương thức định tuyến sự kiện: lan truyền lên (bubble), lan truyền xuống

(tunnel) và trực tiếp (direct).

Border

StackPanel

Button Button Button

Page 112: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team| WPF – Bài 6: Xử lý sự kiện và lệnh trong WPF 7

Lan truyền lên (bubble) là phương thức thường thấy nhất. Nó có nghĩa là một sự kiện sẽ

được truyền đi trên cây trực quan từ thành phần nguồn (nơi sự kiện được phát động) cho tới khi nó

được xử lý hoặc nó chạm tới nút gốc. Điều này cho phép ta xử lý một sự kiện trên một đối tượng

nằm ở cấp trên so với thành phần nguồn. Ví dụ, bạn có thể gắn một hàm xử lý sự kiện Button.Click

vào đối tượng Grid có chứa nút bấm thay vì gắn hàm xử lý đó vào bản thân nút bấm. Sự kiện lan

truyền lên có tên gọi thể hiện hành động của sự kiện, ví dụ: MouseDown.

Sự kiện lan truyền xuống (tunnel) đi theo hướng ngược lại, bắt đầu từ nút gốc và truyền

xuống cây trực quan cho tới khi nó được xử lý hoặc chạm tới thành phần gốc của sự kiện đó. Điều

này cho phép các thành phần cấp trên có thể chặn sự kiện và xử lý nó trước khi sự kiện đó chạm tới

thành phần nguồn (nơi dự định xảy ra sự kiện). Các sự kiện lan truyền xuống có tên được gắn thêm

tiền tố Preview, ví dụ, sự kiện PreviewMouseDown.

Sự kiện trực tiếp (direct) hoạt động giống như sự kiện thông thường trong .NET Framework.

Chỉ có một đơn vị xử lý duy nhất sẽ được gắn với sự kiện trực tiếp.

Thông thường, nếu một sự kiện lan truyền xuống được định nghĩa cho một sự kiện nào đó,

đồng thời cũng sẽ có một sự kiện lan truyền lên tương ứng. Trong trường hợp đó, sự kiện lan truyền

xuống sẽ được phát động trước, bắt đầu từ gốc và chạy xuống tìm kiếm hàm xử lý trên cây trực

quan. Một khi nó đã được xử lý hoặc chạm tới thành phần nguồn, sự kiện lan truyền lên sẽ được

phát động, lan truyền từ thành phần nguồn đi ngược lên để tìm tới hàm xử lý nó trên cây trực quan.

Sự kiện lan truyền lên hay xuống sẽ không ngừng lan truyền vì một hàm xử lý nó được gọi. Do vậy,

nếu ta muốn dừng quá trình truyền xuống hoặc lên, ta phải đánh dấu “đã xử lý” cho tham số sự kiện

truyền vào, cụ thể:

private void OnChildElementMouseDown(object sender, MouseButtonEventArgs

e) {

e.Handled = true;

}

Một khi ta đã đánh dấu “đã xử lý” cho sự kiện (e.Handled = true), nó sẽ không được lan

truyền tiếp nữa.

Page 113: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team| WPF – Bài 6: Xử lý sự kiện và lệnh trong WPF 8

Hình 6.3 - Sự kiện có định tuyến trên cây trực quan [xxx]

Trở lại ví dụ trên, nguồn của sự kiện Click là một trong những thành phần nút bấm, và bất

kể nút nào được bấm, nó sẽ trở thành thành phần đầu tiên được phép xử lý sự kiện. Tuy nhiên, nếu

không có đơn vị xử lý nào tương ứng với sự kiện Click gắn với nút đó, thì sự kiện sẽ được lan

truyền lên trên phần tử cha của nút bấm, trong trường hợp này là StackPanel, rồi sau đó, lan truyền

tới Border... Nói cách khác, tuyến lan truyền sự kiện Click sẽ là:

ButtonStackPanelBorder...

1.3.3 Các tình huống cơ bản sử dụng sự kiện có định tuyến

Phần sau đây tổng kết những tình huống cần vận dụng khái niệm sự kiện có định tuyến, và

tại sao một sự kiện CLR điển hình là không đủ trong những tình huống đó.

a. Bao đóng và kết hợp điều khiển

Nhiều điều khiển trong WPF có cấu trúc nội dung phức hợp. Ví dụ, ta có thể đặt một hình

ảnh bên trong một nút bấm, làm mở rộng cây trực quan của nút bấm. Tuy nhiên, hình ảnh thêm vào

không được phép phá vỡ cơ chế hit-testing, cơ chế khiến nút bấm phản ứng với việc nhắp chuột vào

trong nó, ngay cả khi người dùng nhắp chuột vào những pixel là một phần của hình ảnh thêm vào.

b. Các điều khiển sử dụng cùng một đơn vị xử lý sự kiện

Page 114: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team| WPF – Bài 6: Xử lý sự kiện và lệnh trong WPF 9

Trong Windows Forms, có trường hợp ta cần gán nhiều lần cùng một đơn vị xử lý để xử lý

các sự kiện thuộc vào nhiều thành phần khác nhau. Sự kiện có định tuyến cho phép ta gán đơn vị xử

lý chỉ một lần trong trường hợp đó. Như trong ví dụ đã nêu trong đoạn mã XAML, sau đây là hàm

xử lý tương ứng:

private void CommonClickHandler(object sender, RoutedEventArgs e)

{

FrameworkElement feSource = e.Source as FrameworkElement;

switch (feSource.Name)

{

case "YesButton":

// do something here ...

break;

case "NoButton":

// do something ...

break;

case "CancelButton":

// do something ...

break;

}

e.Handled=true;

}

c. Xử lý lớp:

Sự kiện có định tuyến cho phép một đơn vị xử lý tĩnh (static) được định nghĩa trong lớp.

Đơn vị xử lý lớp này có cơ hội xử lý một sự kiện trước khi một đơn vị xử lý gắn với đối tượng cụ

thể nào đó của lớp có thể.

d. Tham chiếu đến một sự kiện mà không bị hiện tượng phản xạ:

Các kỹ thuật markup và mã lệnh đòi hỏi phải có cách để định danh một sự kiện. Một sự kiện

có định tuyến tạo ra trường RoutedEvent như một định danh, cung cấp một kỹ thuật định danh sự

kiện mạnh mà không đòi hỏi hiện tượng phản xạ tĩnh hoặc run-time.

Page 115: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team| WPF – Bài 6: Xử lý sự kiện và lệnh trong WPF 10

1.3.4 Lợi ích của sự kiện có định tuyến

Cơ chế thông báo sự kiện kiểu định tuyến có nhiều lợi ích. Một lợi ích rất quan trọng của sự

kiện có định tuyến là một thành phần UI trực quan không cần móc nối cùng một sự kiện trên tất cả

các thành phần con trong nó, chẳng hạn sự kiện MouseMove. Thay vào đó, nó có thể móc nối sự

kiện này vào bản thân nó, và khi con chuột di chuyển qua một trong các thành phần con của nó, sự

kiện này sẽ được lan truyền tới nó.

Một ưu điểm quan trọng khác của sự kiện có định tuyến là các thành phần ở tất cả các mức

trong cây trực quan có thể tự động thực thi mã lệnh để phản ứng lại các sự kiện của các thành phần

con của chúng, mà không cần các thành phần con phải thông báo khi sự kiện xảy ra.

1.3.5 Một ví dụ đầy đủ về sự kiện có định tuyến

Form chỉ bao gồm một StackPanel chứa 2 Button và 1 TextBlock có tên xác định.

StackPanel được phân định bắt sự kiện Click trên hai nút bấm nằm trong nó. Nhiệm vụ của đơn vị

xử lý sự kiện Click là cho biết đối tượng nào đã xử lý sự kiện Click, sự kiện Click phát ra từ loại đối

tượng nào, tên gọi là gì nào, và loại lan truyền định tuyến đã được thực hiện. Các thông tin trên

được đưa vào nội dung của TextBlock và hiển thị lên màn hình sau mỗi sự kiện Click.

Đoạn mã XAML khai báo giao diện như sau:

<Window x:Class="Lesson6.Window1"

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

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

Title="Lesson6 - Routed Events" Height="300" Width="300"

>

<!--Khai báo stack panel làm layout chính.

Trong đó, có bắt sự kiện Click của Button và xử lý qua hàm

HandleClick-->

<StackPanel

Name="My_StackPanel"

Button.Click="HandleClick"

>

<!--Khai báo tạo lập Button 1-->

Page 116: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team| WPF – Bài 6: Xử lý sự kiện và lệnh trong WPF 11

<Button Name="Button1">Nút bấm 1</Button>

<!--Khai báo tạo lập Button 2-->

<Button Name="Button2">Nút bấm 2</Button>

<!--Khai báo tạo lập TextBlock lưu trữ và hiển thị kết quả-->

<TextBlock Name="Results"/>

</StackPanel>

</Window>

Đoạn mã lệnh C# cho hàm HandleClick để xử lý sự kiện Click:

//Dùng một StringBuilder để lưu trữ thông tin kết quả

StringBuilder eventstr = new StringBuilder();

//Đơn vị xử lý sự kiện Click của Button

void HandleClick(object sender, RoutedEventArgs args)

{

//Lấy thông tin về đối tượng xử lý sự kiện Click

FrameworkElement fe = (FrameworkElement)sender;

eventstr.Append("Sự kiện được xử lý bởi đối tượng có tên: ");

eventstr.Append(fe.Name);

eventstr.Append("\n");

//

//Lấy thông tin về nguồn phát ra sự kiện CLick:

FrameworkElement fe2 = (FrameworkElement)args.Source;

eventstr.Append("Sự kiện xuất phát từ nguồn đối tượng kiểu:

");

//+ Loại thành phần UI;

eventstr.Append(args.Source.GetType().ToString());

//+ Định danh;

eventstr.Append(" với tên gọi: ");

eventstr.Append(fe2.Name);

eventstr.Append("\n");

//

//Lấy thông tin về phương thức định tuyến

eventstr.Append("Sự kiện sử dụng phương thức định tuyến: ");

eventstr.Append(args.RoutedEvent.RoutingStrategy);

eventstr.Append("\n");

Page 117: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team| WPF – Bài 6: Xử lý sự kiện và lệnh trong WPF 12

//

//Đưa thông tin ra màn hình

Results.Text = eventstr.ToString();

}

Kết quả như sau:

Hình 6.4 – Ví dụ về sự kiện có định tuyến

2. Lệnh (Command) trong WPF

Ra lệnh (commanding) là một cơ chế nhập tin trong WPF cung cấp khả năng xử lý đầu vào ở mức

ngữ nghĩa hơn là xử lý đầu vào từ thiết bị. Các ví dụ về command là các hành động Copy, Cut và Paste mà ta

đã gặp ở nhiều ứng dụng. Phần tiếp theo sẽ trình bày tổng quan về khái niệm này trong WPF.

2.1 Lệnh là gì?

Điểm khác biệt giữa lệnh và một đơn vị xử lý sự kiện đơn giản gắn với một nút hay một đồng hồ

đếm là: lệnh tách bạch giữa ngữ nghĩa cũng như nguồn phát hành động với logic thực hiện hành động đó.

Điều này cho phép nhiều nguồn khác biệt nhau hoàn toàn có thể phát động cùng một logic lệnh, đồng thời,

cho phép tuỳ biến logic lệnh tuỳ vào các đối tượng bị tác động khác nhau.

Ví dụ điển hình về lệnh là các hành động Copy, Cut và Paste, được thấy ở rất nhiều ứng dụng. Ngữ

nghĩa của các lệnh này là nhất quán với tất cả các ứng dụng và lớp khác nhau (Copy - tạo bản sao từ đối

tượng được chọn; Cut - tạo bản sao rồi xoá bỏ đối tượng được chọn (cắt); Paste – Chèn đối tượng được

copy/cắt vào vị trị được chọn). Tuy nhiên, logic hành động lại tuỳ thuộc vào đối tượng cụ thể mà ta tác động

lên. Ví dụ, tổ hợp phím CTRL+X có thể phát động lệnh Cut trên các lớp văn bản, các lớp hình ảnh và trên

trình duyệt Web, nhưng logic thực sự thực hiện hành động Cut lại được định nghĩa bởi đối tượng hoặc ứng

Page 118: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team| WPF – Bài 6: Xử lý sự kiện và lệnh trong WPF 13

dụng mà lệnh cắt tác động lên chứ không phải từ nguồn đã phát ra lệnh. Cụ thể hơn, một đối tượng văn bản

có thể cắt đoạn văn bản được chọn vào clipboard, trong khi một đối tượng hình ảnh có thể cắt lấy vùng ảnh

được chọn, nhưng nguồn phát lệnh là như nhau - một tổ hợp phím hay một nút bấm trên thanh công cụ.

Một cách đơn giản để sử dụng lệnh trong WPF là sử dụng một RoutedCommand đã được định sẵn

trong các lớp thư viện lệnh; sử dụng một điều khiển có hỗ trợ sẵn xử lý lệnh đó và một điều khiển hỗ trợ sẵn

khả năng phát động lệnh. Trong ví dụ dưới đây, lệnh Paste là một trong những lệnh định sẵn trong lớp

ApplicationCommands. Điều khiển TextBox đã xây dựng sẵn khả năng xử lý lệnh Paste. Và lớp MenuItem

hỗ trợ khả năng phát động lệnh.

Ví dụ sau đây minh hoạ cách thức tạo lập một MenuItem để khi nhắp chuột vào nó, lệnh Paste sẽ

được phát động trên một TextBox, với giả thiết là hộp TextBox đang nhận được focus.

Mã XAML:

<StackPanel>

<Menu>

<!--Khai báo hàm xử lý lệnh Paste-->

<MenuItem Command="ApplicationCommands.Paste" />

</Menu>

<TextBox />

</StackPanel>

2.2 Những khái niệm chính trong hệ thống lệnh của WPF

Mô hình lệnh trong WPF có thể được chia thành bốn khái niệm chính: lệnh, nguồn lệnh, đích lệnh,

và liên kết lệnh, trong đó:

- Lệnh là hành động được thực hiện

- Nguồn lệnh là đối tượng phát động lệnh

- Đích lệnh là đối tượng mà lệnh tác động lên

- Liên kết lệnh là đối tượng ánh xạ logic thực hiện lệnh với lệnh

Trong ví dụ trên đây, Paste là lệnh, MenuItem là nguồn lệnh, TextBox là đích lệnh, và liên kết lệnh

được cung cấp bởi điều khiển TextBox (định sẵn). Cần lưu ý rằng không phải lúc nào liên kết lệnh

(CommandBinding) cũng được cung cấp bới điều khiển đóng vai trò đích lệnh. Thông thường,

Page 119: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team| WPF – Bài 6: Xử lý sự kiện và lệnh trong WPF 14

CommandBinding phải được tạo bởi người lập trình ứng dụng, hoặc CommandBinding có thể được gắn với

đối tượng cha của đích lệnh.

2.3 Lệnh có định tuyến

Sự khác biệt giữa lệnh có định tuyến và sự kiện có định tuyến là cách mà lệnh được dẫn

đường từ nơi phát động lệnh (nguồn lệnh) tới nơi xử lý lệnh (đích lệnh). Trong mô hình lệnh có

định tuyến, sự kiện có định tuyến được sử dụng dưới dạng các thông báo giữa giữa nguồn lệnh vào

đích lệnh (thông qua liên kết lệnh).

Trong một thời điểm nhất định, chỉ có một đơn vị xử lý lệnh (gắn với đích lệnh) sẽ được

thực sự kích hoạt (Đơn vị xử lý lệnh hoạt động). Đơn vị xử lý lệnh hoạt động được xác định bằng

việc kết hợp giữa vị trí của nguồn lệnh và đích lệnh trên cây, và đâu là thành phần UI đang nhận

được focus. Khi lệnh được phát đi, sự kiện có định tuyến sẽ được sử dụng để gọi đến đơn vị xử lý

lệnh hoạt động, để hỏi xem lệnh này có được cho phép không (thông qua phương thức

CanExecute), cũng như thực hiện logic hành động (thông qua phát động phương thức Executed).

Thông thường, nơi phát lệnh sẽ tìm liên kết lệnh giữa vị trí của nó trên cây trực quan và nút

gốc của cây trực quan. Nếu nó tìm thấy một liên kết lệnh như thế, đơn vị xử lý lệnh tương ứng sẽ

xác định lệnh này có được cho phép thực hiện không. Nếu như lệnh được gắn với một điều khiển

trên thanh công cụ hay menu, thì một vài bước logic thêm sẽ được thực hiện để tìm dọc theo đường

đi trên cây trực quan từ nút gốc tơi phần tử đang nhận được focus để tìm kiếm một liên kết lệnh.

Một điểm quan trọng cần hiểu về việc định tuyến trong lệnh có định tuyến của WPF là một khi một

đơn vị xử lý lệnh đã được kích hoạt, sẽ không có đơn vị xử lý nào khác được gọi.

Để nắm rõ hơn về ưu điểm của việc sử dụng lệnh trong WPF ta xét ví dụ sau:

2.4 Một ví dụ về sử dụng lệnh trong WPF

Ta xét một form gồm một ListBox (có tên lsbCustomers) chứa danh sách tên các khách

hàng và một menu có chứa mục xoá Delete, có tác dụng xoá mục được chọn trong danh sách. Ta

Page 120: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team| WPF – Bài 6: Xử lý sự kiện và lệnh trong WPF 15

muốn chắc chắn rằng người sử dụng phải chọn tên khách hàng trong danh sách trước khi có thể

bấm mục xoá Delete trên menu.

Đoạn mã sau sẽ vô hiệu hoá mục Delete trên cơ sở có một mục được chọn trong danh sách

khách hàng hay không.

private void HandleMenus()

{

menuDelete.IsEnabled = lsbCustomers.SelectedItem != null;

}

Đây là cách thông thường để đồng bộ hoá việc cho phép hay vô hiệu một mục menu hay nút

bấm ứng với một điều kiện nào đó. Để đạt mục tiêu đã nêu của đầu bài, đoạn mã trên có thể được

gọi trong đơn vị xử lý sự kiện SelectionChanged của ListBox như sau:

private void lsbCustomers_SelectionChanged(object sender,

SelectionChangedEventArgs e)

{

HandleMenus();

}

Cách xử lý dựa trên sự kiện kiểu này là bình thường khi trên form chỉ có một ListBox. Tuy

nhiên, khi form trở nên phức tạp hơn, ví dụ chứa 2 ListBox, khi đó việc xử lý theo cách trên trở nên

phức tạp. Trở lại ví dụ, giả sử ta có thêm một ListBox có chứa danh sách các mặt hàng (có tên

lsbProducts). Cả hai ListBox chứa tên khách hàng và tên mặt hàng đều chịu tác động của mục

Delete khi chúng nhận được focus và một trong các tên được chọn. Trong trường hợp này, để xét

xem mục Delete nên bị vô hiệu hoá hay không, điều kiện kiểm tra trở nên phức tạp hơn:

private void HandleMenus()

{

menuDelete.IsEnabled =

(lsbCustomers.SelectedItem != null &&

((ListBoxItem)lsbCustomers.SelectedItem).IsFocused) ||

(lsbProducts.SelectedItem != null &&

((ListBoxItem)lsbProducts.SelectedItem).IsFocused);

}

Page 121: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team| WPF – Bài 6: Xử lý sự kiện và lệnh trong WPF 16

Đồng thời, cũng yêu cầu thêm mã lệnh đối với việc xử lý sự kiện Click lên mục Delete trên

menu: Ta phải xác định ListBox nào bị tác động:

private void menuDelete_Click(object sender, RoutedEventArgs e)

{

if (lsbCustomers.SelectedItem != null &&

((ListBoxItem)lsbCustomers.SelectedItem).IsFocused)

lsbCustomers.Items.Remove(lsbCustomers.SelectedItem);

else if (lsbProducts.SelectedItem != null &&

((ListBoxItem)lsbProducts.SelectedItem).IsFocused)

lsbProducts.Items.Remove(lsbProducts.SelectedItem);

}

Hãy tưởng tượng nếu như form chứa khoảng 5 điều khiển cùng chịu tác động của hành động

Delete, phần mã lệnh xử lý sẽ trở nên phức tạp đến mức nào. May mắn là WPF cung cấp một

phương thức tốt hơn trong trường hợp như vậy. Cơ chế lệnh trong WPF đơn giản hoá mã lệnh trong

trường hợp này bởi nó phân tách rõ giữa lệnh với phần triển khai lệnh (logic lệnh), cho phép ta liên

kết điều khiển với những lệnh cụ thể, như ta sẽ thấy trong tiếp theo.

Đây là đoạn mã lệnh tương đương cho ví dụ trên sử dụng Command trong WPF.

<StackPanel>

<Menu>

<MenuItem Command="ApplicationCommands.Delete"

Header="Delete" />

</Menu>

<Label>Khách hàng:</Label>

<ListBox Name="lsbCustomers">

<ListBox.CommandBindings>

<CommandBinding

Command="ApplicationCommands.Delete"

CanExecute="DeleteCustomer_CanExecute"

Executed="DeleteCustomer_Executed" />

</ListBox.CommandBindings>

Page 122: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team| WPF – Bài 6: Xử lý sự kiện và lệnh trong WPF 17

<ListBoxItem>Bùi Như Lạc</ListBoxItem>

<ListBoxItem>Ngô Giang Thơm</ListBoxItem>

<ListBoxItem>Nguyễn Y Vân</ListBoxItem>

</ListBox>

</StackPanel>

Đoạn mã đầu phân định giá trị cho thuộc tính Command cho mục Delete trên menu. Nó

cũng gắn một liên kết lệnh vào ListBox danh sách khách hàng. Trong trường hợp này, menu Delete

là nguồn lệnh, và ListBox đóng vai trò là đích lệnh. CommandBinding xác định hàm thực hiện đối

với hai thuộc tính CanExecute và Executed. CanExecute xác định khi nào lệnh Delete có thể được

thực hiện, trong khi Executed xác định thực hiện logic lệnh trên đích lệnh như thế nào. Sau đây là

mã lệnh cài đặt cho hai hàm này:

private void DeleteCustomer_CanExecute(object sender,

CanExecuteRoutedEventArgs e)

{

e.CanExecute = lsbCustomers.SelectedItem != null;

}

private void DeleteCustomer_Executed(object sender,

ExecutedRoutedEventArgs e)

{

lsbCustomers.Items.Remove(lsbCustomers.SelectedItem);

}

Cho tới đây, ta chưa thấy được ưu điểm của cách tiếp cận này. Tuy nhiên, trong trường hợp

thêm vào một ListBox danh sách sản phẩm, lợi ích của phương pháp sẽ thể hiện rõ hơn. Sau đây là

đoạn mã XAML khai báo tạo lập ListBox chứa danh sách sản phẩm:

<Label>Sản phẩm sách:</Label>

<ListBox x:Name="lsbProducts" >

<ListBox.CommandBindings>

<CommandBinding

Command="ApplicationCommands.Delete"

CanExecute="DeleteProduct_CanExecute"

Executed="DeleteProduct_Executed" />

Page 123: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team| WPF – Bài 6: Xử lý sự kiện và lệnh trong WPF 18

</ListBox.CommandBindings>

<ListBoxItem>Nếu còn có ngày mai</ListBoxItem>

<ListBoxItem>Chiếc lá rơi màu xanh</ListBoxItem>

<ListBoxItem>Nhân gian chi ngộ</ListBoxItem>

</ListBox>

Một lần nữa, ta chỉ cần cài đặt hai phương thức CanExecute và Executed như sau:

private void DeleteProduct_CanExecute(object sender,

CanExecuteRoutedEventArgs e)

{

e.CanExecute = lsbProducts.SelectedItem != null;

}

private void DeleteProduct_Executed(object sender, ExecutedRoutedEventArgs

e)

{

lsbProducts.Items.Remove(lsbProducts.SelectedItem);

}

Không giống như cách tiếp cận truyền thống, khi sử dụng phương thức lệnh trong WPF, ta không

cần phải thay đổi mã của nguồn lệnh (menu Delete) khi thêm ListBox thứ hai. Cũng chú ý rằng bạn

không cần phải cân nhắc điều khiển nào nhận được focus. Lớp CommandManeger (lớp phối hợp

hoạn động của các lệnh trong WPF) sẽ tương tác với FocusManager để xác định điều khiển nào hiện

đang nhận focus.

Qua ví dụ trên, ta cũng thấy một đặc điểm quan trọng của lệnh trong WPF đó là: Lệnh không tự

động thực thi logic hành động. Lệnh trong WPF đơn thuần chỉ thông báo cho các phần tử UI biết

rằng có một lệnh có ngữ nghĩa như thế đang được phát động - Bản thân phần tử UI phải triển

khai/hiện thực hoá logic hành động phản ứng lại. Việc tách bạch giữa lệnh và logic thực hiện lệnh là

một điểm mạnh. Như ta thấy qua ví dụ trên, lệnh Delete được phát động từ cùng nguồn (Delete

menu), nhưng việc cài đặt được thực hiện riêng cho 2 đối tượng hoàn toàn khác nhau.

Page 124: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team| WPF – Bài 6: Xử lý sự kiện và lệnh trong WPF 19

2.5 Lệnh tự tạo

Tự tạo các lệnh của riêng bạn trong nhiều trường hợp là cần thiết. Việc này cũng không quá

phức tạp trong WPF. Để làm được điều này, lớp lệnh tự tạo phải hiện thực hoá giao diện

ICommand. Tuy nhiên, ta có thể dùng lớp RoutedUICommand là lớp có sẵn trong framework đã

hiện thực hoá tốt giao diện ICommand. Ví dụ, sau đây là cách tạo nên một lệnh cho phép chèn thêm

một khách hàng.

Trong file code-behind C#, ta tạo một lớp mới có tên là MyCommands chứa một biến public

kiểu RoutedUICommand. Lớp này được đặt trong cùng namespace với đối tượng Window chính.

Đoạn mã ví dụ như sau:

C#

namespace Lesson6

{

public static class MyCommands

{

static MyCommands()

{

InsertCustomer = new RoutedUICommand(

"Insert Customer", "InsertCustomer",

typeof(MyCommands));

}

public readonly static RoutedUICommand InsertCustomer;

}

}

Trong file .xaml, ta thêm một mục menu trên form:

XAML

<MenuItem

Command="local:MyCommands.InsertCustomer"

Header="Insert Customer" />

Page 125: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team| WPF – Bài 6: Xử lý sự kiện và lệnh trong WPF 20

Sau đó, ta thêm một CommandBinding cho lệnh mới này cho Window chính:

<Window.CommandBindings>

<CommandBinding

Command="local:MyCommands.InsertCustomer"

CanExecute="InsertCustomer_CanExecute"

Executed="InsertCustomer_Executed" />

</Window.CommandBindings>

Lưu ý ở đây, ta phải thay đổi một chút phần khai báo Window chính trong file xaml, cụ thể

là thêm dòng:

xmlns:local="clr-namespace:Lesson6"

Dòng này có nhiệm vụ chỉ ra đường dẫn logic đến lớp MyCommands trong namespace,

trong ví dụ là Lesson6, dưới tên tham chiếu là local. Nhờ đó, việc gán thuộc tính Command cho

mục menu hay Window.Binding mới thực hiện được

(Command="local:MyCommands.InsertCustomer"):

<Window x:Class="Lesson6.Window3"

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

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

xmlns:local="clr-namespace:Lesson6"

Title="Lesson6" Height="300" Width="300"

>

Cuối cùng, ta cài đặt thêm các phương thức trong CommandBinding:

private void InsertCustomer_CanExecute(object sender,

CanExecuteRoutedEventArgs e)

{

e.CanExecute = true;

}

private void InsertCustomer_Executed(object sender,

ExecutedRoutedEventArgs e)

{

Page 126: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team| WPF – Bài 6: Xử lý sự kiện và lệnh trong WPF 21

ListBoxItem item = new ListBoxItem();

item.Content = "New Customer";

lsbCustomers.Items.Add(item);

}

Page 127: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team| WPF – Bài 6: Xử lý sự kiện và lệnh trong WPF 22

Câu hỏi Ôn tập

1. Để xử lý một sự kiện cần thực hiện những công việc gì?

A. Viết mã lệnh thực hiện các hành động phản ứng với sự kiện

B. Kết nối sự kiện với hàm xử lý sự kiện

C. Cả hai công việc trên

Trả lời: C

2. Một sự kiện định tuyến có thể là:

A. Sự kiện truyền xuống

B. Sự kiện truyền lên

C. Sự kiện trực tiếp

D. Một trong ba phương án a, b, c, tuỳ thuộc vào chiến lược dẫn tuyến

của sự kiện đó

E. Có thể đồng thời hai trong 3 phương án a, b, c

Trả lời: D

3. Một sự kiện định tuyến có thể lan truyền:

A. Từ phần tử nguồn tới phần tử bất kỳ trên cây trực quan

B. Lan truyền theo một trong hai hướng: từ phần từ nguồn đến nút gốc

hoặc từ phần tử nguồn đến các nút con của nó

C. Chỉ lan truyền (ngược hay xuôi) qua các phần từ nằm trong đoạn từ

nút gốc tới phần tử nguồn mà có quan hệ họ hàng với phần tử nguồn.

Trả lời: C

4. Với mô hình sự kiện có định tuyến, một sự kiện lan truyền xuống được:

A. Lan truyền từ phần tử nguồn lên phần tử gốc trong cây trực quan

B. Lan truyền từ nút gốc đến phần tử nguồn trong cây trực quan

C. Lan truyền từ phần tử nguồn xuống các nút con trong cây trực quan

Page 128: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team| WPF – Bài 6: Xử lý sự kiện và lệnh trong WPF 23

Trả lời: C

5. Khi gắn kết một lệnh với một đối tượng chịu tác động của lệnh, việc thực

hiện lệnh sẽ do:

A. Bản thân lệnh đó tự thực thi hành động tương ứng với ngữ nghĩa của

nó, người lập trình không phải tác động thêm gì

B. Việc gắn kết chỉ có tác dụng thiết lập việc phát thông báo cho đối

tượng chịu tác động lệnh biết nó được ra lệnh gì mỗi khi lệnh được gọi, còn người

lập trình phải viết mã lệnh thực thi lệnh đó như thế nào

C. Nguồn phát lệnh xác định việc thực thi hành động

Trả lời: B

6. Ưu điểm của việc sử dụng lệnh có định tuyến so với xử lý sự kiện có định

tuyến:

A. Nguồn lệnh (nơi phát động lệnh) không bó chặt với đích lệnh (nơi xử

lý lệnh) – chúng không cần các tham chiếu trực tiếp lẫn nhau như trong trường hợp

liên kết bằng đơn vị xử lý sự kiện

B. Lệnh có định tuyến sẽ tự động cho phép hoặc vô hiệu hoá tất cả các

điều khiển UI tương ứng khi đích lệnh xác định rằng lệnh đó bị vô hiệu hoá

C. Lệnh có định tuyến cho phép ta liên kết phím nóng và các dạng nhập

liệu khác như cơ chế phát động lệnh

D. Cả ba ưu điểm trên.

Trả lời: D

7. Trong mô hình lệnh có định tuyến, một khi một đơn vị xử lý lệnh đã được

kích hoạt thực hiện:

A. Giống như sự kiện có định tuyến, lệnh lại được lan truyền tiếp, do

vậy, có thể có nhiều đơn vị xử lý lệnh khác sẽ được thực hiện

B. Không đơn vị xử lý nào khác được gọi

C. Còn tuỳ lệnh đó có được đánh dấu “đã xử lý” hay chưa

Page 129: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team| WPF – Bài 6: Xử lý sự kiện và lệnh trong WPF 24

Trả lời: B

Tài liệu tham khảo

1. Routed Events Overview, http://msdn.microsoft.com/en-us/library/ms742806.aspx

2. Event Handlers in WPF, http://www.kirupa.com/net/event_handlers_pg1.htm

3. Overview of routed events in WPF, http://joshsmithonwpf.wordpress.com/2007/06/22/overview-

of-routed-events-in-wpf/

4. Introduction to the WPF Command Framework,

http://www.devx.com/DevX/Article/37893/0/page/3

5. Commanding Overview, http://msdn.microsoft.com/en-us/library/ms752308.aspx

6. Understanding Routed Events and Commands In WPF,

http://msdn.microsoft.com/en-us/magazine/cc785480.aspx

Page 130: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team | WPF – Bài 7: Kiểu hiển thị (Style) và khuôn mẫu (Template) 1

Bài 7

Kiểu hiển thị (Style) và Khuôn mẫu (Template)

WPF giới thiệu hai khái niệm là Kiểu hiển thị (Style) và Khuôn mẫu (Template) cho phép xây dựng

các mẫu thuộc tính hiển thị áp dụng chung cho nhiều đối tượng UI trên giao diện người dùng. Bài giảng này

tập trung giới thiệu hai khái niệm này và cách sử dụng chúng thông qua các ví dụ cụ thể.

1. Giới thiệu về Kiểu hiển thị (Style)

Thông thường, khi xây dựng một giao diện đồ hoạ, ta thường thiết lập cùng giá trị các thuộc tính

hiển thị trên nhiều đối tượng UI khác nhau. Ví dụ, bạn muốn đặt tất cả các tiêu đề (Label) trong ứng dụng với

phông chữ “Times New Roman”, cỡ 14px, in đậm. Điều này có thể thực hiện dễ dàng với CSS trong một ứng

dụng Web, nhưng không đơn giản đối với WinForm. WPF nhận ra sự cần thiết này và giải quyết bằng việc

đưa ra thành phần „Style‟.

Thành phần „Style‟ cho phép người lập trình lưu trữ một danh sách các giá trị thuộc tính vào một nơi

thuận tiện. Nó tương tự như cách làm việc của CSS trong các ứng dụng Web. Thông thường, các Style được

lưu trữ trong phần Resource hoặc một thư mục Resource riêng của project. Các thuộc tính quan trọng nhất

của thành phần Style bao gồm BasedOn, TargetType, Setters và Triggers.

Được xem như một loại tài nguyên, Style có thể được định nghĩa ở bất kỳ phân cấp nào trong cây

trực quan, ví dụ cho một StackPanel, Window hoặc thậm chí ở mức Application. Việc đặt khai báo Style lẫn

với các mã chức năng XAML thường dễ gây nhầm lẫn khi mở rộng ứng dụng. Lời khuyên ở đây là không đặt

khai báo Style trong App.xaml hay các file chức năng xaml, mà lưu chúng trong một file xaml tài nguyên

riêng. Lưu ý rằng các tài nguyên có thể được chia nhỏ thành các file độc lập sao cho các file ảnh như jpeg có

thể được lưu trữ riêng rẽ.

Một khi đã chia thành các file tài nguyên riêng thì vấn đề tiếp theo sẽ là việc làm sao để tìm tham

chiếu tới tài nguyên bạn cần. Ở đây, ta dùng một giá trị khoá duy nhất: Khi định nghĩa một tài nguyên trong

XAML, bạn định nghĩa một giá trị khoá duy nhất cho tài nguyên đó thông qua thuộc tính x:Key. Kể từ sau

đó, bạn có thể tham chiếu tới tài nguyên này bằng việc sử dụng giá trị này.

Sau đây, các thuộc tính quan trọng trong Style sẽ được lần lượt giới thiệu.

Page 131: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team | WPF – Bài 7: Kiểu hiển thị (Style) và khuôn mẫu (Template) 2

1.1. Các thành phần thuộc tính trong Style

1.1.1 BasedOn

Thuộc tính này giống như tính chất kế thừa, trong đó, một Style kế thừa thuộc tính chung của một

Style khác. Mỗi kiểu hiện thị chỉ hỗ trợ một giá trị BaseOn. Sau đây là một ví dụ nhỏ:

<!--Khai báo Style được kế thừa-->

<Style x:Key="Style1">

...

</Style>

<!--Khai báo Style kế thừa-->

<Style x:Key="Style2" BasedOn="{StaticResource Style1}">

...

</Style>

1.1.2 TargetType

Thuộc tính TargetType được sử dụng để giới hạn loại điều khiển nào được sử dụng Style đó. Ví dụ

nếu ta có một Style với thuộc tính TargetType thiết lập cho nút bấm (Button), thì Style này sẽ không thể áp

dụng cho kiểu điều khiển TextBox. Cách thiết lập thuộc tính này minh họa trong ví dụ sau:

<Style TargetType="{x:Type Button}">

....

</Style>

1.1.3 Setters

Setters cho phép thiết lập một sự kiện hay một thuộc tính với một giá trị nào đó. Trong trường hợp

thiết lập một sự kiện, chúng liên kết với một sự kiện và kích hoạt hàm xử lý tương ứng. Trong trường hợp

thiết lập một thuộc tính, chúng đặt giá trị cho thuộc tính đó.

Sau đây là một ví dụ về việc sử dụng EventSetters để liên kết sự kiện, trong đó, sự kiện nhắm chuột

vào nút bấm (Click) được liên kết:

Page 132: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team | WPF – Bài 7: Kiểu hiển thị (Style) và khuôn mẫu (Template) 3

<Style TargetType="{x:Type Button}">

<EventSetter Event="Click" Handler="b1SetColor"/>

</Style>

Tuy nhiên, Setter thường được dùng để thiết lập giá trị thuộc tính hơn cả. Ví dụ:

<!--Đặt thuộc tính màu vàng cho nền nút bấm-->

<Style TargetType="{x:Type Button}">

<Setter Property="BackGround" Value="Yellow"/>

</Style>

1.1.4 Triggers

Mô hình thiết lập kiểu hiển thị và khuôn mẫu của WPF cho phép bạn định ra các Trigger bên trong

Style của bạn. Trigger là đối tượng cho phép bạn áp dụng những thay đổi về thuộc tính giao diện khi những

điều kiện nhất định (ví dụ khi một giá trị Property nào đó bằng true, hoặc một sự kiện nào đó xảy ra) được

thoả mãn.

Ví dụ sau đây minh hoạ một Style có định danh được áp dụng cho điều khiển Button. Style này định

nghĩa một thành phần Trigger, có tác dụng thay đổi thuộc tính màu chữ của nút bấm khi thuộc tính IsPressed

(nút đang bị bấm xuống) là true.

<Style x:Key="Triggers" TargetType="Button">

<Style.Triggers>

<Trigger Property="IsPressed" Value="true">

<Setter Property = "Foreground" Value="Green"/>

</Trigger>

</Style.Triggers>

</Style>

Một số dạng khác của Trigger sử dụng trong Style:

DataTrigger

DataTrigger Đại diện cho một Trigger áp dụng cho giá trị thuộc tính hoặc thực hiện hành động khi

dữ liệu liên kết thoả mãn một điều kiện định trước. Trong ví dụ sau, DataTrigger được xác định sao cho nếu

Page 133: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team | WPF – Bài 7: Kiểu hiển thị (Style) và khuôn mẫu (Template) 4

như giá trị Tỉnh trong mục dữ liệu Nơi làm việc bằng “HN” thì màu chữ của mục dữ liệu tương ứng trong

ListBox được tô đỏ:

<Style TargetType="ListBoxItem">

<Style.Triggers>

<DataTrigger Binding="{Binding Path=Tinh}" Value="HN">

<Setter Property="Foreground" Value="Red" />

</DataTrigger>

</Style.Triggers>

</Style>

Có một loại Trigger đặc biệt sử dụng nhiều hơn một giá trị để kích hoạt hoạt động, có tên gọi là

Multitrigger. Với loại Trigger này ta có thể thiết lập nhiều điều kiện trong một Trigger. Ví dụ:

<Style TargetType="ListBoxItem">

<Style.Triggers>

<MultiDataTrigger>

<MultiDataTrigger.Conditions>

<Condition Binding="{Binding Path=TenCongViec}" Value="CNTT" />

<Condition Binding="{Binding Path=Tinh}" Value="HN" />

</MultiDataTrigger.Conditions>

<Setter Property="Background" Value="Cyan" />

</MultiDataTrigger>

</Style.Triggers>

</Style>

Trong ví dụ này, đối tượng dữ liệu buộc với điều khiển phải có TenCongViec=”CNTT” và

Tinh=”HN”, thì màu chữ của mục dữ liệu tương ứng trên ListBox được tô đỏ.

EventTrigger

EventTrigger là loại Trigger đặc biệt áp dụng cho một tập các hành động tương ứng với một sự kiện.

Các EventTrigger đặc biệt ở chỗ chúng chỉ cho phép các hành động hoạt họa được kích hoạt. Chúng không

cho phép các thuộc tính bình thường được thiết lập làm cơ sở như đối với các Trigger khác. Sau đây là một

ví dụ của EventTrigger:

<EventTrigger RoutedEvent="Mouse.MouseEnter">

<EventTrigger.Actions>

Page 134: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team | WPF – Bài 7: Kiểu hiển thị (Style) và khuôn mẫu (Template) 5

<BeginStoryboard>

<Storyboard>

<DoubleAnimation

Duration="0:0:0.2"

Storyboard.TargetProperty="MaxHeight"

To="90" />

</Storyboard>

</BeginStoryboard>

</EventTrigger.Actions>

</EventTrigger>

<EventTrigger RoutedEvent="Mouse.MouseLeave">

<EventTrigger.Actions>

<BeginStoryboard>

<Storyboard>

<DoubleAnimation

Duration="0:0:1"

Storyboard.TargetProperty="MaxHeight" />

</Storyboard>

</BeginStoryboard>

</EventTrigger.Actions>

</EventTrigger>

1.2 Một ví dụ đầy đủ về sử dụng Style

Sau đây là một ví dụ đầy đủ về việc sử dụng Style. Trong ví dụ minh hoạ này, hai Style được định

nghĩa cho panel chính. Style thứ nhất quy định các thuộc tính tĩnh về phông chữ, áp dụng đối với đối tượng

UI là Control. Style thứ hai kế thừa các thuộc tính này từ Style thứ nhất và chỉ áp dụng cho Label. Style thứ

hai quy định thêm phản ứng của các đối tượng là Label trong StackPanel khi con trỏ chuột lướt qua, cụ thể,

màu chữ sẽ chuyển đỏ. Sau đây là mã XAML tương ứng:

<Window x:Class="Lesson7.Window1"

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

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

Title="Lesson7 - Using Styles" Height="300" Width="300"

>

<!--Sử dụng Stack Panel làm Panel chính-->

Page 135: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team | WPF – Bài 7: Kiểu hiển thị (Style) và khuôn mẫu (Template) 6

<StackPanel>

<!--Khai báo tài nguyên trong StackPanel-->

<StackPanel.Resources>

<!--Trong trường hợp này, tài nguyên là hai Style:-->

<!--(1) Style quy định về kiểu phông chữ, áp dụng với Control-->

<Style x:Key="baseStyle" TargetType="{x:Type Control}">

<Setter Property="FontFamily" Value="Times New Roman" />

<Setter Property="FontSize" Value="12" />

<Setter Property="FontStyle" Value="Italic" />

<Setter Property="HorizontalAlignment" Value="Center" />

</Style>

<!--(2) Style kế thừa từ Style trước, quy định phản ứng với sự kiện

-->

<Style BasedOn="{StaticResource baseStyle}" TargetType="{x:Type

Label}">

<!--Khai báo trigger-->

<Style.Triggers>

<!--Sự kiện khi con trỏ chuột lướt qua-->

<Trigger Property="IsMouseOver" Value="True">

<Setter Property="Foreground" Value="Red" />

</Trigger>

</Style.Triggers>

</Style>

</StackPanel.Resources>

<!--Kết thúc khai báo tài nguyên-->

<!--Khai báo phần tử trên giao diện-->

<Label>Lũ chúng ta ngủ trong giường chiếu hẹp, </Label>

<Label>Giấc mơ con đè nát cuộc đời con, </Label>

<Label>Hạnh phúc đựng trong một tà áo đẹp, </Label>

<Label>Một mái nhà yên rủ bóng xuống tâm hồn </Label>

<TextBlock>-Chế Lan Viên-</TextBlock>

</StackPanel>

</Window>

Kết quả là:

Page 136: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team | WPF – Bài 7: Kiểu hiển thị (Style) và khuôn mẫu (Template) 7

Hình 7.1 - Sử dụng Style

Chú ý khi áp dụng một Style được thiết lập giá trị x:Key cho một đối tượng UI cụ thể, ta

phải thiết lập thuộc tính Style trong khai báo đối tượng đó. Ví dụ:

<Style x:Key="TitleText" TargetType="{x:Type TextBlock}">

<Setter Property="FontFamily" Value="Times New Roman" />

<Setter Property="FontSize" Value="12" />

<Setter Property="FontStyle" Value="Italic" />

<Setter Property="HorizontalAlignment" Value="Center" />

</Style>

<TextBlock Style="{StaticResource TitleText}">Đoạn text có áp dụng

Style</TextBlock>

<TextBlock>Đoạn text không áp dụng Style</TextBlock>

Trong ví dụ trên, chỉ TextBlock có thiết lập thuộc tính Style tham chiếu đến Style có giá trị

khoá TitleText (Style="{StaticResource TitleText}") mới chịu tác dụng của Style này. Style

trong TextBlock còn lại là ngầm định.

2. Giới thiệu về Khuôn mẫu (Template)

Bằng việc sử dụng Style, ta có thể tạo ra một diện mạo nhất quán và dễ sửa đổi cho giao

diện ứng dụng. Tuy nhiên, đôi khi bạn muốn đi xa hơn. Chẳng hạn, bạn muốn các nút bấm không

phải là hình chữ nhật như thường lệ mà là hình ellipse. Hay bạn muốn hiển thị một tập dữ liệu nhân

viên trong một công ty, trong đó, mỗi bản ghi nhân viên lại được trình bày theo một định dạng xác

Page 137: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team | WPF – Bài 7: Kiểu hiển thị (Style) và khuôn mẫu (Template) 8

định. Bạn không thể đạt được điều này bằng những Setter căn bản trong Style. Trong trường hợp

đó, bạn phải dùng đến khái niệm gọi là Khuôn mẫu (Template).

Trong WPF, có hai dạng khuôn mẫu được sử dụng: ControlTemplate dùng để định lại cấu

trúc hiển thị cho điều khiển UI; và DataTemplate dùng để định ra cách thức hiển thị dữ liệu. Phần

sau đây sẽ trình bày lần lượt hai dạng khuôn mẫu này.

2.1 ControlTemplate

2.1.1 ControlTemplate là gì?

Phần lớn các điều khiển đều bao gồm diện mạo và hành vi. Xét một nút bấm: diện mạo của

nó là vùng nổi lên mà ta có thể bấm vào, trong khi hành vi là sự kiện Click được phát động để phản

ứng với hành động nhắp chuột vào nút bấm đó.

Đôi khi có những điều khiển cung cấp các hành vi mà ta cần nhưng lại không có diện mạo

mà ta mong muốn. Tới giờ, chúng ta có thể dùng các Setter của thành phần Style để thiết lập các giá

trị thuộc tính có ảnh hưởng tới diện mạo của điều khiển. Tuy nhiên, để thay đổi cấu trúc của một

điều khiển hoặc thiết lập giá trị thuộc tính cho các component có chứa một điều khiển, ta cần dùng

đến ControlTemplate.

Trong WPF, ControlTemplate của một điều khiển định nghĩa diện mạo cho điều khiển đó.

Bạn có thay đổi cấu trúc hay diện mạo của một điều khiển bằng cách định nghĩa một

ControlTemplate mới cho dạng điều khiển đó. Trong trường hợp bạn không định nghĩa riêng một

ControlTemplate cho điều khiển của bạn, thì một template ngầm định phù hợp với giao diện chung

của hệ thống sẽ được sử dụng, giống như những gì ta nhìn thấy đối với một nút bấm truyền thống.

Một điều cần nhớ là khi bạn tạo một ControlTemplate cho điều khiển, bạn đang thay thế

toàn bộ ControlTemplate của điều khiển đó. Ví dụ, bạn có thể định nghĩa ControlTemplate cho điều

khiển Button như sau:

<Style TargetType="Button">

<!--Đặt giá trị true để không sử dụng bất kỳ giá trị thuộc tính nào

của theme hệ thống-->

Page 138: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team | WPF – Bài 7: Kiểu hiển thị (Style) và khuôn mẫu (Template) 9

<Setter Property="OverridesDefaultStyle" Value="True"/>

<!--Thiết lập khuôn dạng mẫu cho điều khiển Button-->

<Setter Property="Template">

<Setter.Value>

<ControlTemplate TargetType="Button">

<Grid>

<Ellipse Fill="Navy"/>

<!--Đánh dấu nơi bắt đầu đặt nội dung của Button: chính

giữa-->

<ContentPresenter HorizontalAlignment="Center"

VerticalAlignment="Center"/>

</Grid>

</ControlTemplate>

</Setter.Value>

</Setter>

</Style>

Khi áp dụng, nút bấm sẽ có dạng như một hình Ellipse:

Hình 7.2 – Tạo một điều khiển Button có dạng hình Ellipse sử dụng ControlTemplate

Chú ý rằng diện mạo của Button khi nó nhận được focus hoặc được bấm hiện thời đều thuộc

vào phần diện mạo ngầm định của nút bấm mà ta đã thay thế. Vì ta đã thiết lập thuộc tính

OverridesDefaultStyle bằng true, mọi thuộc tính ngầm định đều bị bỏ qua. Do đó, nếu cần thay

đổi diện mạo của Button khi nhận được focus hay bị bấm, ta phải định nghĩa lại diện mạo trong

những trường hợp này.

2.1.2 Một ví dụ về sử dụng ControlTemplate

Page 139: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team | WPF – Bài 7: Kiểu hiển thị (Style) và khuôn mẫu (Template) 10

Trong phần này, chúng ta cùng xây dựng một ControlTemplate định nghĩa một ListBox mà

trong đó, các chỉ mục được sắp xếp theo chiều ngang (thay vì chiều dọc như thông thường) và có

các góc được uốn cong. Sau đây là đoạn mã XAML minh hoạ:

<Window x:Class="Lesson7.Window3"

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

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

Title="Lesson7 - ControlTemplate Example 02" Height="300" Width="300"

>

<StackPanel>

<!--Khai báo tài nguyên của panel chính-->

<StackPanel.Resources>

<!--Sử dụng một Style để chứa khai báo ControlTemplate-->

<Style TargetType="ListBox">

<!--Khai báo một Setter của thuộc tính Template-->

<Setter Property="Template">

<Setter.Value>

<!--Khai báo định nghĩa ControlTemplate-->

<ControlTemplate TargetType="ListBox">

<!--Khai báo bán kính uốn góc và màu nền-->

<Border CornerRadius="5" Background="Orange">

<ScrollViewer HorizontalScrollBarVisibility="Auto">

<!--Sử dụng một StackPanel sắp xếp theo chiều ngang-->

<StackPanel Orientation="Horizontal"

VerticalAlignment="Center"

HorizontalAlignment="Center"

IsItemsHost="True"/>

</ScrollViewer>

</Border>

</ControlTemplate>

</Setter.Value>

</Setter>

</Style>

</StackPanel.Resources>

<!--Kết thúc khai báo tài nguyên-->

Page 140: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team | WPF – Bài 7: Kiểu hiển thị (Style) và khuôn mẫu (Template) 11

<!--Khai báo ListBox-->

<ListBox Width="250" Height="50">

<ListBoxItem>Mục dữ liệu 01</ListBoxItem>

<ListBoxItem>Mục dữ liệu 02</ListBoxItem>

<ListBoxItem>Mục dữ liệu 03</ListBoxItem>

<ListBoxItem>Mục dữ liệu 04</ListBoxItem>

<ListBoxItem>Mục dữ liệu 05</ListBoxItem>

</ListBox>

</StackPanel>

</Window>

Theo cách trên, bạn xây dựng một ControlTemplate thông qua sử dụng một Style, cụ thể là

khai báo trong một Setter cho thuộc tính Template. Một cách khác nữa là bạn có thể gán trực tiếp

thuộc tính Template của một điều khiển cho một ControlTemplate. Với cách này, ControlTemplate

cần dùng phải được xây dựng trước, trong phần Resourse chẳng hạn, và được gán khoá định danh

thông qua x:Key, và sau đó được sử dụng như một tài nguyên tĩnh (khai báo StaticResource).

Như bạn có thể thấy trong ví dụ trên, lớp ControlTemplate cũng có thuộc tính TargetType

như đối với lớp Style. Tuy nhiên, cần lưu ý rằng, nếu ta xây dựng một ControlTemplate độc lập, với

thuộc tính TargetType được thiết lập cho một kiểu điều khiển nào đó, thì ControlTemplate đó

không được tự động áp dụng cho kiểu điều khiển này. Cũng lưu ý rằng thuộc tính TargetType là bắt

buộc trong một khai báo ControlTemplate nếu như template đó có chứa thành phần

ContentPresenter.

Trong ví dụ trên, một thuộc tính quan trọng cần có là IsItemsHost. Thuộc tính IsItemsHost

được sử dụng để xác định đây là template của một điều khiển chứa các mục con, và các mục con sẽ

được sắp xếp trong đó. Thiết lập thuộc tính này bằng true trong StackPanel có nghĩa là bất kỳ một

mục nào được thêm vào ListBox sẽ được xếp vào StackPanel. Chú ý thuộc tính này chỉ có trong

kiểu Panel.

Kết quả của đoạn mã trên như minh họa trong hình 7.3.

Page 141: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team | WPF – Bài 7: Kiểu hiển thị (Style) và khuôn mẫu (Template) 12

Hình 7.3 – Một ví dụ về xây dựng và sử dụng một ControlTemplate cho ListBox

2.2 DataTemplate

2.2.1 DataTemplate là gì?

DataTemplate được sử dụng để định ra cách thức hiển thị các đối tượng dữ liệu. Đối tượng

DataTemplate đặc biệt hữu dụng khi bạn móc nối một điều khiển chứa mục con (ItemsControl) kiểu

như ListBox với một danh mục dữ liệu. Không có sự định hướng cụ thể, một ListBox sẽ ngầm định

hiển thị các đối tượng trong danh sách dưới dạng chuỗi ký tự. Với việc sử dụng DataTemplate,

chúng ta có thể định khuôn dạng hiển thị của mỗi mục con trong ListBox với nhiều đặc tính trực

quan như màu sắc, hình ảnh, phông chữ…

2.2.2 Một ví dụ sử dụng DataTemplate

Trong ví dụ này, thông tin về các nhân viên trong một văn phòng được hiển thị sử dụng

DataTemplate. Trước hết, ta phải định nghĩa nguồn dữ liệu, cụ thể ở đây là danh sách nhân viên.

Để làm điều này, đầu tiên, ta xây dựng lớp nhân viên (Person), đơn giản bao gồm họ tên

(Name) và ảnh chân dung (ImageRef). Sau đây là mã C#:

namespace Lesson7{

/**

* Định nghĩa lớp thành phần dữ liệu Person

*/

public class Person

{

public Person(string name, string imageRef)

Page 142: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team | WPF – Bài 7: Kiểu hiển thị (Style) và khuôn mẫu (Template) 13

{

this.Name = name;

this.ImageRef = imageRef;

}

private string _name;

public string Name

{

get { return _name; }

set { _name = value; }

}

private string _imageRef;

public string ImageRef

{

get { return _imageRef; }

set { _imageRef = value; }

}

}

}

Tiếp theo, ta xây dựng lớp chứa danh sách nhân viên, giả sử có tên Staffs. Mã lệnh C#

như sau:

namespace Lesson7{

public class Staffs

{

private List<Person> staffs;

public IEnumerable<Person> StaffList

{

get { return staffs; }

}

public Staffs()

{

staffs = new List<Person>();

staffs.Add(new Person("Mary", "mary.jpg"));

Page 143: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team | WPF – Bài 7: Kiểu hiển thị (Style) và khuôn mẫu (Template) 14

staffs.Add(new Person("Johny", "johny.jpg"));

staffs.Add(new Person("Olaf", "olaf.jpg"));

staffs.Add(new Person("Scooby Doo", "scooby_doo.jpg"));

}

}

}

Như đã thấy, lớp Staffs thực chất chứa đựng một danh sách có kiểu Person (biến staffs). Đối

tượng của lớp này khi được tạo lập sẽ khởi tạo một danh sách định trước gồm 4 nhân viên có tên và

đường dẫn ảnh tương ứng (Mary, Johny, Olaf và Scooby Doo). Danh sách nhân viên có thể truy

nhập thông qua thuộc tính StaffList của lớp Staffs.

Tiếp theo, ta xây dựng giao diện chính bằng mã XAML:

<Window x:Class="Lesson7.Window5"

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

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

xmlns:local="clr-namespace:Lesson7"

Title="Lesson 7 - WPF DataTemplate Example" Height="360" Width="530"

WindowStartupLocation="CenterScreen">

<Window.Resources>

<!--Định nghĩa nguồn dữ liệu-->

<local:Staffs x:Key="MyStaffList"/>

</Window.Resources>

<StackPanel>

<StackPanel.Resources>

<!--Định nghĩa cách hiển thị mục dữ liệu thông qua một file xaml

riêng rẽ-->

<ResourceDictionary>

<ResourceDictionary.MergedDictionaries>

<ResourceDictionary Source="/StaffDataTemplate.xaml" />

</ResourceDictionary.MergedDictionaries>

</ResourceDictionary>

</StackPanel.Resources>

Page 144: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team | WPF – Bài 7: Kiểu hiển thị (Style) và khuôn mẫu (Template) 15

<!-- The Person-items -->

<ItemsControl x:Name="personItems"

HorizontalAlignment="Stretch"

Margin="10"

VerticalAlignment="Center"

Background="Orange"

ItemsSource="{Binding Source={StaticResource MyStaffList},

Path=StaffList}"

/>

</StackPanel>

</Window>

Như đã thấy, phần tử UI chính được dùng trên giao diện là một ItemsControl, loại điều

khiển cho phép hiển thị nhiều mục hiển thị con trong nó. Nội dung các mục hiển thị được gắn kết

với một nguồn dữ liệu là thuộc tính StaffList của một đối tượng thuộc lớp Staffs có tên MyStaffList

(ItemsSource="{Binding Source={StaticResource MyStaffList}, Path=StaffList}"). Đối

tượng dữ liệu này được khai báo trong phần Resources của Window (<local:Staffs

x:Key="MyStaffList"/>).

Trong khi đó, việc sử dụng DataTemplate để quy định cách thức hiển thị của mỗi mục dữ

liệu trong StaffList lại được đặt trong một file .xaml riêng rẽ có tên StaffDataTemplate.xaml

(<ResourceDictionary Source="/StaffDataTemplate.xaml" />). Trong trường hợp này, mặc

dù hoàn toàn có thể định nghĩa DataTemplate trực tiếp trong mỗi điều khiển hay trong Resources

của Window trong cùng một file .xaml, việc định nghĩa DataTemplate trong một file riêng như thế

này cho phép ta dễ dàng sửa đổi và tái sử dụng DataTemplate ở nhiều form khác nhau mà không

phải sao chép lại mã lệnh. Sau đây là mã XAML trong file StaffDataTemplate.xaml:

<ResourceDictionary

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

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

xmlns:local="clr-namespace:Lesson7" >

<!-- Định nghĩa cách thức hiển thị cho mỗi mục dữ liệu nhân viên -->

<DataTemplate DataType="{x:Type local:Person}">

Page 145: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team | WPF – Bài 7: Kiểu hiển thị (Style) và khuôn mẫu (Template) 16

<DataTemplate.Resources>

<!--Khai báo một đối tượng chuyển đổi đường dẫn ảnh thành ảnh-->

<local:PersonImageConverter x:Key="imageConverter" />

</DataTemplate.Resources>

<StackPanel Background="Orange" Orientation="Horizontal">

<!-- Hiển thị Hình ảnh -->

<Image Margin="10" Width="60" Height="60"

Source="{Binding Path=ImageRef,

Converter={StaticResource imageConverter}}">

<Image.BitmapEffect>

<DropShadowBitmapEffect />

</Image.BitmapEffect>

</Image>

<!-- Hiển thị Tên -->

<TextBlock x:Name="personName"

Text="{Binding Name}"

Padding="15,15"

Foreground="Black" />

</StackPanel>

</DataTemplate>

</ResourceDictionary>

DataTemplate được định nghĩa ở đây chỉ được dùng với kiểu dữ liệu thuộc lớp Person

(DataType="{x:Type local:Person}"). Cách thức hiển thị tên nhân viên được khai báo trong

một phần tử TextBlock mà thuộc tính Text được liên kết với thuộc tính Name của mỗi đối tượng

Person (Text="{Binding Name}"). Trong khi đó, cách thức hiển thị ảnh của nhân viên được khai

báo trong một phần tử Image, trong đó, phần nguồn ảnh được liên kết với thuộc tính ImageRef

(Source="{Binding Path=ImageRef,Converter={StaticResource imageConverter}}"). Ở

đây, để hiển thị hình ảnh của nhân viên thay vì đường dẫn tới ảnh, ta xây dựng một lớp chuyển đổi

có tên PersonImageConverter. Mã lệnh C# cho lớp này như sau:

Page 146: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team | WPF – Bài 7: Kiểu hiển thị (Style) và khuôn mẫu (Template) 17

namespace Lesson7{

public class PersonImageConverter : IValueConverter

{

#region IValueConverter Members

/// <summary>

/// Hàm chuyển đổi từ đường dẫn ảnh sang đối tượng Bitmap

/// </summary>

public object Convert(object value, Type targetType,

object parameter, System.Globalization.CultureInfo culture)

{

string imageName = value.ToString();

Uri uri = new Uri(imageName, UriKind.RelativeOrAbsolute);

BitmapFrame source = BitmapFrame.Create(uri);

return source;

}

public object ConvertBack(object value, Type targetType,

object parameter, System.Globalization.CultureInfo culture)

{

throw new NotImplementedException();

}

#endregion

}

}

Lưu ý các ảnh được đặt trong thư mục chứa file chạy của chương trình.

Kết quả là:

Page 147: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team | WPF – Bài 7: Kiểu hiển thị (Style) và khuôn mẫu (Template) 18

Hình 7.4 – Hiển thị danh sách nhân viên sử dụng DataTemplate

Câu hỏi Ôn tập

1. Thành phần Style quy định các thuộc tính hiển thị định sẵn có thể được áp

dụng cho:

a. Chỉ một đối tượng điều khiển UI duy nhất

b. Tất cả các đối tượng thuộc một lớp UI cụ thể (ListBox, TextBox, vv)

quy định bởi thuộc tính TargetType trong Style đã định (trong trường hợp thành

phần Style không được đặt khóa định danh x:Key)

c. Các đối tượng UI thuộc các lớp khác nhau nhưng cùng kế thừa từ một

lớp UI (ví dụ, Control) quy định bởi thuộc tính TargetType trong thành phần Style đã

định; và có thuộc tính Style tham chiếu đến khóa của Style đã định (trường hợp Style

có đặt khóa định danh x:Key)

d. b hoặc c tương ứng với từng trường hợp Style đã định có khóa định

danh x:Key hay không

Page 148: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team | WPF – Bài 7: Kiểu hiển thị (Style) và khuôn mẫu (Template) 19

Trả lời: d

2. Thuộc tính Triggers trong thành phần Style quy định:

a. Hàm xử lý sự kiện tương ứng với một sự kiện xảy ra trên đối tượng

UI có áp dụng Style

b. Những thay đổi về thuộc tính hiển thị khi những điều kiện nhất định trên đối

tượng UI có áp dụng Style được thoả mãn

Trả lời: b

3. Sử dụng thuộc tính BaseOn, một Style có thể kế thừa từ:

a. Duy nhất một Style cơ sở

b. Nhiều Style cơ sở tùy thuộc vào giá trị thiết lập cho BaseOn

Trả lời: a

4. Loại Trigger nào sau đây cho phép thiết lập nhiều điều kiện cho một dạng thay

đổi thuộc tính hiển thị:

a. EventTrigger

b. MultiTrigger

c. DataTrigger

Trả lời: b

5. Sự khác biệt giữa Style và ControlTemplate áp dụng cho một lớp đối tượng

UI là gì?

Trả lời: Style cho phép thiết lập giá trị các thuộc tính hiển thị sẵn có (màu nền,

phông chữ, vân vân) của một lớp đối tượng điều khiển UI, trong khi ControlTemplate

cho phép quy định lại cấu trúc hiển thị của lớp đối tượng UI (hình dạng, cách sắp xếp

các phần tử con, vân vân).

6. Trong một ứng dụng WPF, khai báo Style và Template có thể được đặt ở

đâu?

Page 149: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team | WPF – Bài 7: Kiểu hiển thị (Style) và khuôn mẫu (Template) 20

a. Đặt trong phần Resources của bất kỳ mức phân cấp nào trong cây trực

quan trong cùng file .xaml khai báo giao diện

b. Trong một file .xaml riêng và được tham chiếu trong file giao diện có

sử dụng Style hoặc Template thông qua khai báo đường dẫn trong phần tử

<ResourceDictionary>

c. Một trong hai phương án trên

Trả lời: c

Tài liệu Tham khảo

1. WPF Styles and Templates,

http://homepage.ntlworld.com/herring1/styletemp.html

2. WPF: A Beginner's Guide - Part 6 of n,

http://www.codeproject.com/KB/WPF/BeginWPF6.aspx

3. WPF Styles and Control Templates, http://en.csharp-

online.net/WPF_Styles_and_Control_Templates

4. .NET 3.0 Crash Course - Part 6: WPF Styles and Control Templates,

http://devlicio.us/blogs/rob_eisenberg/archive/2006/12/03/net-3-0-crash-course-part-6-wpf-

styles-and-control-templates.aspx

5. WPF DataBinding, Styles and DataTemplates,

http://www.codegod.de/WebAppCodeGod/wpf-databinding-styles-and-datatemplates-

AID406.aspx

6. Data Templating Overview, http://msdn.microsoft.com/en-

us/library/ms742521.aspx

Page 150: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team | WPF – Bài 8: Đồ họa hai chiều trong WPF (2D –

Graphics)

1

Bài 8

Đồ họa hai chiều trong WPF (2D-Graphics)

Trước đây, để xây dựng một ứng dụng đồ họa đẹp, hiện thị các đối tượng đồ họa với những hiệu

ứng và chuyển động người lập trình phải mất nhiều công sức. Với WPF các công việc trên trở nên đơn

giản hơn nhiều, bởi vì WPF đã tích hợp sẵn đồ họa vector, đa phương tiện, hình ảnh động (animation) và

các đối tượng đồ họa phức hợp. Các đối tượng đồ họa trong WPF không chỉ để hiển thị một các đơn

thuần, chúng còn có khả năng phát sinh các sự kiện mà thông thường chỉ có trong các điều khiển thông

dụng của Window. Lập trình viên có thể xây dựng các ứng dụng đồ họa đẹp, sinh động và thú vị với

Microsoft Visual Studio .NET hay thậm chí chỉ cần sử dụng NotePad.

Bài này giới thiệu về cách xây dựng các đối tượng đồ họa như đoạn thẳng, chuỗi đoạn thẳng, đa

giác,.. với các cách thức tô vẽ phong phú, đẹp mắt cũng như các hiệu ứng dịch chuyển bằng mã lệnh

XAML.

1. Các đối tượng đồ họa cơ bản - Shape

Để bắt đầu, chúng ta sẽ tìm hiểu các mã lệnh XAML để hiển thị các đối tượng đồ họa cơ bản như

Line (đoạn thẳng), Ellipse (hình elip), Polygon (đa giác), Polyline (chuỗi đoạn thẳng), Rectangle (chữ

nhật) và Path (hình phức hợp). Các đối tượng này được kế thừa từ đối tượng cơ sở Shape. Các đối tượng

kế thừa từ Shape có chung một số thuộc tính như:

Stroke: Mô tả màu sắc đường viền của một hình hoặc màu của một đoạn thẳng.

StrokeThickness: Độ dày của đường viền.

Fill: Cách tô phần bên trong của một hình.

Data: Mô tả các tọa độ, các đỉnh của một hình, đơn vị đo là pixel.

1.1 Đoạn thẳng (Line)

Đoạn thẳng là một đối tượng được định nghĩa dựa trên hai đầu mút là hai điểm. Chúng ta có thể

định nghĩa độ dày của đoạn thẳng, màu sắc hay cách vẽ đoạn thẳng (nét liền, nét đứt..). Hình dưới đây

minh họa một số ví dụ về đoạn thẳng

Page 151: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team | WPF – Bài 8: Đồ họa hai chiều trong WPF (2D –

Graphics)

2

Mã lệnh XAML của ví dụ trên như sau.

Đoạn mã trình của hai đoạn thẳng trên bằng XAML:

<Window x:Class="Lession08_Graphics.Window1"

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

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

Title="Vi du ve Shape" Height="338" Width="324">

<Canvas Height="300" Width="300">

<!-- Vẽ một đoạn thẳng nằm xiên từ tọa độ (10,10) tới (50,50).

Độ dày đoạn thẳng là 4 pixel mà có màu đen

-->

<Line

X1="10" Y1="10"

X2="50" Y2="50"

Stroke="Black"

StrokeThickness="4" />

<!-- Vẽ một đoạn thẳng nằm ngang từ tọa độ (10,50) to (150,50).

nằm cách lề trái của canvas 100 pixel

Đoạn thẳng màu xanh da trời, độ dày 4 pixel,

nét đứt xen kẽ cứ mỗi đoạn màu xanh là 4 thì lại xen khoảng

trắng là 1.

-->

<Line

X1="10" Y1="50"

X2="150" Y2="50"

Đoạn thẳng nét liền màu

đen có độ dày là 4 pixel

Hình 8.1. Ví dụ về đoạn thẳng

Đoạn thẳng nét đứt

màu da trời có độ dày

là 4 pixel.

Page 152: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team | WPF – Bài 8: Đồ họa hai chiều trong WPF (2D –

Graphics)

3

Canvas.Left="100"

Stroke="Blue"

StrokeThickness="4"

StrokeDashArray="4 1" />

</Canvas>

</Window>

Thông thường ta hay chọn layout là Canvas để chứa các đối tượng đồ họa bởi vì Canvas cho

phép đặt các đối tượng bên trong theo vị trí tuyệt đối.

Trong ví dụ trên thẻ <Line/> dùng để định nghĩa một đoạn thẳng. Thẻ này có một số thuộc

tính cơ bản:

X1="10" Y1="10" :Tọa độ đỉnh thứ nhất là X=10 và Y = 10

StrokeThickness="4" : Độ dày của đoạn thẳng là 4 pixel

X2="50" Y2="50" :Tọa độ đỉnh thứ hai là X=50 và Y = 50

Stroke="Black" : Màu của đoạn thẳng là màu đen

StrokeThickness="4" : Độ dày của đoạn thẳng là 4 pixel

StrokeDashArray="4 1":Đoạn thẳng được tô theo nét đứt, cứ 4 pixel có

màu thì 1 pixel là khoảng trắng.

Đoạn mã trình C# vẽ đoạn thẳng.

// Add a Line Element

myLine = new Line();

myLine.Stroke = System.Windows.Media.Brushes.LightSteelBlue;

myLine.X1 = 1;

myLine.X2 = 50;

myLine.Y1 = 1;

myLine.Y2 = 50;

myLine.HorizontalAlignment = HorizontalAlignment.Left;

myLine.VerticalAlignment = VerticalAlignment.Center;

myLine.StrokeThickness = 2;

myGrid.Children.Add(myLine);

Page 153: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team | WPF – Bài 8: Đồ họa hai chiều trong WPF (2D –

Graphics)

4

1.2 Chuỗi đoạn thẳng (Polyline)

Polyline là đối tượng bao gồm nhiều đoạn thẳng liên tiếp nối với nhau. Một Polyline gồm N đoạn

thẳng thì được định nghĩa bới N+1 điểm.

Hình dưới minh họa hai Polyline, một Polyline gồm ba đoạn và một Polyline gồm hai đoạn.

Để hiển thị một Polyline bằng mã lệnh, tạo một đối tượng Polyline và sử dụng thuộc tính Points

của nó để khai báo tọa độ của các đỉnh. Tiếp đến, có thể sử dụng các thuộc tính Stroke và

StrokeThickness để mô tả màu sắc và độ dày của Polyline.

Đối với mã XAML, cú pháp khai báo dãy các điểm là: mỗi cặp tọa độ X,Y phân biệt với nhau

bởi khoảng trống và giữa X với Y phân biệt bởi dấu phẩy.

Chú ý rằng, đối tượng Polyline cũng có thuộc tính Fill để tô màu bên trong, nhưng thuộc tính này

không có tác dụng. Nếu muốn tô màu cho vùng bên trong của tập hợp các điểm thì sử dụng đối tượng

Polygon.

Đoạn mã sau minh họa mã lệnh XAML của ví dụ này.

<Window x:Class="Lession08_Graphics.Window1"

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

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

Title="Vi du ve Shape" Height="338" Width="324">

<Canvas Height="300" Width="300">

<!--Vẽ một chuỗi đoạn thẳng gồm ba đoạn nối tiếp nhau

Hình 8.2. Ví dụ về Polyline

Polyline gồm ba đoạn

thẳng màu đen, nét liền

Polyline gồm hai đoạn

thẳng màu da trời, nét đứt

4-1-2-1

Page 154: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team | WPF – Bài 8: Đồ họa hai chiều trong WPF (2D –

Graphics)

5

được nối bởi bốn đỉnh (X,Y) = (10,110) (60,10) (110,110) và

(160,110)-->

<Polyline

Points="10,110 60,10 110,110 160,110"

Stroke="Black"

StrokeThickness="4" Canvas.Left="0" Canvas.Top="80" />

<!--Vẽ một chuỗi đoạn thẳng gồm hai đoạn nối tiếp nhau với nét đứt

được nối bởi ba đỉnh (X,Y) = (10,110) (110,110) và (110,10)-->

<Polyline

Points="10,110 110,110 110,10"

Stroke="Blue"

StrokeThickness="4"

StrokeDashArray="4 1 2 1"

Canvas.Left="180" Canvas.Top="80" />

</Canvas>

</Window>

Thẻ <Polyline/> được sử dụng để tạo Polyline.

Thuộc tính Points="X1,Y1 X2,Y2 X3,Y3 X4,Y4" khai báo tập hợp các điểm tạo nên

Polyline.

Thuộc tính StrokeDashArray="4 1 2 1" có nghĩa là Polyline được vẽ bằng nét đứt theo thứ

tự 4 nét màu 1 nét trắng tiếp đến là 2 nét màu mà 1 nét trắng, và tiếp tục lặp lại…

1.3 Hình chữ nhật (Rectangle)

Đối tượng Rectangle được xác định bởi tọa độ của góc trên trái và độ rộng, độ cao của hình chữ

nhật cần hiển thị. Ngoài ra, ta có thể thiết lập các thuộc tính cho đường viền (màu sắc, độ dày, kiểu dáng)

và tô phần bên trong của hình.

Page 155: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team | WPF – Bài 8: Đồ họa hai chiều trong WPF (2D –

Graphics)

6

Đoạn mã sau minh họa mã lệnh XAML của ví dụ này.

<Window x:Class="Lession08_Graphics.Window1"

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

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

Title="Vi du ve Shape" Height="338" Width="324">

<Canvas Height="300" Width="300" Background="AntiqueWhite">

<!-- Vẽ hình chữ nhật không có đường viền, được tô màu #CCCCFF-

->

<Rectangle

Width="100"

Height="50"

Fill="#CCCCFF"

Canvas.Left="10"

Canvas.Top="25" />

<!-- Vẽ hình chữ nhật có đường viền màu đen độ dày 4 pixel,

được tô màu #CCCCFF-->

<Rectangle

Width="100"

Height="50"

Fill="#CCCCFF"

Stroke="Black"

StrokeThickness="4"

Canvas.Left="10"

Hình 8.3. Ví dụ về hình chữ nhật

Hình chữ nhật không

có đường viền

Hình chữ nhật không

có đường viền và

cạnh vát tròn

Hình chữ nhật có

đường viền nét đứt

Page 156: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team | WPF – Bài 8: Đồ họa hai chiều trong WPF (2D –

Graphics)

7

Canvas.Top="100"/>

<!-- Vẽ hình chữ nhật các góc vát tròn không có đường viền,

được tô màu #CCCCFF-->

<Rectangle

Width="100"

Height="50"

RadiusX="20"

RadiusY="20"

Fill="#CCCCFF"

Canvas.Left="135"

Canvas.Top="25"/>

<!-- Vẽ hình chữ nhật các góc vát tròn có đường viền màu đen

độ dày 4 pixel, không được tô màu-->

<Rectangle

Width="100"

Height="50"

RadiusX="20"

RadiusY="20"

Stroke="Black"

StrokeThickness="4"

Canvas.Left="135"

Canvas.Top="100" />

<!-- Vẽ hình chữ nhật có đường viền nét đứt màu đen độ dày 4

pixel,

được tô màu #CCCCFF-->

<Rectangle

Width="100"

Height="50"

Fill="#CCCCFF"

Stroke="Black"

StrokeThickness="4"

StrokeDashArray="4 2"

Canvas.Left="10"

Canvas.Top="180"/>

<!-- Vẽ hình chữ nhật các góc vát tròn có đường viền nét đứt

màu đen

độ dày 4 pixel, không được tô màu-->

<Rectangle

Width="100"

Height="50"

RadiusX="20"

Page 157: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team | WPF – Bài 8: Đồ họa hai chiều trong WPF (2D –

Graphics)

8

RadiusY="20"

Stroke="Black"

StrokeThickness="4"

StrokeDashArray="2 1"

Canvas.Left="135"

Canvas.Top="180" />

</Canvas>

</Window>

Thẻ <Rectangle/> dùng để vẽ một hình chữ nhật.

Các thuộc tính Canvas.Left, Canvas.Top, Width, Height chỉ định tọa độ góc trên trái và

độ rộng, độ cao của hình chữ nhật.

Thuộc tính Fill chỉ định màu tô bên trong hình chữ nhật. nếu bỏ qua thuộc tính này thì hình chữ

nhật sẽ là trong suốt.

Các thuộc tính Stroke, StrokeThickness, StrokeDashArray chỉ định kiểu đường viền

của hình chữ nhật. Nếu không chỉ định giá trị cho các thuộc tính này thì hình chữ nhật sẽ không có đường

viền.

Các thuộc tính RadiusX, RadiusY là bán kính của hình ellipse để tạo ra các góc tròn của hình

chữ nhật.

1.4 Hình elip (Ellipse) và hình tròn (Circle)

Hình Ellipse được xác định bởi tọa độ của góc trên trái và độ rộng, độ cao của hình chữ nhật

ngoại tiếp của Ellipse cần hiển thị. Hình tròn là hình Ellipse với chiều rộng và chiều cao bằng nhau.

Ellipse cũng có các thuộc tính cho đường viền (màu sắc, độ dày, kiểu dáng) và tô phần bên trong của

hình tương tự như hình chữ nhật.

Page 158: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team | WPF – Bài 8: Đồ họa hai chiều trong WPF (2D –

Graphics)

9

Đoạn mã sau minh họa mã lệnh XAML của ví dụ này.

<Window x:Class="Lession08_Graphics.Window1"

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

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

Title="Vi du ve Shape" Height="338" Width="324">

<Canvas Height="300" Width="300" Background="AntiqueWhite">

<!-- Vẽ hình Ellipse được tô màu Blue. -->

<Ellipse

Width="100"

Height="50"

Fill="Blue"

Canvas.Left="10"

Canvas.Top="25" />

<!-- Vẽ hình Ellipse được tô màu Blue và viền đen. -->

<Ellipse

Width="100"

Height="50"

Fill="Blue"

Stroke="Black"

StrokeThickness="4"

Canvas.Left="10"

Canvas.Top="100"/>

Hình 8.4. Ví dụ về hình Ellipse

Hình Ellipse không

có đường viền

Hình tròn không có

đường viền.

Hình Ellipse có

đường viền nét đứt

Page 159: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team | WPF – Bài 8: Đồ họa hai chiều trong WPF (2D –

Graphics)

10

<!-- Vẽ hình Ellipse được tô màu Blue và viền đen nét đứt. -->

<Ellipse

Width="100"

Height="50"

Fill="Blue"

Stroke="Black"

StrokeThickness="4"

StrokeDashArray="2 1"

Canvas.Left="10"

Canvas.Top="180"/>

<!-- Vẽ hình tròn được tô màu Blue. -->

<Ellipse

Width="50"

Height="50"

Fill="Blue"

Canvas.Left="135"

Canvas.Top="25"/>

<!-- Vẽ hình tròn rỗng có viền đen. -->

<Ellipse

Width="50"

Height="50"

Stroke="Black"

StrokeThickness="4"

Canvas.Left="135"

Canvas.Top="100" />

<!-- Vẽ hình tròn rỗng có viền đen nét đứt. -->

<Ellipse

Width="50"

Height="50"

Stroke="Black"

StrokeThickness="4"

StrokeDashArray="2 1"

Canvas.Left="135"

Canvas.Top="180" />

</Canvas>

</Window>

Page 160: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team | WPF – Bài 8: Đồ họa hai chiều trong WPF (2D –

Graphics)

11

Thẻ <Ellipse /> dùng để vẽ một hình Ellipse hay hình tròn.

Các thuộc tính Canvas.Left, Canvas.Top, Width, Height chỉ định tọa độ góc trên trái và

độ rộng, độ cao của hình chữ nhật ngoại tiếp của Ellipse. Nếu Width và Height bằng nhau thì ta sẽ có

hình tròn.

Thuộc tính Fill chỉ định màu tô bên trong hình Ellipse. nếu bỏ qua thuộc tính này thì hình

Ellipse sẽ là trong suốt.

Các thuộc tính Stroke, StrokeThickness, StrokeDashArray chỉ định kiểu đường viền

của hình Ellipse. Nếu không chỉ định giá trị cho các thuộc tính này thì hình Ellipse sẽ không có đường

viền.

1.5 Đa giác (Polygon)

Polygon là đối tượng dùng để trình diễn các hình dạng phức tạp, gồm đoạn thẳng nối tiếp khép

kín. Một Polygon N đỉnh được định nghĩa bởi một tập hợp N cặp tọa độ tương ứng với mỗi đỉnh của nó.

Hình dưới minh họa một số Polygon dạng tam giác và lục giác.

Polygon cũng có các thuộc tính tương tự như Polyline như: thuộc tính Points của nó để khai báo

tọa độ của các đỉnh, các thuộc tính Stroke và StrokeThickness để mô tả màu sắc và độ dày đường viền

Polyline. Tuy nhiên, đối tượng Polygon còn có tính Fill để tô màu bên trong đa giác.

Hình 8.5. Ví dụ về Polygon

Các hình

Tam giác

Hình Lục giác

Page 161: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team | WPF – Bài 8: Đồ họa hai chiều trong WPF (2D –

Graphics)

12

Đối với mã XAML, cú pháp khai báo dãy các điểm là: mỗi cặp tọa độ X,Y phân biệt với nhau

bởi khoảng trống và giữa X với Y phân biệt bởi dấu phẩy.

Đoạn mã sau minh họa mã lệnh XAML của ví dụ này.

<Window x:Class="Lession08_Graphics.Window1"

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

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

Title="Vi du ve Shape" Height="374" Width="637">

<Canvas Height="335" Width="616" Background="AntiqueWhite">

<!-- Vẽ một hình tam giác tô màu Blue và không có đường viền --

>

<Polygon Points="10,110 60,10 110,110"

Fill="Blue" />

<!-- Vẽ một hình tam giác tô màu Blue, có đường viền màu đen

dày 4 pixel

Thuộc tính Canvas.Top dùng để dịch tam giác xuống 150 pixels so

với

đỉnh Canvas. -->

<Polygon Points="10,110 60,10 110,110"

Fill="Blue"

Stroke="Black" StrokeThickness="4"

Canvas.Top="150" />

<!-- Vẽ một hình tam giác tô màu Blue và không có đường viền.

Thuộc tính Canvas.Left dùng để dịch tam giác sang phải 150

pixels. -->

<Polygon Points="10,110 110,110 110,10"

Fill="Blue"

Canvas.Left="150" />

<!-- Vẽ một hình tam giác có đường viền màu đen không tô màu.

vị trí của tam giác cách canh trái và đỉnh của Canvas 150

pixel.-->

<Polygon Points="10,110 110,110 110,10"

Stroke="Black" StrokeThickness="4"

Canvas.Left="150" Canvas.Top="150" />

<!-- Vẽ một hình lục giác màu nền là Gold,đường viền màu đen.-->

<Polygon Name="hexagon"

Stroke="Blue"

StrokeThickness="2.0"

Page 162: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team | WPF – Bài 8: Đồ họa hai chiều trong WPF (2D –

Graphics)

13

Fill="Gold"

Points="176,30 302.44,103 302.44,249 176,322 49.5603,249

49.5603,103"

Canvas.Left="280" Canvas.Top="0" />

</Canvas>

</Window>

Thẻ <Polygon /> được sử dụng để tạo đa giác.

Thuộc tính Points="X1,Y1 X2,Y2 X3,Y3 X4,Y4" khai báo tập hợp các đỉnh của đa giác.

1.6 Đường cong Bezier bằng đối tượng Path

Đối tượng Path được sử dụng để tạo nên những hình phức tạp, gồm nhiều phần nối với nhau. Ví

dụ dưới đây minh họa sử dụng đối tượng Path tạo nên một hình gồm một đường cong Bezier, cuối

đường cong là một đoạn thẳng nối ngược trở lại điểm đầu.

Đoạn mã sau minh họa mã lệnh XAML của ví dụ này.

<Window x:Class="Lession08_Graphics.Window1"

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

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

Title="Vi du ve Shape" Height="319" Width="418">

<Canvas Height="280" Width="393" Background="AntiqueWhite">

<!-- Vẽ một Path gồm hai phần:

- Đường cong Bezier từ tọa độ (10,100) tới (300,100)

tọa độ hai điểm điều khiển là (100,0) và (200,200)

Hình 8.6. Ví dụ về Path

Một Path gồm đường cong

Bezier và một đoạn thẳng nối

ngược về điểm đầu.

Một Path gồm đường cong

Bezier và một đoạn thẳng nối

ngược về điểm giữa. Hình này

được tô bên trong bởi màu Blue.

Page 163: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team | WPF – Bài 8: Đồ họa hai chiều trong WPF (2D –

Graphics)

14

- Tại điểm kết thúc (300,100) vẽ một đường nằm ngang

ngược về điểm khởi đầu.

-->

<Path

Data="M 10,100 C 100,0 200,200 300,100 z"

Stroke="Black"

StrokeThickness="4" />

<!-- Vẽ một Path các đỉnh Canvas 100 pixel gồm hai phần:

- Đường cong Bezier từ tọa độ (10,100) tới (300,100)

tọa độ hai điểm điều khiển là (100,0) và (200,200).

- Tại điểm kết thúc (300,100) vẽ một đường nằm ngang

ngược về điểm có tọa độ X = 150.

-->

<Path

Data="M 10,100 C 100,0 200,200 300,100 H 150"

Stroke="Black"

StrokeThickness="4"

Fill="Blue"

Canvas.Top="100"/>

</Canvas>

</Window>

Thẻ <Path /> được sử dụng để tạo đường cong Bezier.

Thuộc tính Data="M 10,100 C 100,0 200,200 300,100 z" khai báo các thông số tạo nên

một Path. Trong đó M 10,100 nghĩa là đường cong bắt đầu từ điểm có tọa độ (10,100) tính theo hệ tọa

độ của Canvas chứa Path này. Các thông số của thuộc tính Data có phân biệt chữ hoa, chữ thường. Nếu

là chữ hoa thì tọa độ điểm được tính theo vị trí tuyệt đối, chữ thường thì tọa độ được tính theo vị trí tương

đối. Ví dụ, M khai báo tọa độ điểm bắt đầu của Path tính theo vị trí tuyệt đối, còn nếu thay bằng m thì sẽ

hiểu là vị trí tương đối. Ký tự C dùng để khai báo hai điểm điều khiển (Control Point) của đường cong.

Ví dụ với C 100,0 200,200 thì hai điểm điều khiển sẽ có tọa độ là (100,0) và (200,200). Sau hai

điểm điều khiển là điểm kết thúc của đường cong, trong ví dụ trên, tọa độ điểm kết thúc là (300,100).

Đoạn thứ hai của Path trong ví này là một đường kẻ ngang nối từ điểm kết thúc tới điểm khởi đầu

của đường cong nhờ tham số z. Nếu muốn đặt đường kẻ này tới một điểm nào đó trên đường nằm ngang

thì ta thay tham số z bằng tham số H. Trong ví dụ ở hình thứ 2 ta có tham số H 150 nghĩa là đường kẻ

ngang bắt đầu từ điểm cuối của đường cong và kết thúc ở điểm có tọa độ X = 150 tính theo vị trí tuyệt

đối (nếu là chữ thường h thì nghĩa là vị trí tương đối).

Page 164: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team | WPF – Bài 8: Đồ họa hai chiều trong WPF (2D –

Graphics)

15

2. Sử dụng chổi tô - Brush

Tất cả những gì chúng nhìn thấy trên màn hình, chúng hiển thị được là nhờ được tô bởi chối tô

(Brush). Chối tô có thể sử dụng để tô nền của một nút bấm (Button), tô các nét chữ (Text) hay tô màu

bên trong cho một đối tượng hình học như hình chữ nhật, đa giác,... Trong phần này chúng ta sẽ tìm hiểu

cách sử dụng chổi tô trong WPF bằng mã lệnh XAML để tô các đối tượng hình học theo nhiều cách

khách nhau như tô mầu đồng nhất (Solid Color), tô kiểu đổ màu theo tuyến tính (Linear Gradient

Color), tô đổ màu dọc theo bán kính hình tròn (Radial Gradient Color) và sử dụng ảnh bitmap để tô.

Thao tác cơ bản để tô vẽ cho một đối tượng trước hết là tạo một đối tượng chổ tô (Brush) tùy

theo từng loại như trên, sau đó gắn chổi tô với thuộc tính có liên quan của đối tượng cần sử dụng chổi tô

này. Mỗi loại đối tượng có một số thuộc tính khác nhau để chỉ định tô màu cho phần bên trong của nó.

Bảng sau đây liệt kê một số loại đối tượng và thuộc tinh được dùng để gắn với chổi tô.

Loại đối tượng (Class) Thuộc tính tô vẽ (Brush properties)

Border BorderBrush, Background

Control Background, Foreground

Panel Background

Pen Brush

Tô đổ màu

theo tâm tròn

Hình 8.7. Minh họa một số kiểu tô hình ảnh

Tô màu

đồng nhất

Tô đổ màu theo

tuyến tính

Tô bằng ảnh

Bitmap

Page 165: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team | WPF – Bài 8: Đồ họa hai chiều trong WPF (2D –

Graphics)

16

Shape Fill, Stroke

TextBlock Background

2.1. Tô màu đồng nhất – Solid Color

Có nhiều cách để tô màu đồng nhất cho một đối tượng, ta có thể sử dụng trực tiếp thuộc tính tô

màu của đối tượng (đối tượng hình học sử dụng thuộc tính Fill, đối tương Button sử dụng thuộc tính

Background,..) hoặc tạo đối tượng tên là SolidColorBrush để định nghĩa màu sắc cần tô và gắn cho đối

tượng cần sử dụng chổi tô đồng nhất này.

Đối tượng SolidColorBrush sử dụng thuộc tính Color để chỉ định màu cần tô. Giá trị gắn cho

Color có thể là tên của mầu đã được định nghĩa sẵn (là các thuộc tính tĩnh của đối tượng Brush) như Red,

MediumBlue,.. hoặc sử dụng công thức biểu diễn màu theo dạng mẫu 32 bit “#RRGGBB”, trong đó RR

(giá trị của màu đỏ - Red), GG (giá trị của màu lá cây - Green), BB (giá trị của màu da trời – Blue) là các

số Hexa (hệ đếm cơ số 16) có giá trị nằm trong khoảng từ 00 đến FF ( tương đương với giá trị từ 0 đến

255) . Ngoài ra, có thẻ sử dụng mẫu “#AARRGGBB”, AA là độ Alpha dùng để chỉ độ trong suốt của

màu. Độ Alpha càng gần với 0 (00 hệ 16) thì màu càng trở nên trong suốt và càn gần với 255 (FF hệ 16)

thì mầu càng rõ nét.

Sau đây là ví dụ hiển thị một hình chữ nhật được tô đồng nhất màu đỏ.

Đoạn mã XAML của hình chữ nhật này như sau:

<!—-Sử dụng mã định nghĩa sẵn -->

<Rectangle Width="75" Height="75">

<Rectangle.Fill>

<SolidColorBrush Color="Red" />

</Rectangle.Fill>

</Rectangle>

<!—-Sử dụng mã mầu dạng #RRGGBB -->

<Rectangle Width="75" Height="75">

Page 166: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team | WPF – Bài 8: Đồ họa hai chiều trong WPF (2D –

Graphics)

17

<Rectangle.Fill>

<SolidColorBrush Color="#FF0000" />

</Rectangle.Fill>

</Rectangle>

<!—- Sử dụng trực tiếp thuộc tình Fill -->

<Rectangle Width="75" Height="75" Fill="#FF0000"/>

Đoạn mã lệnh C# tương đương như sau:

// Tạo đối tượng Rectangle để vẽ hình chữ nhật.

Rectangle exampleRectangle = new Rectangle();

exampleRectangle.Width = 75;

exampleRectangle.Height = 75;

// Tạo đối tượng SolidColorBrush dùng để tô màu cho hình chữ nhật.

SolidColorBrush myBrush = new SolidColorBrush(Colors.Red);

exampleRectangle.Fill = myBrush;

Ta cũng có thể tô màu cho các điều khiển nhờ thuộc tính Background. Ví dụ, tô màu cho nút bấm

<Button Background="#FFFF0000">Nút bấm</Button>

<!—- Hoặc -->

<Button> Nút bấm

<Button.Background>

<SolidColorBrush Color="Red" />

</Button.Background>

</Button>

2.2. Tô đổ màu theo định hướng tuyến tính – Linear

Gradient Color

Tô đổ màu (hay tản màu) là kỹ thuật tô một vùng bằng nhiều màu trộn với nhau dọc theo một

trục định hướng (Gradient axis). Ta có thể sử dụng kỹ thuật này để tạo nên các hình ảnh ấn tượng với độ

sáng hay độ bóng của màu tạo cảm giác ba chiều (3D) hoặc cũng có thể dùng kỹ thuật này để tạo ra các

giả lập bề mặt kính, nhôm, nước hay các bề mặt vật liệu mềm khác. WPF cung cấp hai loại đổ màu là đổ

màu theo hướng tuyến tính (đối tượng LinearGradientBrush) hay đổ màu dọc theo bán kính hình tròn

(đối tượng RadialGradientBrush).

Page 167: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team | WPF – Bài 8: Đồ họa hai chiều trong WPF (2D –

Graphics)

18

Đối tượng LinearGradientBrush dùng để tô một vùng theo kỹ thuật tản màu dựa trên kỹ thuật

nội suy các màu nằm giữa các cặp màu chỉ định dọc theo một trục (Gradient axis) dạng đường thẳng.

Hướng của đường thẳng chính là hướng đổ màu, ta phải chỉ định những màu cần xuất hiện tại các điểm

nằm trên đường thẳng này nhờ đối tượng GradientStop. Ta có thể chỉ định hướng đổ màu nằm ngang từ

trái qua phải, nằm dọc từ trên xuống, hay theo đường chéo bất kỳ. Mặc định thì hướng đổ màu sẽ theo

đường chéo từ góc trên bên trái tới góc dưới bên phải.

Dưới đây là một ví dụ vẽ một hình chữ nhật gồm bốn màu khác nhau được tô theo chế độ trộn

màu.

Mã lệnh XAML của hình trên như sau:

<!-- Hình chữ nhật được tô tản màu theo đường chéo với bốn màu. -->

<Rectangle Width="200" Height="100">

<Rectangle.Fill>

<LinearGradientBrush StartPoint="0,0" ="1,1">

<GradientStop Color="Yellow" Offset="0.0" />

<GradientStop Color="Red" Offset="0.25" />

<GradientStop Color="Blue" Offset="0.75" />

<GradientStop Color="LimeGreen" Offset="1.0" />

</LinearGradientBrush>

</Rectangle.Fill>

</Rectangle>

Mã lệnh C# tương ứng:

Rectangle diagonalFillRectangle = new Rectangle();

diagonalFillRectangle.Width = 200;

diagonalFillRectangle.Height = 100;

// Hình chữ nhật được tô tản màu theo đường chéo với bốn màu.

LinearGradientBrush myLinearGradientBrush =

new LinearGradientBrush();

myLinearGradientBrush.StartPoint = new Point(0, 0);

myLinearGradientBrush.EndPoint = new Point(1, 1);

myLinearGradientBrush.GradientStops.Add(

Page 168: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team | WPF – Bài 8: Đồ họa hai chiều trong WPF (2D –

Graphics)

19

new GradientStop(Colors.Yellow, 0.0));

myLinearGradientBrush.GradientStops.Add(

new GradientStop(Colors.Red, 0.25));

myLinearGradientBrush.GradientStops.Add(

new GradientStop(Colors.Blue, 0.75));

myLinearGradientBrush.GradientStops.Add(

new GradientStop(Colors.LimeGreen, 1.0));

// Sử dụng chổi tô để tô hình chữ nhật.

diagonalFillRectangle.Fill = myLinearGradientBrush;

Thuộc tính StartPoint và EndPoint của LinearGradientBrush để xác định điểm đầu và

điểm của của trục tô mỗi thuộc tính này được xác định bởi một cặp giá trị “x,y” . Trong đó, x và y là các

số thực (double) có giá trị từ 0 đến 1, tương ứng với 0% và 100% tính tương đối so với đỉnh trên trái của

hình chữ nhật cần vẽ (hay đỉnh của hình chữ nhật bao đối với các hình khác như Ellipse, Polygon,..).

Thẻ <GradientStop Color="Color value" Offset="m.n" /> để chỉ định các điểm

chốt nằm dọc theo trục tô. Thuộc tính Color xác định màu cần tô tại điểm chốt. Thuộc tính Offset

nhằm xác định vị trí của điểm chốt, giá trị này là một số thực nằm trong khoảng từ 0 đến 1, giá trị càng

gần 0 thì càng gần với điểm xuất phát StartPoint, giá trị gần với 1 thì càng gần với điểm kết thúc

EndPoint của trục tô. Hệ thống sẽ tự động nội suy các màu nằm giữa các cặp điểm chốt.

hình dưới đây minh họa các điểm chốt của ví dụ trên. Đường nét đứt chỉnh là trục tô (Gradient

Axis) còn vòng tròn là vị trí của điểm chốt (GradientStop).

Tọa độ các điểm StartPoint và EndPoint của trục tô ảnh hưởng đến hướng tô màu. Mặc định thì

giá trị của StartPoint là (0,0) và EndPoint là (1,1), nghĩa là trục tô là đường chéo của hình chữ nhật bao.

Page 169: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team | WPF – Bài 8: Đồ họa hai chiều trong WPF (2D –

Graphics)

20

Hình dưới đây minh họa các tô đổ màu theo chiều ngang gồm bốn màu.

Mã lệnh XAML của hình trên như sau:

<!-- Tô đổ màu hình chữ nhật theo hướng nằm ngang từ trái sang phải với

bốn màu -->

<Rectangle Width="200" Height="100">

<Rectangle.Fill>

<LinearGradientBrush StartPoint="0,0.5" EndPoint="1,0.5">

<GradientStop Color="Yellow" Offset="0.0" />

<GradientStop Color="Red" Offset="0.25" />

<GradientStop Color="Blue" Offset="0.75" />

<GradientStop Color="LimeGreen" Offset="1.0" />

</LinearGradientBrush>

</Rectangle.Fill>

</Rectangle>

Mã lệnh C# tương ứng:

Rectangle horizontalFillRectangle = new Rectangle();

horizontalFillRectangle.Width = 200;

horizontalFillRectangle.Height = 100;

// Tạo chổi tô theo chiều ngang với 4 điểm chốt.

LinearGradientBrush myHorizontalGradient =

new LinearGradientBrush();

myHorizontalGradient.StartPoint = new Point(0, 0.5);

Page 170: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team | WPF – Bài 8: Đồ họa hai chiều trong WPF (2D –

Graphics)

21

myHorizontalGradient.EndPoint = new Point(1, 0.5);

myHorizontalGradient.GradientStops.Add(

new GradientStop(Colors.Yellow, 0.0));

myHorizontalGradient.GradientStops.Add(

new GradientStop(Colors.Red, 0.25));

myHorizontalGradient.GradientStops.Add(

new GradientStop(Colors.Blue, 0.75));

myHorizontalGradient.GradientStops.Add(

new GradientStop(Colors.LimeGreen, 1.0));

// Dùng chổi tô để tô hình chữ nhật.

horizontalFillRectangle.Fill = myHorizontalGradient;

Tô hình chữ nhật theo chiều đứng

Mã lệnh XAML của hình trên như sau:

<!-- Tô đổ màu hình chữ nhật theo hướng từ trên xuống với bốn màu. -->

<Rectangle Width="200" Height="100">

<Rectangle.Fill>

<LinearGradientBrush StartPoint="0.5,0" EndPoint="0.5,1">

<GradientStop Color="Yellow" Offset="0.0" />

<GradientStop Color="Red" Offset="0.25" />

<GradientStop Color="Blue" Offset="0.75" />

<GradientStop Color="LimeGreen" Offset="1.0" />

</LinearGradientBrush>

</Rectangle.Fill>

</Rectangle>

Mã lệnh C# tương ứng:

Rectangle verticalFillRectangle = new Rectangle();

verticalFillRectangle.Width = 200;

verticalFillRectangle.Height = 100;

Page 171: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team | WPF – Bài 8: Đồ họa hai chiều trong WPF (2D –

Graphics)

22

// Tạo chổi tô theo chiều đứng với 4 điểm chốt.

LinearGradientBrush myVerticalGradient =

new LinearGradientBrush();

myVerticalGradient.StartPoint = new Point(0.5, 0);

myVerticalGradient.EndPoint = new Point(0.5, 1);

myVerticalGradient.GradientStops.Add(

new GradientStop(Colors.Yellow, 0.0));

myVerticalGradient.GradientStops.Add(

new GradientStop(Colors.Red, 0.25));

myVerticalGradient.GradientStops.Add(

new GradientStop(Colors.Blue, 0.75));

myVerticalGradient.GradientStops.Add(

new GradientStop(Colors.LimeGreen, 1.0));

// Dùng chổi tô để tô hình chữ nhật.

verticalFillRectangle.Fill = myVerticalGradient;

2.3. Tô đổ màu theo bán kính hình tròn – Radial Gradient

Kỹ thuật tô đổ màu theo bán kính hình tròn tương tự như kỹ thuật đổ màu tuyến tính, nhưng điểm

xuất phát bắt đầu từ tâm đường tròn và màu được lan dần ra ngoài cho tới biên của đường tròn, sử dụng

đối tượng tên là RadialGradientBrush. Các điểm chốt vẫn sử dụng đối tượng GradientStop như phần

trên.

Dưới đây là ví dụ minh họa cách tô đổ màu theo bán kính hình tròn

Page 172: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team | WPF – Bài 8: Đồ họa hai chiều trong WPF (2D –

Graphics)

23

Mã lệnh minh họa thanh trạng thái trên như sau:

<!-- Hình chữ nhật được tô bốn màu đổ từ trong ra ngoài -->

<Rectangle Width="200" Height="100">

<Rectangle.Fill>

<RadialGradientBrush

GradientOrigin="0.5,0.5" Center="0.5,0.5"

RadiusX="0.5" RadiusY="0.5">

<GradientStop Color="Yellow" Offset="0" />

<GradientStop Color="Red" Offset="0.25" />

<GradientStop Color="Blue" Offset="0.75" />

<GradientStop Color="LimeGreen" Offset="1" />

</RadialGradientBrush>

</Rectangle.Fill>

</Rectangle>

Mã lệnh C# của ví dụ trên như sau:

RadialGradientBrush myRadialGradientBrush = new RadialGradientBrush();

myRadialGradientBrush.GradientOrigin = new Point(0.5, 0.5);

myRadialGradientBrush.Center = new Point(0.5, 0.5);

myRadialGradientBrush.RadiusX = 0.5;

myRadialGradientBrush.RadiusY = 0.5;

myRadialGradientBrush.GradientStops.Add(

new GradientStop(Colors.Yellow, 0.0));

myRadialGradientBrush.GradientStops.Add(

new GradientStop(Colors.Red, 0.25));

myRadialGradientBrush.GradientStops.Add(

new GradientStop(Colors.Blue, 0.75));

myRadialGradientBrush.GradientStops.Add(

new GradientStop(Colors.LimeGreen, 1.0));

Rectangle myRectangle = new Rectangle();

myRectangle.Width = 200;

myRectangle.Height = 100;

myRectangle.Fill = myRadialGradientBrush;

Các thông số Center - Tọa độ tâm đường tròn, RadiusX - Bán kính ngang, RadiusY - Bán

kính dọc được nhật các giá trí số thực trong khoảng từ 0 đến 1 tương ứng với tỷ lệ khoảng cách tới đỉnh

trên trái của hình chữ nhật.

Page 173: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team | WPF – Bài 8: Đồ họa hai chiều trong WPF (2D –

Graphics)

24

Thông số GradientOrigin là tọa độ của điểm xuất phát. Các điểm chốt sẽ chạy từ điểm

GradientOrigin này, dọc theo đường kính hình tròn.

2.4. Tô bằng ảnh Bitmap – Radial Gradient

Ngoài các kỹ thuật tô màu cho các đối tượng hình học như trên, WPF còn hỗ trợ tô một vùng

bằng những hình ảnh có sẵn (ảnh Bitmap, JPG,..) một cách dễ dàng nhờ đối tượng ImageBrush. Ta chỉ

cần đưa tệp ảnh vào tài nguyên của Project và gắn đường dẫn anh cho thuộc tính ImageSource của đối

tượng ImageBrush, sau đó dùng chổi tô ImageBush để tô các đối tượng hình học (Shape), các điều

khiển (Control), Panel hay Text,….

Dưới đây là ví dụ minh họa tô màu cho hình chữ nhật bằng hình ảnh.

Page 174: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team | WPF – Bài 8: Đồ họa hai chiều trong WPF (2D –

Graphics)

25

Mã lệnh minh họa thanh trạng thái trên như sau:

<Window x:Class="Lession08_Graphics.Window1"

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

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

Title="Ví dụ chổi tô - Brush" Height="373" Width="372">

<Canvas Height="331" Width="343" Background="DimGray">

<!-- Tô hình chữ nhật bằng hình ảnh

File ảnh pinkcherries.jpg có kích thước 100x100 -->

<!-- Mặc định ảnh tự động co dãn trùng khít với hình

Do tỷ lệ chiều rộng và cao của vùng tô khác với tỷ lệ

của ảnh nên sẽ bị méo hình -->

<Rectangle Canvas.Left="10" Canvas.Top="10" Height="200"

Width="150"

Stroke="Black" >

<Rectangle.Fill>

<ImageBrush ImageSource="sampleImages\pinkcherries.jpg"

/>

</Rectangle.Fill>

</Rectangle>

<!-- Thuộc tính Stretch="None" để hiện thị ảnh với khích thước

gốc,

không tự co dãn -->

Ảnh co dãn

nhưng giữ

nguyên tỷ lệ

Hình 8.8. Minh họa tô một vùng bằng hình ảnh

Ảnh tự động

co dãn phủ kín

hình chữ nhật

Ảnh giữ nguyên

kích thước gốc

Sử dụng Viewport

và cách tô kiểu

xếp lợp

Page 175: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team | WPF – Bài 8: Đồ họa hai chiều trong WPF (2D –

Graphics)

26

<Rectangle Canvas.Left="180" Canvas.Top="10" Height="150"

Width="150"

Stroke="Black" >

<Rectangle.Fill>

<ImageBrush ImageSource="sampleImages\pinkcherries.jpg"

Stretch="None" />

</Rectangle.Fill>

</Rectangle>

<!-- Thuộc tính Stretch="Uniform" để co dãn ảnh khớp với vùng

nhưng giữ tỷ lệ ảnh gốc-->

<Rectangle Canvas.Left="10" Canvas.Top="220" Height="100"

Width="150"

Stroke="Black" >

<Rectangle.Fill>

<ImageBrush ImageSource="sampleImages\pinkcherries.jpg"

Stretch="Uniform"/>

</Rectangle.Fill>

</Rectangle>

<!-- Sử dụng thuộc tính Viewport để chỉ định khung nhìn cần tô

Thuộc tính TileMode="FlipXY" để làm cho các khung nhìn

tô kiểu xếp lợp đối xứng theo cả trục X và trục Y -->

<Rectangle Canvas.Left="180" Canvas.Top="170" Height="150"

Width="150"

Stroke="Black" >

<Rectangle.Fill>

<ImageBrush ImageSource="sampleImages\pinkcherries.jpg"

TileMode="FlipXY"

Viewport="0,0,25,25"

ViewportUnits="Absolute" />

</Rectangle.Fill>

</Rectangle>

</Canvas>

</Window>

Thuộc tính ImageSource của ImageBrush để chỉ định đường dẫn đến tệp hình ảnh.

Thuộc tính Stretch để chỉ định các co dãn hình khi tô vùng, giá trị mặc định của Stretch là

Fill. Thuộc tính này có các giá trị:

Page 176: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team | WPF – Bài 8: Đồ họa hai chiều trong WPF (2D –

Graphics)

27

None: Chổi tô không tự động co dãn hình.

Uniform: Chổi tô co dãn hình trùng khít với một chiều của vùng tô nhưng giữ

nguyên tỷ lệ của ảnh gốc.

UniformToFill: Chổi tô co dãn hình phủ kín vùng tô nhưng giữ nguyên tỷ lệ

của ảnh gốc.

Fill: Chổi tô co dãn hình phủ kín vùng tô, không giữ tỷ lệ ảnh. Nếu tỷ lệ hai

chiều của vùng tô khác với tỷ lệ hai chiều của ảnh thỉ ảnh tô sẽ bị méo.

Các giá trị thuộc tính Stretch được minh họa như hình dưới đây.

Thuộc tính TileMode chỉ định kiểu xếp lợp, gồm các giá trị sau:

None: Không xếp lợp.

Tile: Xếp lợp để phủ kín vùng cần tô.

FlipX: Xếp lợp để phủ kín vùng cần tô, hình được lật theo chiều ngang.

FlipY: Xếp lợp để phủ kín vùng cần tô, hình được lật theo chiều dọc.

FlipXY: Xếp lợp để phủ kín vùng cần tô, hình được lật theo cả hai chiều.

Các thuộc tình trên được minh họa bởi hình dưới.

Page 177: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team | WPF – Bài 8: Đồ họa hai chiều trong WPF (2D –

Graphics)

28

2.5. Thiết lập độ mờ của chổi tô - Opacity

Để tạo độ mờ (Opacity), WPF cung cấp thuộc tính Opacity áp dụng cho các đối tượng hình học

(Shape), hình ảnh hay RadientBrush,..

Ví dụ sau đây minh họa thiết lập độ mờ cho chổi tô.

Mã XAML của ví dụ trên như sau:

<Window x:Class="Lession08_Graphics.Window1"

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

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

Title="Ví dụ chổi tô - Brush" Height="285" Width="310">

<Canvas Height="243" Width="242">

<Rectangle Canvas.Left="10" Canvas.Top="10" Height="50"

Width="50"

Stroke="Black" Fill="Blue"/>

<Rectangle Canvas.Left="70" Canvas.Top="10" Height="50"

Width="50"

Stroke="Black" Fill="Green"/>

<Rectangle Canvas.Left="10" Canvas.Top="68" Height="50"

Width="50"

Stroke="Black" Fill="Yellow"/>

<Rectangle Canvas.Left="70" Canvas.Top="68" Height="50"

Width="50"

Hình 8.9. Minh họa chổi tô với đô mờ

Chữ nhật màu

đỏ được tô với

độ mờ là 0.75

Chổi tô Gradient với

độ mờ là 0.5

Chổi tô Image với

độ mờ là 0.3

Page 178: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team | WPF – Bài 8: Đồ họa hai chiều trong WPF (2D –

Graphics)

29

Stroke="Black" Fill="HotPink"/>

<!-- Hình chữ nhật được tô màu đỏ với độ Opacity = 0.75-->

<Rectangle Canvas.Left="28" Canvas.Top="35" Height="55"

Width="75"

Stroke="Black" Fill="Red" Opacity="0.75"/>

<!-- Hình chữ nhật tô đổ màu kiểu linear gradient. -->

<Rectangle Width="100" Height="50" Canvas.Left="133"

Canvas.Top="10"

Stroke="Black" StrokeThickness="1">

<Rectangle.Fill>

<LinearGradientBrush StartPoint="0,0"

EndPoint="0.75,0.75"

Opacity="1">

<GradientStop Color="Blue" Offset="0.0" />

<GradientStop Color="Black" Offset="0.5" />

<GradientStop Color="White" Offset="1" />

</LinearGradientBrush>

</Rectangle.Fill>

</Rectangle>

<Rectangle Canvas.Left="133" Canvas.Top="76" Height="50"

Width="100"

Stroke="Black" StrokeThickness="1" >

<Rectangle.Fill>

<LinearGradientBrush EndPoint="0.75,0.75"

StartPoint="0,0"

Opacity="0.5">

<GradientStop Color="Blue" Offset="0.0" />

<GradientStop Color="Black" Offset="0.5" />

<GradientStop Color="White" Offset="1" />

</LinearGradientBrush>

</Rectangle.Fill>

</Rectangle>

<Rectangle Canvas.Left="10" Canvas.Top="134" Height="100"

Width="100"

Stroke="Black" >

<Rectangle.Fill>

<ImageBrush ImageSource="sampleImages\pinkcherries.jpg"

/>

Page 179: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team | WPF – Bài 8: Đồ họa hai chiều trong WPF (2D –

Graphics)

30

</Rectangle.Fill>

</Rectangle>

<!-- ảnh được tô với độ mờ 0.3 -->

<Rectangle Canvas.Left="133" Canvas.Top="134" Height="100"

Width="100"

Stroke="Black" Opacity="0.3" >

<Rectangle.Fill>

<ImageBrush ImageSource="sampleImages\pinkcherries.jpg"

/>

</Rectangle.Fill>

</Rectangle>

</Canvas>

</Window>

Giá trị của Opacity là một số thực từ 0 đến 1. Nếu Opacity càng gần bằng 0 thì chổi tô là

trong suốt, giá trị càng gần một thị chổi tô càng rõ nét.

3. Biến đổi hình học – Transform

Trong phần này, chúng ta tìm hiểu cơ bản về các kỹ thuật biến đổi hình học với WPF như các

phép dịch chuyển (translate), phép co dãn (scale), phép lệch hình (skew), phép quay (rotate)…

WPF cung cấp một số lớp (class) để hỗ trợ cho công việc biến đổi hình học

Lớp (Class) Mô tả Minh họa

RotateTrans

form

Quay đối tượng theo

góc chỉ định bởi thuộc tính

Angle.

ScaleTransf

orm

Co dãn đối tượng

theo chiều ngang và dọc với

các thuộc tính ScaleX và

ScaleY.

Page 180: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team | WPF – Bài 8: Đồ họa hai chiều trong WPF (2D –

Graphics)

31

SkewTransf

orm

Làm lênh hình theo

các góc chỉ định bởi AngleX

và AngleY.

TranslateTr

ansform

Dịch chuyển đối

tượng tới vị trí chỉ định bởi X

và Y.

Khi biến đổi một đối tượng, ta không chỉ biến đổi bản thân nó mà còn biến đổi cả không gian tọa

độ chứa đối tượng đó. Mặc định, tâm của phép biến đổi thực hiện tại gốc của hệ tọa độ của đối tượng.

Ví dụ sau minh họa sử dụng phép quay để xoay một hình chữ nhật một góc 45 độ xung quanh

tâm (0,0) của trục tọa độ của đối tượng (chính là góc trên trái của hình chữ nhật)

Mã lệnh XAML như sau:

<Canvas Width="200" Height="200">

<Rectangle

Canvas.Left="100" Canvas.Top="100"

Width="50" Height="50"

Fill="RoyalBlue" Opacity="1.0">

<Rectangle.RenderTransform>

Page 181: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team | WPF – Bài 8: Đồ họa hai chiều trong WPF (2D –

Graphics)

32

<RotateTransform Angle="45" />

</Rectangle.RenderTransform>

</Rectangle>

</Canvas>

Ví dụ tiếp theo minh họa xoay hình chữ nhật một góc 45 độ quanh tâm của chính nó, có tọa độ là

(25,25).

Mã lệnh XAML như sau:

<Canvas Width="200" Height="200">

<Rectangle

Canvas.Left="100" Canvas.Top="100"

Width="50" Height="50"

Fill="RoyalBlue" Opacity="1.0">

<Rectangle.RenderTransform>

<RotateTransform Angle="45" CenterX="25" CenterY="25"/>

</Rectangle.RenderTransform>

</Rectangle>

</Canvas>

Không chỉ dừng ở việc biến đổi các đối tượng hình học, WPF còn cho phép biến đổi các điều

khiển (Control).

Ví dụ sau minh họa xoay một nút bấm một góc 45 độ theo chiều kim đồng hồ, tâm góc quay là

tâm của nút bấm. Mặc định thì tâm góc quay là góc trên trái của nút bấm. Sử dụng thuộc tính

RenderTransformOrigin của Button và thiết lập ví trí tâm quay. Cặp giá trị của

RenderTransformOrigin là số thực từ 0 đến 1, được tính theo tỷ lệ chiều rộng và cao của đối tượng

tính từ góc trên trái.

Page 182: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team | WPF – Bài 8: Đồ họa hai chiều trong WPF (2D –

Graphics)

33

Sử dụng thuộc tính RenderTransform của Button và để thực hiện biến đổi hình học, góc

quay được thiết lập nhờ thuộc tính Angle của đối tượng RotateTransform.

Mã lệnh XAML của ví dụ trên:

<Border Margin="30"

HorizontalAlignment="Left" VerticalAlignment="Top"

BorderBrush="Black" BorderThickness="1">

<StackPanel Orientation="Vertical">

<Button Content="A Button" Opacity="1" />

<Button Content="Rotated Button"

RenderTransformOrigin="0.5,0.5">

<Button.RenderTransform>

<RotateTransform Angle="45" />

</Button.RenderTransform>

</Button>

<Button Content="A Button" Opacity="1" />

</StackPanel>

</Border>

4. Câu hỏi ôn tập

1. Các đối tượng hình học có thể được xây dựng bởi?

A. Mã lệnh XAML

B. Mã lệnh C#

C. Cả hai

Câu trả lời: C

2. Đối tượng Polyline gồm N cạnh được định nghĩa bởi bao nhiêu cặp tọa độ?

A. N cặp tọa độ

B. N+1 cặp tọa độ

C. N-1 cặp tọa độ

Page 183: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team | WPF – Bài 8: Đồ họa hai chiều trong WPF (2D –

Graphics)

34

Câu trả lời: B

3. Đối tượng Rectangle được định nghĩa bởi?

A. Các thuộc tính Left, Top, Right, Bottom

B. Các thuộc tính Left, Top, Width, Height

Câu trả lời: B

4. Đối tượng Ellipse được định nghĩa bởi?

A. Các thuộc tính Tọa độ tâm của Ellipse và các bán kính của nó

B. Hình chữ nhật ngoại tiếp của Ellipse.

Câu trả lời: B

5. Đối tượng Polygon gồm N định được định nghĩa bởi bao nhiêu cặp tọa độ?

A. N cặp tọa độ

B. N+1 cặp tọa độ

C. N-1 cặp tọa độ

Câu trả lời: A

6. Các đối tượng hình học có thể được tô bên trong bởi:

A. Màu đồng nhất

B. Tô đổ màu theo tuyến tính hoặc theo bán kính hình tròn

C. Tô màu bởi hình ảnh có sẵn

D. Đáp án A,B,C

E. Đáp án A và B

Câu trả lời: D

7. Khi sử dụng ảnh để tô màu cho đối tượng thì mặc định ảnh sẽ

A. Giữ nguyên kích thước gốc.

B. Tự co dãn để phủ kín đối tượng.

C. Giữ nguyên kích thước và tự động xếp lợp phủ kín đối tượng.

D. Không đáp án nào đúng.

Câu trả lời: B

Page 184: Bài mở đầu - dulieu.tailieuhoctap.vndulieu.tailieuhoctap.vn/books/cong-nghe-thong-tin/lap-trinh-ung-dung/... · Hãy xem xét một ứng dụng cụ thể trong quản lý

Microsoft Vietnam – DPE Team | WPF – Bài 8: Đồ họa hai chiều trong WPF (2D –

Graphics)

35

5. Tài liệu tham khảo

1. WPF Graphics, Animation, and Media Overview; http://msdn.microsoft.com/en-

us/library/ms742562.aspx .

2. Shapes and Basic Drawing in WPF Overview; URL: http://msdn.microsoft.com/en-

us/library/ms747393.aspx .

3. WPF Brushes Overview; URL: http://msdn.microsoft.com/en-us/library/aa970904.aspx .

4. Transforms Overview; URL http://msdn.microsoft.com/en-us/library/ms750596.aspx .