Implementing the MVVM pattern (Part 1)

Mẫu thiết kế MVVM đưa ra cách tách biệt rõ rệt giữa UI, presentation logic, business logic và dữ liệu của ứng dụng bằng cách tách thành các lớp riêng biệt. Các tương tác giữa View và ViewModel là phần quan trọng nhất để tìm hiểu, nhưng các tương tác giữa các model class và ViewModel cũng quan trọng không kém. Nội dung trình bày sau đây mô tả các pattern khác cho các tương tác này và mô tả cách thiết kế chúng khi bạn thực thi mẫu thiết kế MVVM trong các ứng dụng của bạn.

Data Binding

Kết buộc dữ liệu đóng một vai trò quan trọng trong MVVM. Cả WPF và Silverlight đề cung cấp những khả năng kết buộc dữ liệu mạnh mẽ. ViewModel và các model class của bạn có thể được thiết kế để hỗ trợ việc kết buộc dữ liệu để chúng có thể tận dụng những khả năng này của chúng. Thông thường, điều này có nghĩa là chúng phải thực thi đúng các interface.

Kết buộc dữ liệu của Silverlight và WPF hỗ trợ  nhiều chế độ kết buộc dữ liệu. Với việc kiết buộc “1 chiều” (one-way), các UI control có thể giới hạn cho một ViewModel để chúng có thể “phản chiếu” giá trị của dữ liệu bên dưới khi việc hiển thị được render. Kết buộc “2 chiều” (two-way) cũng sẽ tự động cập nhật dữ liệu bên dưới khi người dùng định nghĩa nó trong UI.

Để đảm bảo UI luôn được cập nhật khi dữ liệu thanh đối trong ViewModel, nó phải thực thi interface thông báo thay đổi tương ứng. Nếu nó định nghĩa các thuộc tính có thể là giới hạn dữ liệu, nó sẽ thực thi INotifyPropertyChanged interface. Nếu ViewModel mô tả một collection, nó sẽ thực thi INotifyCollectionChanged interface hoặc chuyến hóa từ lớp ObservableCollection<T> cung cấp cách thực thi của interface này. Cả hai interface này đều định nghĩa một sự kiện được đưa ra bất kể khi nào dữ liệu bên dưới thay đổi. Bất kì control giới hạn dữ liệu nào cũng sẽ được tự động cập nhật khi các sự kiện này được tạo ra.

Trong nhiều trường hợp, một ViewModel sẽ định nghĩa các thuộc tính trả về các đối tượng. Khả năng kết buộc dữ liệu trong WPF và Silverlight hỗ trợ kết buộc các thuộc tính lồng nhau thông qua thuộc tính Path. Hơn nữa, việc ViewModel của View trả về các tham chiếu đến ViewModel hoặc các model class khác là rất thường xuyên. Tất cả ViewModel và các model class có khả năng truy xuất đến View sẽ thực thi các interface INotifyPropertyChanged hoặc INotifyCollectionChanged tương ứng.

Implementing INotifyPropertyChanged

Việc thực thi INotifyPropertyChanged interface trong ViewModel hoặc các model class cho phép chúng cung cấp các thông báo thay đổi (change notification) đến bất kì control giới hạn dữ liệu nào (data-bound control) trong View khi giá trị của thuộc tính bên dưới thay đổi. Ví dụ (xem Questionnaire class trong demo)

questionnaire-class

Việc thực thi INotifyPropertyChanged interface trên nhiều ViewModel class có thể lăp lại và error-prone bởi việc cần thiết xác định tên thuộc tính trong tham số của sự kiện. Prism Library cung cấp một lớp cơ sở mà bạn có thể chuyển hóa các ViewModel class thực thi iNotifyPeropersyChanged interface

INotifyPropertyChanged

Một ViewModel class được kế thừa có thể tạo ra sự kiện thay đổi thuộc tính bằng việc thực thi RaisePropertyChanged với tên thuộc tính được xác định hoặc sử dụng một lamda expression để chỉ ra thuộc tính

RaisePropertyChanged

Thông thường, Model và ViewModel của bạn sẽ bao gồm các thuộc tính mà các giá trị của chúng được tính toán từ những thuộc tính khác trong Model hoặc ViewMode. Khi xử lý những thay đổi đối với các thuộc tính này phải đảm bảo rằng các sự kiện thông báo cũng phải được tạo ra cho bất kì thuộc tính được tính toán nào.

Implementing INotifyCollectionChanged

ViewModel hoặc Model class của bạn có thể mô tả một collection của các item, hoặc nó có thể định nghĩa một hoặc nhiều thuộc tính trả về một collection của các item. Trong các trường hợp này, bạn sẽ muốn hiển thị collection trong một ItemControl như ListBox hoặc DataGrid control trong View. Những control này có thể giới hạn dữ liệu cho một ViewModel mà mô tả một collection hoặc một thuộc tính trả về một collection thông qua thuộc tính ItemSource.

ItemSource-DataGrid

Để hỗ trợ các yều cầu thông báo thay đổi tức khắc, ViewModel hoặc Model class, nếu nó mô tả một collection, nên thực thi INotifyCollectionChanged interface (trong trường hợp khác có thể là INotifyPropertyChanged interface). Nếu ViewModel hoặc Model class xác định một thuộc tính mà trả về một tham chiếu đến một collection, collection class được trả về nên thực thi INotifyCollectionChanged interface.

Tuy nhiên, việc thực thi INotifyCollectionChanged interface có thẩ là thách thức vì nó cung cấp các notification khi các item được thêm vào, xóa (hủy) hoặc bị thay đổi trong collection. Thay vì thực thi trực tiếp interface này, nó thường dễ dàng sử dụng hoặc chuyển hóa từ một collection class thực thi nó rồi. ObservableCollection<T> class cung cấp một sự thực thi cho interface này và được sử dụng phổ biến như một lớp cơ sở hoặc thực thi các thuộc tính mô tả một collection của các item.

ObservableCollection

Nếu bạn có một tham chiếu đến một collection class (chẳng hạn như từ thành phần khác hoặc service không thực thi INotifyCollectionChanged), thông thường bạn có thể “gói’” trong một đối tượng ObservableCollection<T> sử dụng một trong các hàm dựng lấy một tham số IEnumerable<T> hoặc List<T>.

Implementing ICollectionView

Đoạn code trước cho thấy cách thực thi một thuộc tính ViewModel đơn giản trả về một collection các item có thể được hiển thị thông qua các control giới hạn dữ liệu trong View. Vì ObservableCollection<T> class thực thi INotifyCollectionChanged interface, các control trong View sẽ được tự động cập nhật để “chiếu’” danh sách các item trong collection khi các item được thêm vào hay bị xóa đi.

Tuy nhiên, thông thường bạn sẽ cần nhiều sự kiểm soát hơn về cách collection các item được hiển thị trong View, hoặc theo dõi sự tương tác của người dùng với collection các item được hiển thị trong bản thân ViewModel. Ví dụ, bạn có thể cần phải cho phép collection các item được chọn lọc hoặc sắp xếp theo presentation logic được thực thi trong View, hoặc bạn có thể cần theo dõi item được chọn hiện thời trong View để các câu lệnh được thực thi trong ViewModel có thể thực thi trên item được chọn đó.

WPF và Silverlight hỗ trợ những trường hợp này bằng việc cung cấp các class khác nhau thực thi ICollectionView interface. interface này cung cấp các thuộc tính và phương thức cho phép một collection được chọn lọc, sắp xếp hoặc gom nhóm lại, và cho phép item được chọn hiện tại được thay đổi hoặc được theo dõi. Cả WPF và Silverlight cung cấp các implementation cho interface này – Silverlight cung cấp PagedCollectionView class, WPF cung cấp ListCollectionView class.

Các collection view class có thể được sử dụng bởi ViewModel để theo dõi thông tin trạng thái quan trọng cho collection bên dưới, trong khi việc bảo trì một sự tách biệt rõ ràng của các mối quan tâm giữa UI trong View và dữ liệu bên dưới trong Model. Để hiệu quả, các CollectionView là các ViewModel được thiết kế đặc biệt để hỗ trợ các collection.

Hơn nữa, nếu bạn cần thực thi việc chọn lọc, sắp xếp, gom nhóm hoặc theo dõi các item được chọn trong ViewModel của bạn, ViewModel của bạn nên tạo ra một đối tượng của một view collection class cho mỗi collection được đưa đến View. Sau đó bạn có thể đăng kí các selection changed event chẳng hạn như CurrentChanged event, hoặc điều khiển việc chọn lọc, sắp xếp hoặc gom nhóm bằng cách sử dụng các phương thức được cung cấp bởi collection view class trong ViewModel của bạn.

ViewModel nên thực thi một thuộc tính chỉ đọc trả về một ICollectionView reference để các control trong View có thể kết buộc dữ lêệu vào collection view object và tương tác với nó. Tất cả control của WPF và Silverlight mà chuyển hóa từ ItemsControl base class có thể tự động tương tác với các ICollectionView class.

Đoạn code sau minh họa cho việc sử dụng PagedCollectionChanged trong Silverlight để theo dõi customer được chọn hiện tại.

PageCollectionChanged

Trong View, bạn có thể kết buộc một ItemsControl, chẳng hạn như ListBox, vào thuộc tính Customers trên ViewModel thông qua thuộc tính ItemsSource của nó.

Customers-ItemsSource

Khi người dùng chọn một customer trong UI, ViewModel sẽ được thông báo để nó có thể thực thi các câu lệnh liên quan đến customer được chọn hiện tại. ViewModel cũng có thể thay đổi theo như lập trình chọn lựa hiện tại trong UI bằng việc gọi các phương thức trên collection view object.

customer-next

Khi selection thay đổi trong collection view, UI tự động cập nhật để mô tả trạng thái được chọn của item. Việc thực thi cũng tương tự đối với WPF mặc dù PagedCollectionChanged trong ví dụ trước thông thường sẽ bị thay thế bằng một ListCollectionView hoặc BindingListCollectionView class.

customers-binding

One thought on “Implementing the MVVM pattern (Part 1)

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s