2010-03-28 20 views
24

Câu hỏi chung của tôi là trạng thái tiêu đề, tốt nhất là tải dữ liệu trong khi xây dựng ViewModel hay sau đó thông qua một số xử lý sự kiện được tải?Tải dữ liệu MVVM trong hoặc sau khi xây dựng ViewModel?

Tôi đoán câu trả lời là sau khi xây dựng thông qua một số xử lý sự kiện được tải, nhưng tôi tự hỏi làm thế nào được phối hợp rõ ràng nhất giữa ViewModel và Chế độ xem?

Dưới đây là chi tiết hơn về tình hình của tôi và các vấn đề cụ thể tôi đang cố gắng để giải quyết:

Tôi đang sử dụng khuôn khổ MVVM ánh sáng cũng như Unity cho DI. Tôi có một số khung nhìn lồng nhau, mỗi khung được gắn với một ViewModel tương ứng. ViewModels liên kết với mỗi DataContext kiểm soát gốc của View thông qua ý tưởng ViewModelLocator mà Laurent Bugnion đã đưa vào MVVM Light. Điều này cho phép tìm kiếm ViewModels thông qua một tài nguyên tĩnh và để kiểm soát tuổi thọ của ViewModels thông qua một khuôn khổ Dependency Injection, trong trường hợp này là Unity. Nó cũng cho phép Expression Blend xem mọi thứ liên quan đến ViewModels và cách liên kết chúng.

Vì vậy, dù sao đi chăng nữa, tôi cũng có Chế độ xem chính có Chế độ xem dữ liệu ComboBox cho ObservableCollection trong ViewModel của nó. ComboIt's SelectedItem cũng bị ràng buộc (hai chiều) đến một thuộc tính trên ViewModel. Khi việc lựa chọn các ComboBox thay đổi, điều này là để kích hoạt các bản cập nhật trong các khung nhìn và các bản xem trước khác. Hiện tại tôi đang thực hiện điều này thông qua hệ thống Nhắn tin được tìm thấy trong MVVM Light. Đây là tất cả làm việc tuyệt vời và như mong đợi khi bạn chọn các mục khác nhau trong ComboBox.

Tuy nhiên, ViewModel đang nhận dữ liệu trong suốt thời gian xây dựng thông qua một loạt các cuộc gọi phương thức khởi tạo. Điều này dường như chỉ là một vấn đề nếu tôi muốn kiểm soát những gì SelectedItem ban đầu của ComboBox là. Sử dụng hệ thống nhắn tin của MVVM Light, tôi hiện đã thiết lập nơi thiết lập thuộc tính SelectedItem của ViewModel là bản phát sóng bản cập nhật và ViewModels quan tâm khác đăng ký thông báo trong các nhà xây dựng của họ. Có vẻ như tôi hiện đang cố gắng thiết lập SelectedItem thông qua ViewModel tại thời điểm xây dựng, điều này đã không cho phép các ViewModels phụ được xây dựng và đăng ký.

Cách nào là cách sạch nhất để phối hợp tải dữ liệu và cài đặt ban đầu của SelectedItem trong ViewModel? Tôi thực sự muốn gắn bó với việc đặt ít tiền trong mã của View như là hợp lý. Tôi nghĩ rằng tôi chỉ cần một cách để ViewModel biết khi nào công cụ đã được tải và sau đó nó có thể tiếp tục tải dữ liệu và hoàn tất giai đoạn thiết lập.

Cảm ơn bạn đã trả lời trước.

+1

Bạn không thể gọi sự kiện được tải lên một phương thức trên chế độ xem? – Klinger

+1

Vâng, tôi cho là có thể. Tôi có lẽ đang nghĩ về nó. Tôi đoán do dự của tôi với đó là tôi đã có thể ràng buộc tất cả mọi thứ cho đến nay tuyên bố trong XAML. Tôi đã thiết lập DataContext và sau đó thiết lập các ràng buộc thành viên tất cả ở một nơi. Có cách nào sạch sẽ để tiếp tục điều này trong XAML với sự kiện Loaded của điều khiển bị ràng buộc với một phương thức ViewModel? Tất nhiên, tôi không nghĩ rằng ViewModel nên có thông số xử lý sự kiện theo giao diện người dùng cụ thể mặc dù một trong hai. – mkmurray

Trả lời

23

Đối với các sự kiện, bạn nên sử dụng EventToCommand trong Bộ công cụ nhẹ MVVM. Sử dụng điều này, bạn có thể ràng buộc bất kỳ sự kiện nào của bất kỳ phần tử ui nào để chuyển tiếp. Kiểm tra bài viết của mình trên EventToCommand tại

http://blog.galasoft.ch/archive/2009/11/05/mvvm-light-toolkit-v3-alpha-2-eventtocommand-behavior.aspx

Tải mẫu và có một cái nhìn. Thật tuyệt vời. Bạn sẽ không cần bất kỳ codebehind sau đó. Một ví dụ là như sau:

<Page x:Class="cubic.cats.Wpf.Views.SplashScreenView" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
     xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
     xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity" 
     xmlns:cmd="clr-namespace:GalaSoft.MvvmLight.Command;assembly=GalaSoft.MvvmLight.Extras" 
     mc:Ignorable="d" 
     d:DesignHeight="300" d:DesignWidth="300" 
    Title="SplashScreenPage"> 

    <i:Interaction.Triggers> 
     <i:EventTrigger EventName="Loaded"> 
      <cmd:EventToCommand Command="{Binding LoadedCommand}" /> 
     </i:EventTrigger>   
    </i:Interaction.Triggers> 

    <Grid> 
     <Label Content="This is test page" /> 
    </Grid> 
</Page> 

và chế độ xem có thể là như thế này

public class SplashScreenViewModel : ViewModelBase 
    { 
     public RelayCommand LoadedCommand 
     { 
      get; 
      private set; 
     } 

     /// <summary> 
     /// Initializes a new instance of the SplashScreenViewModel class. 
     /// </summary> 
     public SplashScreenViewModel() 
     { 
      LoadedCommand = new RelayCommand(() => 
      { 
       string a = "put a break point here to see that it gets called after the view as been loaded"; 
      }); 
     } 
    } 

nếu bạn muốn mô hình nhằm có EventArgs, bạn có thể đơn giản thiết lập PassEventArgsToCommand true:

<i:Interaction.Triggers> 
      <i:EventTrigger EventName="Loaded"> 
       <cmd:EventToCommand PassEventArgsToCommand="True" Command="{Binding LoadedCommand}" /> 
    </i:EventTrigger>   
</i:Interaction.Triggers> 

và mô hình xem sẽ như thế nào

public class SplashScreenViewModel : ViewModelBase 
{ 
    public RelayCommand<MouseEventArgs> LoadedCommand 
    { 
     get; 
     private set; 
    } 

    /// <summary> 
    /// Initializes a new instance of the SplashScreenViewModel class. 
    /// </summary> 
    public SplashScreenViewModel() 
    { 
     LoadedCommand = new RelayCommand<MouseEventArgs>(e => 
     { 
      var a = e.WhateverParameters....; 
     }); 
    } 

} 
+0

Ahh .. Tôi đang tìm giải pháp với Bộ công cụ MVVM, ví dụ: mẫu mvvm đơn giản. Bạn có thể đề nghị một cái không? Cảm ơn –

1

Tôi quyết định chỉ có XAML khai báo ràng buộc với một trình xử lý sự kiện được tải trên mã phía sau của View, mà lần lượt được gọi là phương thức trên đối tượng ViewModel, thông qua phần tử gốc UserControl DataContext của View.

Đó là một giải pháp khá đơn giản, thẳng tiến và sạch sẽ. Tôi đoán tôi đã hy vọng cho một cách để ràng buộc các sự kiện Loaded cho đối tượng ViewModel trong cùng một cách khai báo bạn có thể với ICommands trong XAML.

Tôi có thể đã cung cấp cho Klinger tín dụng trả lời chính thức, nhưng anh ấy đã đăng nhận xét cho câu hỏi của tôi chứ không phải câu trả lời. Vì vậy, tôi ít nhất đã cho anh ta một một trong những nhận xét của mình.

2

Ok, sau đó. :-)

Bạn có thể liên kết với một phương thức trong ViewModel bằng cách sử dụng một hành vi.

Đây là liên kết sẽ giúp bạn với điều đó. http://expressionblend.codeplex.com/

0

Tôi đã gặp vấn đề tương tự khi xử lý thư giữa cửa sổ chính và cửa sổ con. Chỉ cần thay đổi thứ tự mà các mô hình khung nhìn của bạn được tạo ra trong lớp ViewModelLocator của bạn. Đảm bảo rằng tất cả các kiểu xem phụ thuộc vào tin nhắn được tạo trước khi mô hình xem gửi tin nhắn.

Ví dụ, trong constructor lớp ViewModelLocator của bạn:

public ViewModelLocator() 
{ 
    if (s_messageReceiverVm == null) 
    { 
     s_messageReceiverVm = new MessageReceiverVM(); 
    } 

    if (s_messageSenderVm == null) 
    { 
     s_messageSenderVm = new MessageSenderVM(); 
    } 
} 
+1

Bạn có thể khởi tạo các máy ảo trong ViewModelLocator theo cách này: 'SimpleIoc.Default.Register (true);' –

+0

Tôi không hiểu sự liên quan của nhận xét của bạn với câu trả lời của tôi. Bạn có thể giải thích? – bugged87

+1

Tôi chỉ đề xuất một cách khác (sử dụng Ioc), để tạo ViewModels trong hàm tạo ViewModelLocator. –

1

Các giải pháp sau đây là tương tự như đã được cung cấp và được chấp nhận, nhưng nó không sử dụng một lệnh trong mô hình điểm để nạp dữ liệu, nhưng một "phương pháp bình thường". Tôi nghĩ rằng lệnh phù hợp hơn cho hành động của người dùng (lệnh có thể có sẵn và không có sẵn khi chạy), đó là lý do tại sao sử dụng cuộc gọi phương thức thông thường, nhưng cũng bằng cách đặt trình kích hoạt tương tác trong chế độ xem.

Tôi đề xuất điều này: Tạo lớp mô hình chế độ xem. Khởi tạo lớp mô hình chế độ xem trong xaml của chế độ xem bằng cách tạo lớp bên trong thuộc tính DataContext.

Triển khai phương pháp tải dữ liệu trong mô hình chế độ xem của bạn, ví dụ: LoadData. Thiết lập chế độ xem, để phương thức này được gọi khi lượt xem tải. Điều này được thực hiện bởi trình kích hoạt tương tác trong chế độ xem của bạn được liên kết với phương pháp trong mô hình chế độ xem (tham chiếu đến "Microsoft.Expression.Interactions" và "System.Windows.Interactivity"):

Chế độ xem (xaml):

<Window x:Class="MyWindow" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    Title="Test" 
    xmlns:viewModel="clr-namespace:ViewModels" 
    xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" 
    xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"    
    > 
<Window.DataContext> 
    <viewModel:ExampleViewModel/> 
</Window.DataContext> 
<i:Interaction.Triggers> 
    <i:EventTrigger EventName="Loaded"> 
     <ei:CallMethodAction TargetObject="{Binding}" MethodName="LoadData"/> 
    </i:EventTrigger> 
</i:Interaction.Triggers> 

Điều này sẽ gọi phương thức LoadData trong Chế độ xem khi chạy. Đây là nơi bạn tải dữ liệu của mình.

public class ExampleViewModel 
{ 
    /// <summary> 
    /// Constructor. 
    /// </summary> 
    public ExampleViewModel() 
    { 
     // Do NOT do complex stuff here 
    } 


    public void LoadData() 
    { 
     // Make a call to the repository class here 
     // to set properties of your view model 
    } 

Nếu phương pháp trong kho là một phương pháp async, bạn có thể thực hiện phương pháp LoadData async quá, nhưng điều này là không cần thiết trong từng trường hợp.

Nhân tiện, thông thường tôi sẽ không tải dữ liệu trong hàm tạo của mô hình khung nhìn. Trong ví dụ trên, hàm tạo (tham số ít) của mô hình khung nhìn được gọi khi trình thiết kế hiển thị khung nhìn của bạn. Làm những điều phức tạp ở đây có thể gây ra lỗi trong trình thiết kế khi hiển thị chế độ xem của bạn (vì lý do tương tự, tôi sẽ không tạo ra những thứ phức tạp trong hàm tạo khung nhìn). Trong một số kịch bản mã trong constructor xem mô hình thậm chí có thể gây ra các vấn đề trong thời gian chạy, khi các mô hình khung nhìn constructor thực hiện, thiết lập các thuộc tính của mô hình khung được ràng buộc với các phần tử trong khung nhìn, trong khi đối tượng view không hoàn toàn hoàn thành tạo.