2009-06-24 6 views
7

Tôi có một loại mô hình plug-in, trong đó người dùng điều khiển phức tạp khác nhau được lưu trữ trong file DLL và nạp và khởi tạo lúc thực thi bằngTạo điều khiển trong một tổ chức phi thread UI

Activator.CreateInstanceFrom(dllpath, classname). 

Kể từ khi tôi tải khá một vài trong số này tôi muốn làm điều đó trong nền, giữ giao diện người dùng của tôi đáp ứng, bằng cách tạo ra một chủ đề mới để làm việc tải. Các điều khiển sau đó được parented vào hình thức chính và hiển thị khi cần thiết.

Điều này có vẻ hoạt động tốt - cho đến khi tôi cố gắng đặt bất kỳ thuộc tính nào trên bất kỳ kiểm soát lồng nhau nào trên một trong các điều khiển người dùng này, ví dụ: trong xử lý sự kiện của một nút, trong đó ném một ngoại lệ luồng chéo. Tôi nhận ra rằng tôi có thể tránh điều này bằng cách kiểm tra InvokeRequired mỗi khi tôi truy cập một thuộc tính, nhưng tôi không phải lo lắng về điều đó khi viết mã cho các điều khiển người dùng (đặc biệt là vì có những người khác viết các mã này không phải lúc nào cũng nhớ).

Vì vậy, câu hỏi của tôi là, có cách nào an toàn để làm những gì tôi đang cố gắng hay không hoặc làm cách nào tốt nhất tôi nên tải các điều khiển này xuống dưới nền? Hoặc là nó về cơ bản không thể và tôi có phải dính vào thread chính để tạo điều khiển?

Tôi hy vọng rằng thông tin tôi đã cung cấp đủ để làm cho tình hình của tôi rõ ràng; nếu không, tôi sẽ rất vui khi xây dựng và cung cấp các mẫu mã.

+0

"cho đến khi tôi cố gắng đặt bất kỳ thuộc tính nào" - đó là trong Chủ đề hay sau này? –

+0

Đó là sau, sau khi kiểm soát người dùng đã được parented để hình thức chính (như trong form.controls.add (mycontrol)) –

Trả lời

3

Có thể tải các tệp DLL và tạo đối tượng điều khiển trong nền, nhưng điều khiển phải được thêm vào biểu mẫu trong chuỗi chính và tất cả tương tác của người dùng cũng như bất kỳ thay đổi chương trình nào của thuộc tính điều khiển (sau khi được tạo) phải xuất hiện trong chuỗi chính. Chỉ đơn giản là không có cách nào xung quanh điều này, như bạn có thể đã biết. Bây giờ, trừ khi tải của các DLL và kiểm soát sáng tạo mất lứa tuổi, tôi thấy không có lý do để làm điều đó trong các chủ đề nền riêng biệt.

Nếu các hành động nhất định được thực hiện bởi các điều khiển đang chặn giao diện người dùng và bạn muốn chúng xuất hiện dưới nền, đây là một câu chuyện khác và bạn nên xem xét từng hành động trên cơ sở cá nhân và tạo phương pháp rõ ràng để giao tiếp giữa chuỗi giao diện người dùng và chủ đề nền.

Cố gắng thực hiện một khung chung phù hợp với chế độ chuỗi giao diện người dùng và ở chế độ nền và chỉ cần kiểm tra kết quả InvokeRequired đôi khi ở hiệu suất tồi tệ hơn (vì tất cả chuỗi đều bị chặn trong Gọi) hoặc ngay cả trong (không bị phát hiện) deadlocks ngay sau khi ứng dụng đạt đến một phức tạp hợp lý. Cũng làm tất cả các cập nhật async trong BeginInvoke, w/o xem xét từng phương pháp riêng lẻ, có thể dẫn đến các vấn đề thống nhất dữ liệu (tức là các điều khiển có thể tự cập nhật trạng thái trở lại trong thời gian do đảo ngược thứ tự gọi giữa các chuỗi).

+0

Vâng, tôi có cảm giác rằng tôi đã cố gắng để chỉ là một chút quá thông minh. Tôi đã nhiều hơn hoặc ít hơn bỏ rơi nỗ lực đầu tiên của tôi và đang di chuyển dọc theo các dòng tương tự như đoạn thứ hai của bạn. Cảm ơn những lời khuyên ngang hàng. 3; Tôi đã không nhận ra điều đó. –

+0

Cá nhân tôi thích sử dụng hàng đợi (http://msdn.microsoft.com/en-us/library/7977ey2c.aspx) để liên lạc giữa các chủ đề: chuỗi nền thêm các mục ('kết quả') vào hàng đợi, Chủ đề giao diện người dùng kiểm tra hàng đợi trên Idle và cập nhật các điều khiển (đây là nhà sản xuất/người tiêu dùng điển hình). Nhưng bạn phải xem xét cẩn thận nếu nền không thể vượt qua khả năng của UI để theo kịp, điều này có thể dễ dàng xảy ra, đặc biệt với lưới, khiến hàng đợi phát triển ngoài tầm kiểm soát trừ khi bạn thực hiện các biện pháp đặc biệt. –

1

Mẫu mã trong this answer cung cấp một cách thanh lịch xung quanh vấn đề này.

Mã trích dẫn từ câu trả lời rằng:

public void UpdateTestBox(string newText) 
{ 
    BeginInvoke((MethodInvoker) delegate { 
     tb_output.Text = newText; 
    });   
} 

... mặc dù trong trường hợp của bạn, bạn sẽ muốn gọi BeginInvoke trên các điều khiển bản thân:

public void UpdateTestBox(string newText) 
{ 
    tb_output.BeginInvoke((MethodInvoker) delegate { 
     tb_output.Text = newText; 
    });   
} 
+0

Tôi không hoàn toàn nhìn thấy nhưng làm thế nào điều này sẽ giúp đỡ. Ý tôi là, vâng, bạn nói đúng, tất nhiên, bằng cách sử dụng Invoke/BeginInvoke sẽ giúp đỡ, nhưng đó chính xác là những gì tôi đang cố gắng tránh. Hoặc có lẽ tôi chỉ không hiểu những gì bạn đang nhận được. –

+1

Nếu bạn tạo các điều khiển trên một chủ đề khác với chuỗi giao diện người dùng, tôi không thể xem bạn sẽ thoát khỏi Invoke/BeginInvoke như thế nào. Tuy nhiên, mã được đề xuất đơn giản hơn nhiều so với biến thể if (ctl.InvokeRequired) [...]. Các hình phạt ngay lập tức đi multithreaded là bạn cần phải làm đồng bộ hóa của bạn;) –

1

Không biết các chi tiết của cơ chế InvokeRequired , Tôi đã thử nghiệm một chút và afaik bạn có thể thiết lập hầu hết các thuộc tính trong một Thread miễn là nó chưa được Parented (tức là thêm vào một số thuộc tính Control.Controls).

Vì vậy, bạn sẽ có thể chuẩn bị các điều khiển của mình, lưu trữ chúng trong danh sách và đính kèm chúng vào giao diện người dùng chính trong phương thức được viện dẫn.


Chỉnh sửa: Tôi không nghĩ điều quan trọng là Chủ đề đã tạo Kiểm soát. Vì vậy, các quy tắc bình thường nên áp dụng, nghĩa là bạn chỉ có thể làm việc với các điều khiển tạo thành chuỗi Windows chính. Và tôi nghĩ tiêu chí là HandleCreated hơn là Parented. Miễn là điều đó đã không xảy ra bạn sẽ có được một chút nghỉ ngơi.

+0

Vâng, có vẻ như nuôi dạy con cái là những gì giết chết tôi. Ý tưởng của bạn không quá tệ, nhưng sẽ không giúp ích gì, vì các thuộc tính tôi đang cài đặt đều phản ứng với một số tương tác của người dùng, nói cách khác * sau * điều khiển đã được biểu thị bằng biểu mẫu chính và được hiển thị. Điều đó có thể không rõ ràng từ câu hỏi của tôi. –

+0

Nó đã được rõ ràng nhưng tôi đang làm điều đó w/o vấn đề. –