2009-02-26 37 views
35

Trong ứng dụng WPF mà tôi đang viết bằng mẫu MVVM, tôi có một quá trình nền để thực hiện điều đó, nhưng cần phải cập nhật trạng thái từ nó vào giao diện người dùng.Đảm bảo OnPropertyChanged() được gọi trên chuỗi giao diện người dùng trong ứng dụng MVVM WPF

Tôi đang sử dụng mẫu MVVM, vì vậy ViewModel của tôi hầu như không biết gì về giao diện (UI) đang trình bày mô hình cho người dùng.

Nói rằng tôi có phương pháp sau đây trong ViewModel của tôi:

public void backgroundWorker_ReportProgress(object sender, ReportProgressArgs e) 
{ 
    this.Messages.Add(e.Message); 
    OnPropertyChanged("Messages"); 
} 

Theo quan điểm của tôi, tôi có một ListBox ràng buộc với Tin nhắn của tài sản (một List<string>) của ViewModel. OnPropertyChanged hoàn thành vai trò của giao diện INotifyPropertyChanged bằng cách gọi số PropertyChangedEventHandler.

Tôi cần đảm bảo rằng OnPropertyChanged được gọi trên chuỗi giao diện người dùng - làm cách nào để thực hiện việc này? Tôi đã thử những điều sau đây:

public Dispatcher Dispatcher { get; set; } 
public MyViewModel() 
{ 
    this.Dispatcher = Dispatcher.CurrentDispatcher; 
} 

Sau đó, thêm dòng sau vào phương pháp OnPropertyChanged:

if (this.Dispatcher != Dispatcher.CurrentDispatcher) 
{ 
    this.Dispatcher.Invoke(DispatcherPriority.Normal, new ThreadStart(delegate 
    { 
     OnPropertyChanged(propertyName); 
    })); 
    return; 
} 

nhưng điều này đã không làm việc. Bất kỳ ý tưởng?

Trả lời

33

WPF tự động sửa đổi các thay đổi về thuộc tính đối với chuỗi giao diện người dùng. Tuy nhiên, nó không thay đổi tập hợp soái, vì vậy tôi nghi ngờ việc thêm tin nhắn của bạn là nguyên nhân gây ra lỗi.

Bạn có thể sắp xếp lại việc thêm thủ công theo cách thủ công (xem ví dụ bên dưới) hoặc sử dụng một cái gì đó như this technique Tôi đã viết blog về một lúc trước.

thủ marshalling:

public void backgroundWorker_ReportProgress(object sender, ReportProgressArgs e) 
{ 
    Dispatcher.Invoke(new Action<string>(AddMessage), e.Message); 
    OnPropertyChanged("Messages"); 
} 

private void AddMessage(string message) 
{ 
    Dispatcher.VerifyAccess(); 
    Messages.Add(message); 
} 
+0

Điều đó đã làm nó, với một thay đổi nhỏ khác - tôi đã thay đổi Danh sách của tôi một ObservableCollction và nó hoạt động như một nét duyên dáng. Cảm ơn! –

+1

Nếu 'Tin nhắn' có thể quan sát được thì không cần phải gọi OnPropertyChanged() cho nó. – Doug

3

Tôi đã có một kịch bản tương tự chỉ trong tuần này (MVVM đây quá). Tôi đã có một lớp riêng biệt làm việc của nó, báo cáo tình trạng trở lại trên một xử lý sự kiện. Trình xử lý sự kiện đã được gọi như mong đợi và tôi có thể thấy kết quả quay lại đúng thời điểm với số của Debug.WriteLine.

Nhưng với WPF, bất kể tôi đã làm gì, giao diện người dùng sẽ không cập nhật cho đến khi quá trình hoàn tất. Ngay khi quá trình kết thúc, giao diện người dùng sẽ cập nhật như mong đợi. Nó giống như là nó đang nhận PropertyChanged, nhưng chờ đợi cho thread hoàn thành trước khi thực hiện cập nhật giao diện người dùng cùng một lúc.

(nhiều để mất tinh thần của tôi, cùng mã trong Windows.Forms với một DoEvents và .Refresh() làm việc như một nét duyên dáng.)

Cho đến nay, tôi đã giải quyết điều này bằng cách bắt đầu quá trình ngày của riêng mình chủ đề:

//hook up event handler 
myProcess.MyEvent += new EventHandler<MyEventArgs>(MyEventHandler); 

//start it on a thread ... 
ThreadStart threadStart = new ThreadStart(myProcess.Start); 

Thread thread = new Thread(threadStart); 

thread.Start(); 

và sau đó trong xử lý sự kiện:

private void MyEventHandler(object sender, MyEventArgs e) { 
.... 
Application.Current.Dispatcher.Invoke(
       DispatcherPriority.Send, 
       (DispatcherOperationCallback)(arg => 
       { 
     //do UI updating here ... 
     }), null); 

tôi không đề xuất mã này, kể từ khi tôi vẫn đang cố gắng để hiểu được mô hình thread WPF, cách thức hoạt động Dispatcher, và wh y trong trường hợp của tôi giao diện người dùng sẽ không cập nhật cho đến khi quá trình hoàn tất ngay cả với trình xử lý sự kiện được gọi là mong đợi (theo thiết kế?). Nhưng điều này đã làm việc cho tôi cho đến nay.

tôi thấy hai liên kết này hữu ích:

http://www.nbdtech.com/blog/archive/2007/08/01/Passing-Wpf-Objects-Between-Threads-With-Source-Code.aspx

http://srtsolutions.com/blogs/mikewoelmer/archive/2009/04/17/dealing-with-unhandled-exceptions-in-wpf.aspx

0

tôi xử lý các sự kiện BackgroundWorker.ReportProgress bên ngoài của mô hình quan điểm của tôi và truyền thể hiện BackgroundWorker thực tế và ViewModel vào lớp học của tôi mà định nghĩa (các) phương thức asynch.

Phương pháp asynch sau đó gọi bgWorker.ReportProgress và chuyển một lớp kết thúc tốt đẹp một đại biểu như userstate (làm đối tượng). Các đại biểu tôi viết như một phương pháp vô danh.

Trong trình xử lý sự kiện, tôi đưa nó từ đối tượng trở lại loại trình bao bọc và sau đó gọi đại biểu bên trong.

Tất cả điều này có nghĩa là tôi có thể mã thay đổi giao diện người dùng trực tiếp từ mã đang chạy không đồng bộ, nhưng nó chỉ có gói này bao quanh nó.

này sẽ giải thích chi tiết hơn:

http://lukepuplett.blogspot.com/2009/05/updating-ui-from-asynchronous-ops.html

8

Tôi thực sự thích Jeremy của câu trả lời: Dispatching In Silverlight

Tóm tắt:

  • Đặt Dispatcher trong ViewModel dường như không thanh nha

  • Tạo một hành động < Action> tài sản, thiết lập nó để chỉ chạy các hành động trong constructor VM

  • Khi sử dụng máy ảo từ V, thiết lập thuộc tính Action để gọi Dispatcher
0

Đây là chi tiết của một mở rộng của câu trả lời chấp nhận nhưng tôi đã làm điều này với hander sự kiện của tôi ...

using System.Threading; 

private void Handler(object sender, RoutedEventArgs e) 
{ 
    if (Thread.CurrentThread == this.Dispatcher.Thread) 
    { 
     //do stuff to this 
    } 
    else 
    { 
     this.Dispatcher.Invoke(
      new Action<object, RoutedEventArgs>(Handler), 
      sender, 
      e); 
    } 
} 
Các vấn đề liên quan