2016-09-21 67 views
1

我創建一個應用程序,它啓動一個FormSelect(),要求用戶在列表中選擇一個項目,當用戶按下「Select」按鈕時,它將創建一個MainForm的新實例),隱藏FormSelect並顯示MainForm,但它不工作,我得到一個異常,我做了所有的測試。表單不顯示線程

下面是一些代碼:

static class Program { 
     public static MainForm mainForm; 
     /// <summary> 
     /// The main entry point for the application. 
     /// </summary> 
     [STAThread] 
     static void Main() { 
      Application.EnableVisualStyles(); 
      Application.SetCompatibleTextRenderingDefault(false); 
      Application.Run(new FormSelect()); 
     } 
    } 

沒什麼特別的我FormSelect,所以這裏是點擊該按鈕在那裏開始的MainForm:

private async void btnSelect_Click(object sender, EventArgs e) { 

      loadingSpiner.Visible = true; 
      btnSelect.Enabled = false; 

      int index = listProcess.SelectedIndex; 

      await startMainForm(index); 

      this.Hide(); 
      Program.mainForm.Show(); 
     } 

private async Task startMainForm(int index) { 

      await Task.Run(() => { 
       Program.mainForm = new MainForm(runningProcess[index]); 
      }); 
     } 

正如你可以在上面看到,我使用任務啓動MainForm,所以它不凍結我的用戶界面和我的「加載spiner」

但是,當它嘗試使用.Show()我得到一個跨線程異常,所以我試圖調用該actio N使用:

this.Invoke(new MethodInvoker(delegate() { Program.mainForm.Show(); })); 

但是,使用上面的方法我得到一個異常說:

型「System.ComponentModel.Win32Exception」發生在System.Windows.Forms.dll中的一個例外,但在沒有處理用戶代碼

附加信息:創建窗口句柄時出錯。

如果我在startMainForm方法中刪除了「await Task.Run()...」,那麼一切正常,但它完全阻止了FormSelect UI。

我該如何避免這個問題?謝謝!

+1

你不應該從uithread中打開任何ui。 –

+4

除了用於調用「Application.Run」的線程(通常稱爲UI線程)外,您無法在任何線程上創建UI。 – Enigmativity

+0

我認爲在運行應用程序時啓動的「默認窗體」在Program.cs中指定。在Program.cs中,你應該能夠找到調用來啓動該表單,該表單應該被阻塞(因爲那時我們是單線程的),並且在添加代碼以啓動下一個表單之後? – XtrmJosh

回答

1

你不能那樣做。

沒有一個好的Minimal, Complete, and Verifiable code example,可靠地再現問題,不可能肯定地說你的方案的最佳解決方案是什麼。但UI對象只能在UI線程中創建和使用。您不能在工作線程中創建您的MainForm實例。

由於您的代碼示例不完整,因此您不清楚爲什麼要首先使用Task.Run()來創建MainForm實例。所有代碼都是調用構造函數,構造函數不應該是耗時的操作。

如果你有一些費時的初始化來執行,你應該抽象,走出MainForm類的,把它變成一個可以傳遞到MainForm一旦它被完全初始化一些其他類。然後你可以用例如調用Task.Run()(但仍然不在構造函數中;創建對象,然後如果需要異步調用初始化它的方法,請執行此操作)。

例如爲:

private async void btnSelect_Click(object sender, EventArgs e) { 
    loadingSpiner.Visible = true; 
    btnSelect.Enabled = false; 

    int index = listProcess.SelectedIndex; 

    MainFormData data = await startMainForm(index); 

    Program.mainForm = new MainForm(data); 
    this.Hide(); 
    Program.mainForm.Show(); 
} 

private async Task<MainFormData> startMainForm(int index) { 
    await Task.Run(() => { 
     MainFormData data = new MainFormData(); 
     data.Initialize(runningProcess[index]); 
     return data; 
    }); 
} 

class MainFormData 
{ 
    public MainFormData() { ... } 

    // You don't say what "runningProcess[index]" is, so placeholder here... 
    public void Initialize(object o) { ... } 
} 

類似的東西。當然,請根據您的需求進行調整。

當然,其他類只能初始化非UI方面。即您UI的基礎數據。由於UI對象通常不會耗費時間來創建,因此我認爲在您的情況下可行。如果您覺得這是用戶界面本身的初始化過程非常耗時,那麼您應該尋求幫助,弄清楚這是什麼情況並解決這個問題。當然,在這裏,如果沒有一個好的MCVE,我沒有什麼可以專門評論的。

+0

謝謝你,我將MainForm構造函數中的所有邏輯(除了InitializeComponent()之外)移動到了返回「MainFormData」的Task中,所以我創建了一個新的MainForm(FormData),它運行良好。再次感謝 – Kyore