2015-06-06 19 views
5

Sau nhiều giờ tìm kiếm tôi vẫn không trả lời cho câu hỏi này. Tôi đã đọc điều này tốt đẹp writing về async MVVM và làm cho viewmodel của tôi để sử dụng phương pháp nhà máy.MVVM Cách đặt datacontext khi viewmodel sử dụng async

public class MainViewModel 
{ 
    // sic - public, contrary to the pattern in the article I cite 
    // so I can create it in the Xaml as below 
    public MainViewModel() 
    { 
    } 

    private async Task InitializeAsync() 
    { 
     await DoSomethingAsync(); 
    } 

    public static async Task<MainViewModel> CreateAsync() 
    { 
     var ret = new MainViewModel(); 
     await ret.InitializeAsync(); 
     return ret; 
    } 
} 

Điều này rõ ràng đối với tôi, nhưng tôi không thể hiểu cách tạo ví dụ của MainViewModel và đặt nó thành datacontext trong MainPage. Tôi không thể chỉ cần viết

<Page.DataContext> 
    <viewModel:MainViewModel/> 
</Page.DataContext> 

vì tôi nên sử dụng phương thức MainViewModel.CreateAsync(). Và tôi không thể làm điều đó trên code-behind, mà tôi thậm chí muốn làm, bởi vì code-behind -constructor là phương thức bình thường, không phải là một phương thức async. Vì vậy, đó là cách thích hợp để tiếp tục?

+0

WP8.1! = WPF. Điều đáng tiếc hơn. Đây là thời điểm mong đợi ngày chúng tôi có thể đồng bộ hóa chúng. – Will

Trả lời

5

làm viewmodel tôi sử dụng phương pháp nhà máy

Tôi thường là một fan hâm mộ của cách tiếp cận đó - đó là cách yêu thích của tôi để làm việc xung quanh "không constructors async" giới hạn. Tuy nhiên, nó không hoạt động tốt trong mẫu MVVM.

Điều này là do máy ảo là giao diện người dùng của bạn, một cách hợp lý. Và khi người dùng điều hướng đến màn hình trong ứng dụng, ứng dụng cần trả lời ngay lập tức (đồng bộ). Nó không nhất thiết phải hiển thị bất cứ điều gì hữu ích, nhưng nó cần phải hiển thị một cái gì đó. Vì lý do này, việc xây dựng VM phải đồng bộ.

Vì vậy, thay vì cố gắng xây dựng máy ảo của bạn một cách không đồng bộ, trước tiên hãy quyết định bạn muốn giao diện người dùng "tải" hoặc "không hoàn chỉnh" của mình trông như thế nào. Nhà xây dựng VM (đồng bộ) của bạn nên khởi tạo trạng thái đó, và nó có thể khởi động một số công việc không đồng bộ mà cập nhật VM khi nó hoàn thành.

Đây không phải là quá khó để làm bằng tay, hoặc bạn có thể sử dụng phương pháp NotifyTaskCompletion mà tôi đã mô tả trong một MSDN article on async MVVM data binding để thúc đẩy quá trình chuyển đổi trạng thái bằng cách sử dụng các ràng buộc dữ liệu.

2

Thứ nhất, bạn nên tạo hàm tạo mặc định là riêng tư để tránh lạm dụng lớp của bạn (bài viết bạn trích dẫn thực hiện điều này - hàm tạo là private).

Cách tiếp cận bạn đang sử dụng để đặt DataContext không phù hợp với mẫu MVVM (số View không được tự tạo ViewModel). Bạn nên tạo ViewViewModel trong lớp cấp cao hơn và yêu cầu lớp đó ràng buộc chúng. Nói nếu Page là chính bạn View bạn nên tạo chúng trong App.xaml.cs bằng cách ghi đè OnStartup, một cái gì đó như thế này:

var page = new Page(); 
var dataService = new YourDataService(); // iff Create or the ctor require arguments 
var viewModel = await MainViewModel.CreateAsync(dataService); 
page.DataContext = viewModel; 
page.Show(); 
3

Bạn phải initalize viewmodel trước khi cửa sổ đang mở. Truy cập tệp App.xaml của bạn và xóa phần: StartupUri="MainWindow.xaml". Sau đó, bạn truy cập vào số App.xaml.cs và thêm số này:

protected async override void OnStartup(StartupEventArgs e) 
{ 
    base.OnStartup(e); 
    var mainWindow = new MainWindow { DataContext = await CreateAsync() }; 
    mainWindow.Show(); 
} 
+0

Cảm ơn bạn, có vẻ đẹp. Nhưng ứng dụng của tôi là dành cho wp 8.1, vì vậy nó không có thuộc tính StartupUri trên App.xaml của tôi. Phần nào của App.xaml.cs tôi nên sửa đổi và làm cách nào? –

2

Tôi sẽ tính lại. Làm cho cấu trúc xây dựng/instantiation gọn nhẹ MainViewModel. Sau đó, tạo phương thức Load hoặc Initialize trên máy ảo của bạn. Từ code-behind tạo một thể hiện, đặt nó vào DataContext, sau đó gọi phương thức init và để cho nó chạy.

Ví dụ:

/// <summary>Interaction logic for MainWindow.xaml</summary> 
public partial class MainWindow : Window 
{ 
    public MainWindow() 
    { 
     this.InitializeComponent(); 

     var dc = new MainViewModel(); 
     dc.Initialize("Hello", " ", "world"); 
     this.DataContext = dc; 
    } 
} 

public class MainViewModel 
{ 
    /// <summary>Simple constructor</summary> 
    public MainViewModel() { } 

    public void Initialize(params object[] arguments) 
    { 
     //use the task to properly start a new thread as per: 
     //http://stackoverflow.com/a/14904107/1144090 and 
     //https://msdn.microsoft.com/en-us/library/hh965065.aspx 
     //(what would happen if we simply invoke init async here?) 
     this.InitializeAsync(arguments) 
      .ContinueWith(result => 
      { 
       if (!result.IsFaulted) 
        return; 

       MessageBox.Show("Unexpected error: " + Environment.NewLine + result.Exception.ToString()); 
      }); 
    } 

    private async Task InitializeAsync(params object[] arguments) 
    { 
     await Task.Delay(2333); 

     MessageBox.Show(String.Concat(arguments)); 
    } 
} 

Lưu ý rằng đây là giải pháp nhanh chóng và bẩn, hai câu trả lời khác (kết hợp với khung tiêm phụ thuộc) sẽ cung cấp cho bạn cấu trúc mức cao phù hợp cho giải pháp của bạn.

+0

Windows không được khởi chạy ViewModels. Bạn kết thúc với mã rất giòn và khó quản lý những gì sẽ xảy ra nếu intialization không thành công (tức là, hiển thị một hộp thông báo như thế này là không thực tế trong trường hợp chung). Tôi đề nghị mạnh mẽ xem xét cách tiếp cận trong các câu trả lời khác đầu tiên. –

+0

@RubenBartelink nó thực sự là container IOC nên quản lý việc khởi động ứng dụng. Có, ghi đè khởi động ứng dụng như trong các ví dụ khác là "thích hợp" hơn. Nơi thích hợp hơn để đặt init IOC của bạn. –

Các vấn đề liên quan