2011-12-30 34 views
7

xem xét tài sản ViewModel sau:Một trì hoãn ràng buộc từ nguồn

private string _slowProperty; 
public string SlowProperty 
{ 
    get { return _slowProperty; } 
    set 
    { 
     _slowProperty = value; 
     RaisePropertyChanged("SlowProperty"); 
    } 
} 

Đó là ràng buộc với một hộp, như vậy:

<TextBox Text="{Binding SlowProperty}" /> 

Bây giờ, vấn đề ở đây là mỗi khi giá trị của SlowProperty thay đổi, và nó làm như vậy khá thường xuyên, hộp văn bản sẽ đi và cố gắng để có được giá trị của nó, đó là, khá chậm. Tôi có thể làm giảm bớt tình hình bằng cách sử dụng async ràng buộc, tuy nhiên, mà vẫn sẽ lãng phí chu kỳ CPU.

Thay vào đó, những gì tôi muốn có một cái gì đó như:

<TextBlock Text="{z:DelayedSourceBinding SlowProperty}" /> 

nào sẽ cố gắng để có được những ràng buộc sau khi một sự chậm trễ nhất định. Vì vậy, ví dụ: nếu SlowProperty thay đổi 5 lần liên tiếp, trong một thời gian ngắn, thì chỉ văn bản cuối cùng mới hiển thị trong hộp văn bản.

Tôi đã tìm thấy following project mà thực hiện một cái gì đó như thế, vì vậy nó ví dụ của tôi, tôi có thể sử dụng nó như vậy:

<TextBox Text="{z:DelayBinding Path=SearchText}" /> 

Vấn đề với nó, là nó chỉ cập nhật các ràng buộc mục tiêu sau một sự trì hoãn. Đường dẫn nguồn, tuy nhiên, được đánh giá và getter của nó được thực thi khi mọi thay đổi của nguồn. Trong trường hợp của SlowProperty vẫn sẽ lãng phí chu kỳ CPU.

Tôi đã cố gắng tạo lớp ràng buộc bị trì hoãn của riêng mình, nhưng got stuck. Có chất kết dính nào khác có thể làm bất cứ thứ gì như thế không?

Đối với đầy đủ lợi ích, đây là 2 dự án khác thực hiện nhiệm vụ tương tự, tuy nhiên, không ai trong địa chỉ vấn đề tôi đang trải qua:

DeferredBinding - Một giải pháp tương tự để DelayBinding. Tuy nhiên, nó phức tạp hơn một chút để sử dụng.

DelayedBindingTextBox - Thực hiện ràng buộc bị trì hoãn bằng cách sử dụng điều khiển hộp văn bản tùy chỉnh.

Cảm ơn!

Trả lời

3

Tại sao không giải quyết được sự cố này trong mô hình chế độ xem? Nếu tài sản của bạn thay đổi nhanh chóng, nhưng rất chậm để có được, bạn có thể có một tài sản 'chậm trễ' thứ hai được hiển thị bởi mô hình khung nhìn của bạn. Bạn có thể sử dụng bộ hẹn giờ để cập nhật thuộc tính 'bị trễ' này định kỳ.

Hoặc, việc triển khai rõ ràng hơn có thể sử dụng chức năng Throttle do khung tiện ích mở rộng phản hồi cung cấp.

+0

Colin, tuyệt vời. Tất cả các vấn đề CS có thể được giải quyết bằng cách thêm một lớp khác nữa của sự hướng dẫn! :) –

+0

Bạn có lẽ đúng, đây thực sự là một vấn đề cần được giải quyết ở cấp VM. Mặc dù, tôi vẫn muốn biết nếu nó có thể làm một ràng buộc tùy chỉnh, giống như một trong những tôi mô tả. – VitalyB

1

Dường như với tôi những gì bạn thực sự muốn là trì hoãn thời điểm khi RaisePropertyChanged() được gọi.
Vì vậy, tôi đã cố gắng nó ra, và đây là một giải pháp:

Các XAML:

<StackPanel> 
    <TextBox Text="{Binding DelayedText, UpdateSourceTrigger=PropertyChanged}" /> 
    <TextBlock Text="{Binding DelayedText}" /> 
</StackPanel> 

C#:

public partial class MainWindow : Window, INotifyPropertyChanged 
    { 
     public MainWindow() 
     { 
      InitializeComponent(); 
      this.DataContext = this; 
     } 

     private String m_DelayedText; 
     public String DelayedText 
     { 
      get 
      { 
       return m_DelayedText; 
      } 
      set 
      { 
       if (m_DelayedText != value) 
       { 
        String delayedText; 
        m_DelayedText = delayedText = value; 
        Task.Factory.StartNew(() => 
         { 
          Thread.Sleep(2000); 
          if (delayedText == m_DelayedText) 
          { 
           RaisePropertyChanged("DelayedText"); 
          } 
         }); 
       } 
      } 
     } 

     public event PropertyChangedEventHandler PropertyChanged; 
     private void RaisePropertyChanged(String _Prop) 
     { 
      if (PropertyChanged != null) 
      { 
       PropertyChanged(this, new PropertyChangedEventArgs(_Prop)); 
      } 
     } 
    } 

Bạn có thể kiểm tra xem nó hoạt động bằng cách thiết lập một breakpoint tại RaisePropertyChanged("DelayedText")

Tôi hiểu rằng nó có thể trông giống như rất nhiều mã cho "chỉ" một thuộc tính.
Nhưng bạn có thể sử dụng đoạn mã hoặc chèn mã boilerplate vào thời gian chạy bằng cách sử dụng Resharper và tương tự.
Và dù sao, có thể bạn sẽ không cần đến nó thường xuyên.

Ngoài ra, nếu bạn sử dụng một cách tiếp cận khác (ví dụ, bằng cách tinh chỉnh một TextBox), bạn sẽ phải xử lý mọi nơi bạn liên kết với thuộc tính.
Bằng cách thực hiện theo cách này, trong trình cài đặt của thuộc tính, bạn đảm bảo rằng tất cả những người truy cập vào thuộc tính này bị giới hạn trong các bản cập nhật đã nhận.

HTH,

Bab.

+0

Giải pháp thú vị, cảm ơn! Nó thực sự là một chút nặng nề trên mã kết quả, nhưng nó là một đọc thú vị. – VitalyB

2

Tôi có yêu cầu tương tự trong khi tôi cần có khả năng trì hoãn cả nguồn và mục tiêu, vì vậy tôi đã tạo hai tiện ích mở rộng đánh dấu được gọi là DelayBindingDelayMultiBinding. Để trì hoãn một cập nhật cho nguồn bạn chỉ định UpdateSourceDelay

<TextBox Text="{db:DelayBinding SlowProperty, 
           UpdateSourceDelay='00:00:01'}" /> 

Các mã nguồn và sử dụng mẫu cho DelayBindingDelayMultiBinding có thể downloaded here.
Nếu bạn quan tâm đến các chi tiết thực hiện, bạn có thể kiểm tra bài viết trên blog của tôi về nó ở đây:
DelayBinding and DelayMultiBinding with Source and Target delay

1

Đáng chú ý, như của Net 4.5, một delay property đã được thêm vào khuôn khổ, cho phép bạn đặt số lượng độ trễ ràng buộc theo mili giây. Trong ví dụ của Microsoft, chế độ twoway được nhấn mạnh nhưng độ trễ ràng buộc hoạt động ở bất kỳ chế độ ràng buộc nào. Ví dụ, tôi đã sử dụng điều này trong một DataGrid, nơi mục/giá trị đã chọn phải được thay đổi cả từ một hộp văn bản trong một usercontrol tùy chỉnh và rõ ràng là từ bên trong DataGrid. Vì lý do tôi sẽ không đề cập đến ở đây, hộp văn bản phải liên kết với một thuộc tính khác với mô hình khung nhìn, nhưng cả hai thuộc tính phải có cùng giá trị vào cuối ngày và mọi thay đổi trong một trong số đó phải là phản ánh trên khác. Khi giá trị được chọn thay đổi trong datagrid, hộp văn bản phải được cập nhật quá, và tôi đã kiểm tra những thay đổi giá trị thực tế trong setter của thuộc tính SelectedValue binded, để ngăn chặn loop vô hạn. Khi thay đổi quá nhanh, đã xảy ra lỗi khi lưu dữ liệu trở lại nguồn khi văn bản thay đổi bên trong hộp văn bản đã được thay đổi bởi bộ chọn SelectedValue. Một khung chậm trễ hai cố định vấn đề mà không cần bất kỳ giải pháp phức tạp và không có cảm giác UI quá lag:

SelectedValue="{Binding SampleNumberSelect, Mode=OneWayToSource, Delay=33}" 

này rất thuận tiện và tiết kiệm cho bạn những rắc rối của việc thực hiện bất kỳ thay đổi như vậy trong mô hình xem, mà không cần thiết sẽ lộn xộn mã, bao gồm phải vứt bỏ bất kỳ bộ hẹn giờ nào trên đóng cửa sổ. Tất nhiên nó thậm chí không phải được sử dụng trong một kịch bản tương đối phức tạp như của tôi, nhưng nó có thể hữu ích để ngăn chặn mã nguồn CPU/tài nguyên chạy không cần thiết với mỗi thay đổi nhỏ trong giao diện người dùng.

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