2009-03-20 35 views
9

Tôi có Biểu mẫu trong ứng dụng của tôi hiển thị một số dữ liệu. Khi tôi lần đầu tiên hiển thị biểu mẫu, tôi tải một số dữ liệu vào một DataTable sau đó liên kết DataTable với một DataGridView. Tôi cũng bắt đầu một phương thức không đồng bộ thực hiện một số truy vấn cơ sở dữ liệu chậm hơn. Khi các truy vấn này chậm hoàn thành, tôi cần phải cập nhật một vài trăm hàng trong DataTable, điền vào các giá trị trở về từ các truy vấn chậm, như vậy:tối ưu hóa các cập nhật cho DataTable được ràng buộc với DataGridView

foreach (DataRow row in data.Rows) 
{ 
    SlowLoadingData slow_stuff = slow_query_results[(int)row["id"]]; 

    row.BeginEdit(); 
    row[column_one] = slow_stuff.One; 
    row[column_two] = slow_stuff.Two; 
    row[column_three] = slow_stuff.Three; 
    row.EndEdit(); 
} 

này là cực kỳ chậm, treo thread UI trong một phút hoặc nhiều hơn, có lẽ vì mỗi hàng đang kích hoạt vẽ lại.

Sau một số nghiên cứu, tôi đã tìm ra cách để thực hiện nhanh chóng. Đầu tiên, ràng buộc DataGridView với một BindingSource ràng buộc với DataTable, thay vì trực tiếp đến DataTable. Sau đó, hãy làm như sau khi bạn thực hiện thay đổi đối với DataTable:

binding_source.SuspendBinding(); 
binding_source.RaiseListChangedEvents = false; 
// foreach (DataRow in Data.Rows) ... code above 
binding_source.RaiseListChangedEvents = true; 
binding_source.ResumeBinding(); 
grid.Refresh(); 

Có một vấn đề, tuy nhiên, và đó là một doozy: mã trên ngăn DataGridView từ phát hiện hàng mới được thêm vào DataTable. Bất kỳ hàng mới nào được thêm vào bảng sẽ không xuất hiện trong lưới. Lưới cũng có thể ném ngoại lệ nếu bạn sử dụng các phím mũi tên để di chuyển lựa chọn ô hiện tại khỏi đầu dưới của lưới, vì nguồn dữ liệu cơ bản có nhiều hàng hơn nhưng lưới không tạo hàng lưới để hiển thị chúng.

Vì vậy, hai giải pháp khả thi mà tôi có thể thấy:

  1. Có cách nào tốt hơn để ngăn chặn cập nhật ràng buộc khi thực hiện thay đổi đối với DataTable tiềm ẩn?

  2. Có cách nào dễ dàng để cho DataGridView làm mới bộ sưu tập hàng lưới của nó một cách duyên dáng để phù hợp với số lượng các hàng DataTable cơ bản không? (Lưu ý: Tôi đã cố gắng gọi BindingSource.ResetBindings, nhưng có vẻ như để kích hoạt nhiều trường hợp ngoại lệ nếu bạn có loại bỏ hàng từ DataTable)

+0

Tôi không biết nhiều về việc sử dụng nguồn ràng buộc nhưng thực hiện quá trình khôi phục trước khi đặt raiselistchangedevents thành sự thật có khác biệt gì không? – eglasius

+0

Tôi đã thử tất cả các lệnh có thể gọi các phương thức. Tất cả những gì được yêu cầu cho lưới để vít lên là RaiseListChangedEvents là sai khi hàng mới được thêm vào. –

+0

Gọi hàm có tên ResetBindings để buộc giao diện người dùng cập nhật. – CodingBarfield

Trả lời

4

Bạn đã xem là ngắt kết nối DataGrid hoặc BindingSource trong khi điền vào bảng và kết nối lại sau đó? Nó có thể trông hơi xấu xí, nhưng nó sẽ nhanh hơn rất nhiều.

+0

Tính năng này hoạt động nhưng không phải là trải nghiệm người dùng dễ chịu, phần lớn là do lưới sẽ cuộn trở lại đầu trang khi bạn hủy liên kết rồi gắn lại bảng. Có lẽ tôi chỉ là một người lạc quan nhưng tôi hy vọng tìm thấy một giải pháp duyên dáng hơn. –

+0

Bạn có thể định vị lại hàng, và tinh chỉnh nó một chút để thu nhỏ lại quá trình vẽ lại màn hình. Có lẽ kết nối lại lưới với nguồn như bước cuối cùng. –

6

Bạn có thể thử sử dụng Merge method trên DataTable. Tôi sẽ cố gắng tạo một ứng dụng demo đơn giản và đăng nó ở đây, nhưng ý tưởng thì đơn giản. Khi bạn muốn cập nhật Grid, truy vấn kết quả vào một DataTable mới, sau đó hợp nhất bảng cũ với bảng mới. Miễn là cả hai bảng có các khóa chính (bạn có thể tạo chúng cho chúng nếu chúng không quay trở lại từ DB) thì nó sẽ theo dõi các thay đổi và cập nhật DataGridView một cách liền mạch. Nó cũng có lợi thế là không làm mất vị trí của người dùng trên lưới điện.

OK, đây là mẫu. Tôi tạo một biểu mẫu với hai nút và một dataGridView. Khi nhấn button1, tôi điền bảng chính với một số dữ liệu và kết nối lưới với nó. Sau đó, trên nhấp chuột thứ hai, tôi tạo một bảng khác với cùng một lược đồ. Thêm dữ liệu vào nó (một số có cùng khóa chính và một số có khóa mới). Sau đó, họ hợp nhất chúng trở lại bảng gốc. Nó cập nhật lưới như mong đợi.

public partial class Form1 : Form 
    { 
     private DataTable mainTable; 
     public Form1() 
     { 
      InitializeComponent(); 
      this.mainTable = this.CreateTestTable(); 
     } 

     private void button1_Click(object sender, EventArgs e) 
     { 
      for (int i = 1; i <= 10; i++) 
      { 
       this.mainTable.Rows.Add(String.Format("Person{0}", i), i * i); 
      } 

      this.dataGridView1.DataSource = this.mainTable; 
     } 

     private void button2_Click(object sender, EventArgs e) 
     { 
      DataTable newTable = this.CreateTestTable(); 
      for (int i = 1; i <= 15; i++) 
      { 
       newTable.Rows.Add(String.Format("Person{0}", i), i + i); 
      } 
      this.mainTable.Merge(newTable); 
     } 

     private DataTable CreateTestTable() 
     { 
      var result = new DataTable(); 
      result.Columns.Add("Name"); 
      result.Columns.Add("Age", typeof(int)); 
      result.PrimaryKey = new DataColumn[] { result.Columns["Name"] }; 

      return result; 

     } 
    } 
+0

Đề xuất hay. –

+0

Điều này có vẻ hiệu quả và giải quyết tình trạng khó xử hàng mới, nhưng tôi thấy hai nhược điểm: lưới dường như đặt lại cuộn của nó thành * hàng đã chọn * (không giống như "địa điểm của người dùng") và hợp nhất chính xác yêu cầu bạn tạo một bản sao của toàn bộ bảng mục tiêu (có thể là lớn). –

+0

1) đặt lại giao diện người dùng chính xác sẽ rất khó - và khó xác định. 2) Không có "bản sao của toàn bộ bảng mục tiêu" đang diễn ra. Và những gì là lớn những ngày này? –

3

Nếu bạn đang sử dụng một BindingSource cho dữ liệu phức tạp ràng buộc, điều quan trọng là phải hiểu rằng SuspendBindingResumeBinding chỉ ngưng và tiếp tục ràng buộc cho mục hiện hành. Điều này cho phép bạn vô hiệu hóa ràng buộc cho mục hiện tại và thay đổi một loạt các thuộc tính của nó mà không có bất kỳ thay đổi riêng lẻ nào cho thuộc tính bị đẩy ra khỏi điều khiển bị ràng buộc. (Điều này không được giải thích trong tài liệu cho BindingSource, nơi nó sẽ hữu ích, oh không: nó nằm trong tài liệu hướng dẫn cho CurrencyManager.)

Bất kỳ thay đổi nào bạn thực hiện cho các mục khác trong danh sách, tức là mọi thứ ngoại trừ mục hiện tại, tăng sự kiện ListChanged. Nếu bạn vô hiệu hóa các sự kiện này, BindingSource sẽ ngừng nói cho kiểm soát bị ràng buộc về những thay đổi trong danh sách cho đến khi bạn bật lại chúng. Điều này có kết quả bạn đã thấy: bạn thêm tất cả các hàng của mình vào số DataTable bên dưới, nhưng vì bạn đã tắt ListChanged sự kiện, BindingSource không cho biết DataGridView về chúng và do đó DataGridView vẫn trống.

Giải pháp thích hợp là gọi ResetBindings, buộc BindingSource làm mới tất cả các điều khiển được liên kết với nó, sử dụng các giá trị hiện tại trong danh sách bị ràng buộc của nó.

Bạn nhận được loại ngoại lệ nào sau khi gọi ResetBindings? Bởi vì nó hoạt động tốt cho tôi, cho dù tôi thêm, chỉnh sửa, xóa hoặc xóa các hàng khỏi số DataTable bên dưới.

+0

Thử nghiệm với mã đồng nghiệp của tôi, chúng tôi đã quan sát thấy rất nhiều điều kỳ lạ sau khi gọi ResetBindings, bao gồm các hàng lưới ảo và các ngoại lệ IndexOutOfRange được ném bởi lưới. Sẽ thử nghiệm nhiều hơn ngày hôm nay để xem liệu có điều gì khác đang diễn ra hay không. –

2

Tôi đã gặp sự cố tương tự. Đây là một giải pháp thậm chí còn đơn giản hơn (nếu ít thanh lịch hơn).

tôi thấy rằng đây:

dataGridView.DataSource = null; 
dataTable.BeginLoadData(); 
foreach(DataRow row in dataTable.Rows){ 
    //modify row 
} 
dataTable.EndLoadData(); 
dataGridView.DataSource = dataTable; 

... là cách nhanh hơn này:

dataTable.BeginLoadData(); 
foreach(DataRow row in dataTable.Rows){ 
    //modify row 
} 
dataTable.EndLoadData(); 

Cheers - DC

0

tôi thấy rằng việc sử dụng resetBindings dường như di chuyển thanh cuộn và người dùng còn lại suy nghĩ tôi đã làm gì? Tôi thấy rằng bằng cách sử dụng một bindingList như một nguồn dữ liệu, với một đối tượng sử dụng INotifyPropertyChanged, và sau đó khi tôi chỉnh sửa hàng, (ràng buộc với một đối tượng). hàng không được cập nhật cho đến khi nhấp chuột hoặc lựa chọn thay đổi trên biểu mẫu.

nhưng gọi dgv.Refresh() dường như giải quyết được sự cố, không có thay đổi cuộn.

0

tôi tìm ra giải pháp bằng cách Ravi LVS trên CodeProject hoạt động tốt:

BindingSource bs = new BindingSource(); 
DataTable dt = new DataTable(); 

bs.DataSource = dt; 
bs.SuspendBinding(); 
bs.RaiseListChangedEvents = false; 
bs.Filter = "1=0"; 
dt.BeginLoadData(); 

//== some modification on data table 

dt.EndLoadData(); 
bs.RaiseListChangedEvents = true; 
bs.Filter = ""; 

Liên kết đến trang gốc: http://www.codeproject.com/Tips/55730/Achieve-performance-while-updating-a-datatable-bou

1

Chỉ cần đăng này như một giải pháp: Làm một số ký hiệu để các ý kiến ​​và bài viết rồi. Phương pháp Merge Table được BFree đề cập là một phương pháp rất tốt để sử dụng và tôi nghĩ là cách tiếp cận đúng không quá đề cập đến rất đơn giản và thanh lịch. Đây là ghi chú của tôi tại sao và phần lớn tôi không chắc liệu có ai bị bắt hay không là các truy cập trên máy chủ cho truy vấn. Các op được nêu trong ý kiến ​​của mình để bfree rằng ông sẽ cần phải sao chép các bảng để làm những gì anh cần đến, tất nhiên mà bảng đó là tôi không chắc chắn, bởi vì mã của mình:

foreach (DataRow row in data.Rows) 

Những hàng đến từ bảng của ông được gọi là dữ liệu mà bản sao được yêu cầu trên đó - ông đã có nó.

Sau đó, đây là một cái gì đó mà chỉ smacks ngay trên mỗi lần lặp của vòng lặp rằng:

SlowLoadingData slow_stuff = slow_query_results[(int)row["id"]]; 

là OP thực sự truy vấn cơ sở dữ liệu mỗi lần lặp của những hàng (Mà nếu nó là một bảng lớn chúng ta đang nói 100.000 hàng +). Hãy suy nghĩ về tải trên máy chủ (ứng dụng của mình cũng phải tạo yêu cầu truy vấn này!), Và cũng là lượng lưu lượng truy cập mà nó đặt trên mạng để thực hiện việc này! NẾU nó là ứng dụng duy nhất có thể nó là ok, nhưng ngay cả ở đó nó không phải là những gì tôi muốn làm gì nếu tôi muốn được hiệu quả.

Nếu thu thập dữ liệu từ cơ sở dữ liệu trong một truy vấn có vẻ như nhiều - thì có lẽ phương pháp tốt hơn là trang dữ liệu của anh ấy vào và thực hiện hợp nhất.

SlowLoadingData Page1_SlowLoadingData = slow_query_results[Page1] as DataTable; 
data.Merge(Page1_SlowLoadingData); 

SlowLoadingData Page2_SlowLoadingData = slow_query_results[Page2] as DataTable; 
data.Merge(Page2_SlowLoadingData); 
Các vấn đề liên quan