2012-01-26 44 views
10

Làm việc trên một dự án hiện có, tôi phải sử dụng WinForms (không làm việc với nó một thời gian) và đã gặp sự cố khi đồng bộ hóa với chuỗi giao diện người dùng.Nhân viên đồng bộ với chuỗi giao diện người dùng

Thiết kế tôi phải tích hợp với các công trình như sau: A BackgroundWorker nhận một tham số Action và thực thi nó một cách không đồng bộ. Hành động tôi đang làm việc có hai phần; một lớp lõi (có chứa logic nghiệp vụ) và một phần GUI được thông báo bởi lõi thông qua các sự kiện nếu nó phải yêu cầu tương tác của người dùng.

Tôi đã thêm việc tạo xử lý để các nhà xây dựng có dạng

if (!IsHandleCreated) 
{ 
    //be sure to create the handle in the constructor 
    //to allow synchronization with th GUI thread 
    //when using Show() or ShowDialog() 
    CreateHandle(); 
} 

Với điều này, đoạn mã sau hoạt động:

private DialogResult ShowDialog(Form form) 
{ 
    DialogResult dialogResult = DialogResult.None; 
    Action action = delegate { dialogResult = form.ShowDialog(); }; 
    form.Invoke(action); 
    return dialogResult; 
} 

Đối với ví dụ này, địa điểm khởi động đã được thiết lập để cửa sổ mặc định.

Nếu tôi thay đổi nó để:

Action action = delegate { dialogResult = form.ShowDialog(ParentWindow); }; 

đâu ParentWindow là một thể hiện của IWin32WindowWindowStartupLocation được thiết lập để CenterParent. Tôi nhận được một ngoại lệ cross-thread khi gọi form.Invoke(action).

Thao tác chéo không hợp lệ: Kiểm soát 'Kích hoạt xác nhậnĐược tìm thấy' từ một chuỗi khác với chuỗi được tạo.

Câu hỏi:

  • Tại sao lại có một sợi ngoại lệ chéo chỉ khi thiết lập các vị trí khởi động như CenterParent? Và làm thế nào để tránh nó?
  • Tại sao luôn là false?

Cả hai đều có thể liên quan !?

[sửa] @Reniuz: Bạn sẽ không bỏ lỡ bất cứ điều gì ở đây;) Cuộc gọi đang được thực hiện từ một người nghe được thông báo của lõi

private static void OnActivationConfirmationRequired(DmsPackageConfiguratorCore sender, 
ConfigurationActivationConfirmationEventArgs args) 
{ 
    args.DoAbort = (ShowDialog(new ActivationConfirmationForm(args.Data)) == DialogResult.No); 
} 

Tất cả mọi thứ theo ý của tôi là trong GUI interface

/// <summary> 
/// Interface defining methods and properties used to show dialogs while performing package specific operations 
/// </summary> 
public interface IPackageConfiguratorGui 
{ 
/// <summary> 
/// Gets or sets the package configurator core. 
/// </summary> 
/// <value>The package configurator core.</value> 
IPackageConfiguratorCore PackageConfiguratorCore { get; set; } 

/// <summary> 
/// Gets or sets the parent window. 
/// </summary> 
/// <value>The parent window.</value> 
IWin32Window ParentWindow { get; set; } 

/// <summary> 
/// Gets the package identifier. 
/// </summary> 
/// <value>The package identifier.</value> 
PackageIdentifier PackageIdentifier { get; } 
} 
+0

là trường hợp ngoại lệ vượt qua do 'CenterParent' hoặc vì bạn đang thực sự đặt cửa sổ chính (tức là nó vẫn kích hoạt nếu bạn xóa cài đặt' CenterParent')? – Strillo

+0

@Strillo luôn luôn kích hoạt khi thiết lập biểu mẫu ParentWindow – Philippe

+0

gọi để hiển thị chính nó? Hay Iam thiếu cái gì ở đây? Bạn có thể đăng tất cả mã không? – Reniuz

Trả lời

3

Xem biểu mẫu.InvokeRequired tại false là cốt lõi của vấn đề của bạn. Bạn biết nó phải đúng. Lời giải thích đơn giản là đối tượng biểu mẫu được truyền cho phương thức ShowDialog() của bạn là đối tượng sai. Lỗi cổ điển là sử dụng mới để tạo cá thể thay vì sử dụng phiên bản hiện tại của đối tượng biểu mẫu, đối tượng mà người dùng đang xem và được tạo trên chuỗi chính. Đảm bảo rằng mã chuỗi có tham chiếu đến đối tượng biểu mẫu đó để nó có thể chuyển tham chiếu chính xác. Chỉ sử dụng Application.OpenForms [0] nếu bạn không thể làm đúng.

Nói chung, tách mã chuỗi khỏi giao diện người dùng. Chuỗi công nhân không có doanh nghiệp hiển thị hộp thoại. Bạn có thể làm cho nó hoạt động nhưng nó không hoạt động tốt trong thực tế. Hộp thoại bật lên mà không cần người dùng mong đợi. Làm cho tai nạn có khả năng, người dùng có thể bấm hoặc bấm một phím một phần nhỏ của một giây trước khi hộp thoại bật lên. Loại bỏ hộp thoại mà không cần nhìn thấy nó. Các CreateHandle() hack tương tự nên không là trong mã của bạn. Chỉ cần không bắt đầu chuỗi cho đến khi giao diện người dùng sẵn sàng. Được báo hiệu bởi sự kiện Tải trọng của biểu mẫu.

+0

Cảm ơn bạn đã giải thích! Biểu mẫu này thực sự được tạo khi đang chạy trong phần GUI của nhân viên chứ không phải bởi luồng GUI. Đây là vấn đề thiết kế mà tôi không thể thay đổi. Người dùng sẽ phải sống với các cuộc đối thoại xuất hiện ở vị trí mặc định của windows;) – Philippe

1

ok Tôi không có quyền "bình luận" vì tôi là người dùng mới, vì vậy tôi sẽ chỉ sử dụng không gian trả lời này.

bạn đang tạo một phiên bản mới của biểu mẫu ActivationConfirmationForm, bất kỳ chuỗi nào bạn đang tạo, biểu mẫu này và việc thực thi ShowDialog đang thực hiện trong cùng một ngữ cảnh luồng, vì điều này đúng, InvokeRequired (xem msdn) rõ ràng là sẽ sai vì biểu mẫu mà bạn muốn truy cập được tạo trong chuỗi mà bạn đang truy cập vào nó. Không cần phải sử dụng gọi/begininvoke vv Loại của những gì @reniuz đã lo lắng về.

1

Biểu mẫu thuộc về một chuỗi khác với chuỗi gốc.Có vẻ như đối số WinForms CenterParent làm cho một cuộc gọi đến đối tượng WinForms .Net thay vì sử dụng API Win32 để tìm vị trí cửa sổ cha mẹ từ HWND và cuộc gọi cross-thread này là nguyên nhân gây ra ngoại lệ cho chuỗi chéo.

Câu trả lời thực sự là chuỗi công việc không được có giao diện người dùng. Họ nên đưa ra một kết quả cho thấy rằng sự can thiệp của người dùng là cần thiết và chủ đề chính nên chăm sóc tương tác của người dùng.

Nếu không, không đặt cửa sổ chính cho GUI của công nhân. Nó có thể (chỉ) là hoàn toàn khả thi nếu bạn chỉ có một chuỗi công nhân, nhưng nó sẽ gây ra tất cả các loại lẫn lộn nếu bạn có nhiều hơn không.

Nếu bạn hoàn toàn phải, hãy sử dụng P/Gọi để tìm vị trí cửa sổ hiện tại của cửa sổ chính từ Win32API và đặt rõ ràng.

0

Làm theo dõi:
Tôi hiện đã có giải pháp làm việc (đối thoại không phải là phương thức mà không đặt bố mẹ). Tôi sẽ đồng ý nó không phải là điều tốt nhất để khởi động một cuộc đối thoại từ một sợi công nhân, nhưng trong trường hợp này nó là một yêu cầu. ParentForm bây giờ là một Form, như trái ngược với một IWin32Window trước

Tạo cuộc đối thoại trong các chủ đề đồ họa:

private void OnActivationConfirmationRequired(DmsPackageConfiguratorCore sender, ConfigurationActivationConfirmationEventArgs args) 
{ 
    //create the dialog in the graphical thread 
    ActivationConfirmationForm dialog = null; 
    Action createDialogInGuiThread =() => dialog = new ActivationConfirmationForm(args.Data); 
    ParentForm.Invoke(createDialogInGuiThread); 

    if (dialog != null) 
    { 
     args.DoAbort = (ShowDialog(dialog) == DialogResult.No); 
    } 
} 

Gọi cuộc đối thoại từ thread UI

private DialogResult ShowDialog(Form form) 
{ 
    DialogResult dialogResult = DialogResult.None; 

    //launch the form in the graphical htread (the one of the parent form) 
    Action action = delegate { dialogResult = form.ShowDialog(ParentForm); }; 
    ParentForm.Invoke(action); 

    return dialogResult; 
} 

Như tất cả những thứ đồ họa đang được thực hiện trong chuỗi giao diện người dùng, không cần tạo trình xử lý nữa.

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