2017-07-22 102 views
1

我想創建一個簡單的「鋼琴」應用程序,我可以在UI未被阻止時按下按鈕。調用線程必須是STA錯誤

private void Form1_Load(object sender, EventArgs e) 
{ 
    Thread t = new Thread(() => keyboard()); 
    t.SetApartmentState(ApartmentState.STA); 
    t.Start(); 
} 

public async void keyboard() 
{ 
    while(true) 
    { 
     if (Keyboard.IsKeyDown(Key.A)) 
     { 

      btn_A_firstDo.BeginInvoke((Action)(() => btn_A_firstDo.Enabled = false)); 
      //Play some music here... 
      await Task.Delay(1000); 
      btn_A_firstDo.BeginInvoke((Action)(() => btn_A_firstDo.Enabled = true)); 

     } 
    } 
} 

我已經得到了我無法弄清楚它解決了一個錯誤:「調用線程必須爲STA,因爲許多UI組件都需要這種」 我在這部分代碼收到此錯誤:

if(Keyboard.IsKeyDown(Key.A)) 

我還沒有完全不顧我搜索了這個理解這個錯誤。 我的問題如下:

  1. 我該怎麼解決這個錯誤?這是什麼原因造成的?
  2. 我不認爲我正確使用了await。 我想要例如並行按下2個鍵而不會阻塞UI。 如何解決這個問題?
  3. 有什麼方法可以讓我的代碼更優雅嗎?

編輯: 我已經想通了,工作代碼: (也訂閱事件KeyDown工作,例如將顯示..)

public async void keyboard(Key key) 
    { 
     await Task.Run(() => { 
       this.BeginInvoke((Action) (() => { 
        btn_A_firstDo.Enabled = false; 
        // Play some music.. 
       })); 
       Thread.Sleep(200); 
       this.BeginInvoke((Action) (() => { 
        btn_A_firstDo.Enabled = true; 
       })); 
     }); 
    } 

訂閱事件KeyDown

private void Piano_Load(object sender, EventArgs e) 
    { 
     this.KeyPreview = true; 
     this.KeyDown += SubKeyDown; // Deligate this event. 
    } 

    // Subscribing to event. 
    void SubKeyDown(object sender, System.Windows.Forms.KeyEventArgs e) 
    { 
     switch(e.KeyCode) 
     { 
      case Keys.A: 
       keyboard(Key.A); 
       break; 
      // Continue with the cases... 
      default: 
       break; 
     } 
    } 
+2

可能是* 3 *的答案:您應該訂閱事件而不是運行循環來檢查是否按下了某個鍵。 –

+0

或者如果在這種情況下沒有任何適當的事件,請使用「計時器」。 –

+0

主要問題呢?我該如何解決它? –

回答

0

1.How我可以解決這個呃ROR?這是什麼原因造成的?

你所得到的例外儘管已經設置線程單元狀態的原因,是由於您使用的await,通過你的第二次循環時,你不再在線程中執行你最初​​開始。繼續以及之後的每一個延續都是在線程池線程中執行的。您的原始線程已經退出該點。

2.我不認爲我正確地使用了await。我想要例如並行按下2個鍵而不會阻止UI。如何解決這個問題?

您的問題中沒有足夠的上下文來了解可能的答案。您顯示的涉及await的代碼似乎負責在您「播放某些音樂」時禁用UI中的某些對象。如果你要編寫一個UI線程所調用的方法,看起來與這三個語句相似,那麼這可以很容易地完成。例如:

private async Task PlayMusicA() 
{ 
    btn_A_firstDo.Enabled = false; 
    //Play some music here... 
    await Task.Delay(1000); 
    btn_A_firstDo.Enabled = true; 
} 

你可以調用在,例如,一個Click事件處理程序:

private async void btn_A_firstDo_Click(object sender, EventArgs e) 
{ 
    await PlayMusicA(); 
} 

3.Is有一些方法,使我的代碼更優雅?

對於處理鍵盤輸入,那就更有意義了,你似乎想在這裏做你的形式來監視鍵盤輸入,並鍵輸入事件作出反應,而不是投票的鍵盤。

同樣,你的問題很模糊,所以你不清楚你需要做什麼,但你可能會發現an answer I wrote previously討論如何跟蹤一個鍵是否被按下(即「上」或「下」),以便一些連續行爲可以應用,而關鍵是關閉。

另一方面,如果您只關心最初的按鍵,並且不需要知道密鑰何時被釋放,那麼其他答案將不會幫助並且不需要。您所需要的只是處理表單中的關鍵輸入。不幸的是,這是一個相當複雜的話題,因爲有很多變化。一個表單可以有孩子也需要鍵入輸入,所以有幾種不同的事件和方法可以被重寫以接收鍵輸入,其中每個鍵都具有細微差別的行爲。要在單個堆棧溢出答案中描述所有這些,真的太廣泛了。

相反,你應該看看有什麼可用的。在一個非常簡單的情況下,在您的表單類中訂閱KeyPress事件可能就足夠了。如果您的表單包含通常會接收按鍵輸入的其他控件,但您希望能夠響應特定按鍵,則可能需要使用PreviewKeyDown事件(與KeyPressKeyPress事件報告字符輸入有點不同,在Windows翻譯特定鍵之後,PreviewKeyDown會告訴您鍵盤上的哪個物理鍵被按下)。如果您的場景比其中任何一個都可以使用的場景更復雜,那麼您可能會發現自己必須重寫表單中的其中一個Process...關鍵方法。

再一次,你的問題中沒有足夠的信息來確切知道什麼最適合你。你應該使用上面的建議來嘗試做一些工作,然後如果你仍然需要幫助,花點時間做一個好的Minimal, Complete, and Verifiable code example,它清楚地顯示了你已經嘗試了什麼,並且正確地解釋該代碼的作用和你希望它來做。

+0

非常感謝您的解釋! 我寫了可以工作的代碼,如果你願意,你可以檢查它。 (我編輯了我的問題) 謝謝! –

+0

對不起,我發佈了上面的內容,而沒有仔細檢查代碼,我忘了刪除不需要的'BeginInvoke()'調用。請參閱編輯。你的'keyboard()'方法應該看起來更像這樣;沒有'Task.Run()',沒有匿名方法,也沒有'BeginInvoke()'。只需設置按鈕狀態,播放音樂,使用'Task.Delay()'等待所需的時間段,然後再次設置按鈕狀態。從你的問題中不清楚你是如何演奏音樂的;如果這樣會阻塞線程,那麼可以使用'await Task.Run((=)...>)'來表示該線程,並且只能使用該部分。 –

相關問題