2008-10-14 31 views
6

Tôi đang viết một ứng dụng WinForms có hai chế độ: bảng điều khiển hoặc GUI. Ba dự án trong cùng một giải pháp, một cho ứng dụng giao diện điều khiển, một cho các biểu mẫu giao diện người dùng và thứ ba để giữ logic mà cả hai giao diện đều sẽ kết nối. Ứng dụng Console chạy hoàn toàn suôn sẻ.Lỗi giao diện người dùng chéo luồng lạ

Mô hình giữ lựa chọn người dùng, có một số IList<T> trong đó T là đối tượng cục bộ, Step, thực hiện INotifyPropertyChanged, do đó, trong giao diện người dùng này được gắn kết vào một DataGridView. Tất cả là tốt khi chạy, trạng thái ban đầu của các đối tượng được phản ánh trên màn hình.

Mỗi đối tượng Step là một tác vụ được thực hiện lần lượt; một số thuộc tính sẽ thay đổi, được phản ánh trở lại IList và truyền cho DataGridView.

Hành động này trong các phiên bản giao diện người dùng được thực hiện bằng cách tạo một sự kiện nâng cao BackgroundWorker trở lại giao diện người dùng. Các Step hiện điều đó và tạo ra một đối tượng StepResult mà là một loại liệt kê cho thấy một kết quả (ví dụ như chạy, NotRun, OK, NotOK, Caveat) và một chuỗi để chỉ ra một thông báo (vì bước chạy nhưng không hoàn toàn như mong đợi, tức là với một Caveat). Thông thường các hành động sẽ liên quan đến một tương tác cơ sở dữ liệu, nhưng trong chế độ gỡ lỗi, tôi ngẫu nhiên tạo ra một kết quả.

Nếu thông báo là null, không bao giờ có một vấn đề, nhưng nếu tôi tạo ra một phản ứng như thế này:

StepResult returnvalue = new StepResult(stat, "completed with caveat") 

tôi nhận được một thông báo lỗi rằng DataGridView đã được truy cập từ một thread khác với thread nó đã được tạo vào. (Tôi đang vượt qua điều này thông qua một trình xử lý tùy chỉnh sẽ xử lý lời gọi khi được yêu cầu - có thể không?)

Sau đó, nếu tôi tạo ra một phản hồi duy nhất, ví dụ: sử dụng số ngẫu nhiên r:

StepResult returnvalue = new StepResult(stat, r.ToString()); 

các hành động thành công mà không có vấn đề gì, các con số được viết gọn gàng vào DataGridView.

Tôi bị bối rối. Tôi giả sử nó bằng cách nào đó một vấn đề chữ chuỗi, nhưng bất cứ ai có thể đưa ra một lời giải thích rõ ràng hơn?

Trả lời

3

Vì bạn đang làm UI ràng buộc qua thuê bao sự kiện, you might find this helpful ; nó là một ví dụ tôi đã viết một thời gian trước đây cho thấy làm thế nào để phân lớp BindingList<T> để các thông báo được marshalled đến thread UI tự động.

Nếu không có bối cảnh đồng bộ hóa (tức làgiao diện điều khiển), sau đó nó trở lại gọi đơn giản trực tiếp, vì vậy không có phí. Khi chạy trong chuỗi giao diện người dùng, lưu ý rằng điều này về cơ bản sử dụng Control.Invoke, bản thân nó chỉ chạy ủy nhiệm trực tiếp nếu nó nằm trên chuỗi giao diện người dùng. Vì vậy, chỉ có bất kỳ chuyển đổi nào nếu dữ liệu đang được chỉnh sửa từ một chuỗi không phải là giao diện người dùng - hãy chọn những gì chúng tôi muốn ;-p

+0

Không chính xác câu trả lời - nhưng dọc theo dòng bên phải! – Unsliced

+0

Perfick! Chính xác những gì tôi cần để liên kết với các bộ sưu tập trong một DLL từ Winforms và WPF khách hàng, cảm ơn. – Wonko

+0

Nó chỉ hoạt động nếu BindingList được tạo trong chuỗi giao diện người dùng, phải không? Nhưng có bất kỳ giải pháp nào, nếu BindingList <> được tạo bên ngoài luồng giao diện người dùng không? Và Không, chúng tôi không thể chuyển ISynchronizeInvoke hoặc SynchronizationContext tương ứng thành BindingList :( –

4

Bạn đã trả lời quesion của riêng bạn: -

tôi nhận được một thông báo lỗi rằng DataGridView đã được truy cập từ một thread khác với thread nó đã được tạo ra về.

WinForms khẳng định rằng tất cả các hành động được thực hiện trên biểu mẫu và điều khiển được thực hiện trong ngữ cảnh của biểu mẫu được tạo. Nguyên nhân là phức tạp, nhưng có rất nhiều việc phải làm với API Win32 cơ bản. Để biết chi tiết, hãy xem các mục nhập khác nhau trên blog The Old New Thing.

Những gì bạn cần làm là sử dụng InvokeRequired và Gọi phương pháp để đảm bảo rằng các điều khiển luôn được truy cập từ các chủ đề tương tự (pseudocodeish):

object Form.SomeFunction (args) 
{ 
    if (InvokeRequired) 
    { 
    return Invoke (new delegate (Form.Somefunction), args); 
    } 
    else 
    { 
    return result_of_some_action; 
    } 
} 
+0

Tôi đánh giá cao tình hình InvokeRequired, nhưng tại sao nó hoạt động khi tôi vượt qua trong một chuỗi duy nhất, nhưng không phải khi đi qua trong một chuỗi hard-wired? – Unsliced

+0

Có phải hai câu lệnh được gọi từ cùng một vị trí trong cùng một ngữ cảnh không? – Skizz

+0

Có. Hai ví dụ trong câu hỏi ở cùng một chỗ - nếu tôi sử dụng một THÀNH CÔNG! Nếu tôi sử dụng khác - KHÔNG PHẢI! Và nếu Message không được chạm, nhưng chỉ trạng thái được cập nhật, thì không có lỗi (nhưng Grid được cập nhật). Tôi vẫn còn bối rối! – Unsliced

0

tôi tìm thấy bài viết này - "Updating IBindingList from different thread" - mà chỉ ngón tay của đổ lỗi cho các BindingList -

Bởi vì BindingList không được thiết lập cho các hoạt động async, bạn phải cập nhật BindingList từ cùng một chủ đề mà nó đã được kiểm soát.

Hiển thị rõ ràng biểu mẫu gốc làm đối tượng ISynchronizeInvoke và tạo trình bao bọc cho BindingList<T>.

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