Thật không may là không có ứng dụng ví dụ MVVM tuyệt vời nào làm mọi thứ và có rất nhiều cách tiếp cận khác nhau để thực hiện. Đầu tiên, bạn có thể muốn làm quen với một trong các khung ứng dụng (Prism là một lựa chọn tốt), bởi vì chúng cung cấp cho bạn các công cụ tiện lợi như tiêm phụ thuộc, chỉ huy, tập hợp sự kiện, v.v để dễ dàng thử các mẫu khác nhau phù hợp với bạn .
Việc phát hành lăng kính:
http://www.codeplex.com/CompositeWPF
Nó bao gồm một ứng dụng ví dụ khá phong nha (thương nhân cổ phiếu) cùng với rất nhiều ví dụ nhỏ hơn và làm thế nào để là. Ít nhất đó là một minh chứng tốt về một số mẫu phụ phổ biến mà mọi người sử dụng để làm cho MVVM thực sự hoạt động. Họ có những ví dụ cho cả CRUD và hộp thoại, tôi tin.
Lăng kính không nhất thiết cho mọi dự án, nhưng đó là điều tốt để làm quen.
CRUD: Phần này khá dễ dàng, các liên kết hai chiều của WPF giúp bạn dễ dàng chỉnh sửa hầu hết dữ liệu. Bí quyết thực sự là cung cấp một mô hình giúp dễ dàng thiết lập giao diện người dùng. Ít nhất bạn muốn đảm bảo rằng ViewModel của bạn (hoặc đối tượng nghiệp vụ) triển khai INotifyPropertyChanged
để hỗ trợ ràng buộc và bạn có thể liên kết các thuộc tính trực tiếp với các điều khiển giao diện người dùng, nhưng bạn cũng có thể muốn thực hiện IDataErrorInfo
để xác thực. Thông thường, nếu bạn sử dụng một số loại giải pháp ORM thiết lập CRUD là một snap.
Bài viết này chứng tỏ hoạt động crud đơn giản: http://dotnetslackers.com/articles/wpf/WPFDataBindingWithLINQ.aspx
Nó được xây dựng trên LinqToSql, nhưng đó là không liên quan đến ví dụ - tất cả những gì là quan trọng là đối tượng kinh doanh của bạn thực hiện INotifyPropertyChanged
(mà lớp được tạo ra bởi LinqToSql làm) . MVVM không phải là điểm của ví dụ đó, nhưng tôi không nghĩ rằng nó quan trọng trong trường hợp này.
Bài viết này chứng tỏ xác nhận dữ liệu
http://blogs.msdn.com/wpfsdk/archive/2007/10/02/data-validation-in-3-5.aspx
Một lần nữa, hầu hết các giải pháp ORM tạo ra các lớp học đã thực hiện IDataErrorInfo
và thường cung cấp một cơ chế để làm cho nó dễ dàng để thêm quy tắc xác nhận tùy chỉnh.
Hầu hết thời gian bạn có thể lấy một đối tượng (mô hình) được tạo bởi một số ORM và bọc nó trong một ViewModel chứa nó và lệnh lưu/xóa - và bạn đã sẵn sàng ràng buộc giao diện người dùng thẳng đến các thuộc tính của mô hình.
Quan điểm sẽ trông giống như một cái gì đó như thế này (ViewModel có một tài sản Item
chứa mô hình, giống như một lớp được tạo ra trong ORM):
<StackPanel>
<StackPanel DataContext=Item>
<TextBox Text="{Binding FirstName, Mode=TwoWay, ValidatesOnDataErrors=True}" />
<TextBox Text="{Binding LastName, Mode=TwoWay, ValidatesOnDataErrors=True}" />
</StackPanel>
<Button Command="{Binding SaveCommand}" />
<Button Command="{Binding CancelCommand}" />
</StackPanel>
Dialogs: Dialogs và MVVM là một chút khôn lanh. Tôi thích sử dụng một hương vị của cách tiếp cận Mediator với hộp thoại, bạn có thể đọc thêm một chút về nó trong câu hỏi StackOverflow này:
WPF MVVM dialog example
cách tiếp cận thông thường của tôi, mà không phải là MVVM khá cổ điển, có thể được tóm tắt như sau:
Lớp cơ sở cho hộp thoại ViewModel hiển thị các lệnh cho cam kết và hủy tác vụ, một sự kiện để cho phép khung nhìn biết rằng hộp thoại đã sẵn sàng để đóng và bất kỳ thứ gì bạn cần trong tất cả các hộp thoại của bạn.
Chế độ xem chung cho hộp thoại của bạn - đây có thể là cửa sổ hoặc điều khiển loại lớp phủ "phương thức" tùy chỉnh. Trái tim của nó là người trình bày nội dung mà chúng ta đổ viewmodel vào, và nó xử lý hệ thống dây điện để đóng cửa sổ - ví dụ về thay đổi ngữ cảnh dữ liệu, bạn có thể kiểm tra xem ViewModel mới có được thừa kế từ lớp cơ sở của bạn không, và nếu có, đăng ký sự kiện đóng có liên quan (trình xử lý sẽ gán kết quả hộp thoại). Nếu bạn cung cấp chức năng đóng phổ quát thay thế (ví dụ như nút X), bạn nên đảm bảo chạy lệnh đóng tương ứng trên ViewModel.
Một nơi nào đó bạn cần cung cấp mẫu dữ liệu cho Chế độ xem của mình, chúng có thể rất đơn giản đặc biệt vì bạn có thể có chế độ xem cho mỗi hộp thoại được đóng gói trong điều khiển riêng. Các mẫu dữ liệu mặc định cho một ViewModel sau đó sẽ giống như thế này:
<DataTemplate DataType="{x:Type vmodels:AddressEditViewModel}>
<views:AddressEditView DataContext={Binding} />
</DataTemplate>
Quan điểm thoại cần phải có quyền truy cập vào đó, bởi vì nếu không nó sẽ không biết làm thế nào để hiển thị các ViewModel, ngoài giao diện người dùng thoại chia sẻ nội dung của nó về cơ bản là:
<ContentControl Content={Binding} />
Mẫu dữ liệu tiềm ẩn sẽ ánh xạ chế độ xem cho mô hình, nhưng ai sẽ khởi chạy nó?
Đây là phần không phải là mvvm. Một cách để làm điều đó là sử dụng một sự kiện toàn cầu. Điều tôi nghĩ là tốt hơn nên làm là sử dụng thiết lập loại tập hợp sự kiện, được cung cấp thông qua việc tiêm phụ thuộc - theo cách này sự kiện là toàn cầu đối với vùng chứa chứ không phải toàn bộ ứng dụng. Prism sử dụng khuôn khổ thống nhất cho ngữ nghĩa container và tiêm phụ thuộc, và nói chung tôi thích Unity khá một chút.
Thông thường, có nghĩa là cửa sổ gốc đăng ký sự kiện này - nó có thể mở hộp thoại và đặt ngữ cảnh dữ liệu của nó thành ViewModel được truyền vào với sự kiện được nâng lên.
Thiết lập điều này theo cách này cho phép ViewModels yêu cầu ứng dụng mở hộp thoại và phản hồi hành động của người dùng ở đó mà không biết gì về giao diện người dùng vì vậy phần lớn MVVM-ness vẫn hoàn tất.
Tuy nhiên, đôi khi giao diện người dùng phải nâng cao hộp thoại, điều này có thể khiến mọi việc trở nên phức tạp hơn một chút. Hãy xem xét ví dụ, nếu vị trí hộp thoại phụ thuộc vào vị trí của nút mở nó.Trong trường hợp này, bạn cần có một số thông tin cụ thể về giao diện người dùng khi bạn yêu cầu mở hộp thoại. Tôi thường tạo một lớp riêng biệt chứa một ViewModel và một số thông tin giao diện người dùng có liên quan. Thật không may một số khớp nối dường như không thể tránh khỏi ở đó.
Pseudo code của một handler nút đó đặt ra một hộp thoại mà cần dữ liệu vị trí phần tử:
ButtonClickHandler(sender, args){
var vm = DataContext as ISomeDialogProvider; // check for null
var ui_vm = new ViewModelContainer();
// assign margin, width, or anything else that your custom dialog might require
...
ui_vm.ViewModel = vm.SomeDialogViewModel; // or .GetSomeDialogViewModel()
// raise the dialog show event
}
Quan điểm hộp thoại sẽ liên kết với dữ liệu vị trí, và vượt qua các ViewModel chứa đến nội ContentControl
. Bản thân ViewModel vẫn không biết gì về giao diện người dùng.
Nói chung, tôi không sử dụng thuộc tính trả về DialogResult
của phương thức ShowDialog()
hoặc mong đợi chuỗi chặn cho đến khi hộp thoại đóng. Một hộp thoại không chuẩn thường không hoạt động như thế, và trong một môi trường tổng hợp, bạn thường không thực sự muốn một trình xử lý sự kiện để chặn như thế nào. Tôi thích để cho ViewModels đối phó với điều này - người tạo ra một ViewModel có thể đăng ký các sự kiện liên quan của nó, thiết lập các phương thức commit/cancel, vv, vì vậy không cần phải dựa vào cơ chế UI này.
Vì vậy, thay vì quy trình này:
// in code behind
var result = somedialog.ShowDialog();
if (result == ...
tôi sử dụng:
// in view model
var vm = new SomeDialogViewModel(); // child view model
vm.CommitAction = delegate { this.DoSomething(vm); } // what happens on commit
vm.CancelAction = delegate { this.DoNothing(vm); } // what happens on cancel/close (optional)
// raise dialog request event on the container
tôi thích nó theo cách này bởi vì hầu hết các hộp thoại của tôi đều là phòng không chặn kiểm soát giả phương thức và làm nó theo cách này có vẻ đơn giản hơn là làm việc xung quanh nó. Dễ dàng để kiểm tra đơn vị là tốt.
Tôi rất vui vì những tài nguyên này đã hữu ích. Tôi hiện đang trong ứng dụng MVVM sản xuất thứ hai của mình và sẽ tiếp tục thêm nội dung sẽ hữu ích cho những người bắt đầu khi tôi bắt gặp nó. – jwarzech