2017-12-05 4 views
0

문제가 있습니다. 레이블로 스크롤 효과를 만들어야합니다. 본질은 값을 1 초 지연으로 레이블에 넣는 것입니다. 배열의 각 레이블 번호에 대해 생성하는 함수가 있습니다. 그러나 즉시 발생하고 지연을 시도했다. Timer을 사용했지만 결과가 전혀 없거나 올바르게 사용하는 방법을 모르겠습니다. 또한 Thread.Sleep()을 사용해 보았지만 예상 결과가 나지 않습니다.지연을 사용하는 라벨의 스크롤 효과 C#

public void generator() // присваивание текстбоксам значений 
    { 
     int[] array = getUniqueRandomArray(1, 81, 20).ToArray(); 
     green = 0; 

     for (int i = 0; i < array.Length; i++) 
     { 
      panel1.Controls[i].Text = array[i].ToString(); 
      Thread.Sleep(350); 
      panel1.Controls[i].Refresh(); 

      foreach (DataGridViewRow currentRow in dataGridView1.Rows) 
      { 
       foreach (DataGridViewCell currentCell in currentRow.Cells) 
       { 
        if (Convert.ToInt32(currentCell.Value) == array[i] && currentCell.Style.BackColor == Color.Yellow) 
        { 
         currentCell.Style.BackColor = Color.Green; 
         green++; 
         panel1.Controls[i].BackColor = Color.Green; 
        } 

       } 
      } 
     } 
    } 

getUniqueRandomArray(1, 81, 20) -이 기능은 1 내지 80의 배열을 생성하고, (20) 수를 반환한다.

Thread.Sleep(350)을 사용할 때 양식이 얼어 버리면 각 레이블은 350ms 지연된 배열에서 값을 가져옵니다. 이 버튼을 한 번 더 누르면 이전 값을 지우지 않고 현재 값이 이전 값으로 대체됩니다.

for (int i = 0; i < panel1.Controls.Count; i++) 
     { 
      panel1.Controls[i].BackColor = Color.White; 
      panel1.Controls[i].Text = null; 
     } 

실제 결과입니다. 하지만 동그라미가 실행되는 동안 양식을 사용할 수있게 해주는 또 다른 스레드에서 동결하지 않고 설명 된 지침을 수행 할 것으로 기대합니다.

마지막으로 나는 더 명확하게 설명하려고합니다.
20 개의 레이블과 20 개의 숫자가 있습니다. label1 = "1"-> (wait 1s) -> label2 = "2"-> (wait 1s) ... 등과 같은 번호를 할당해야합니다.

감사합니다. :)

+0

예상되는 결과와 실제 결과를 제공하여 실패하는 방식을 설명하면 도움이됩니다. – lamandy

+0

완료. 나는 그것이 내 생각을 뒷받침하는 데 도움이 되었으면 좋겠다 :) – Syncmaxim

+0

UI 스레드에서'Thread.Sleep'을 호출하면 안됩니다. 당신의 메소드를 async로 만들고'await Task.Delay()'를 사용하십시오. –

답변

1

그냥하는 데 도움이 발전기() 비동기 하나의 주석에 대해

generator(); 


private async void generator() 
{ 
    //(...) 

    for (int i = 0; i < array.Length; i++) 
    { 
     panel1.Controls[i].Text = array[i].ToString(); 
     panel1.Controls[i].Refresh(); 
     await Task.Run(async() => await Task.Delay(1000)); 

     //(...) 

    } 
} 

:

await Task.Run(async() => await Task.Delay(1000)) 

이 비 블로킹 awaiter입니다. 컨텍스트에 따라 두 번째 작업이 컨텍스트를 인식하지 못할 수도 있습니다. 이 경우 ConfigureAwait()을 사용하여 해결할 수 있습니다.

await Task.Run(async() => await Task.Delay(1200).ConfigureAwait(true)).ConfigureAwait(true); 


벗긴() 메소드 await Task.Run(async() => await Task.Delay(1000)); 작품 번째 비동기.
일반적으로 Task.Factory.StartNew()은 다른 스레드에서 실행되는 새 작업을 시작하고 즉시 반환됩니다. 그러나 Task<Task><TResult>.Unwrap()을 사용하여 다시 Task<TResult>, 내부 작업의 완료에 대한 프록시를 제공합니다

bool _task1 = await Task.Factory.StartNew(async delegate 
{ 
    await Task.Delay(1000); 
    return true; 
}, TaskScheduler.Default).Unwrap<bool>(); 

그래서, 하나는 새로운 awaiter로 포장을 벗긴() 메소드를 대체 할 수 있습니다, 같은 논리를 followind.

bool _task2 = await await Task.Factory.StartNew(async delegate 
{ 
    await Task.Delay(1000); 
    return true; 
}, TaskScheduler.Default); 

지금 Task.Factory.StartNew()Task<Task<bool>> 반환합니다.
Task<Task<bool>>을 기다리는 경우 Task<bool>을 반환하고 해당 작업을 다시 기다리면 bool이 반환됩니다.

, 당신은 유형의 결과가 필요하지 않은 경우 : 기능적으로 동일

await await Task.Factory.StartNew(async delegate 
{ 
    await Task.Delay(1000); 
}, TaskScheduler.Default); 

을 :

await Task.Run(async() => await Task.Delay(1000)) 
+0

'await '이후의 코드가 다른 스레드에서 실행될 가능성은 없습니까 (UI를 업데이트 할 수 없습니까?)? –

+0

@ John Wu Nope. Task.Run()은 현재 스레드가 기다리는 새로운 스레드를 생성합니다. 스레드는 Delay() 후에 만 ​​계속됩니다. 그것은 비 블로킹이며 UI는 정상적으로 업데이트됩니다. – Jimi

+0

정말요? [이 예제] (https://dotnetfiddle.net/9EJMI1)를 실행하여 코드를 평행하게 만들면 출력에 세 가지 스레드 ID가 생깁니다. 이해하려고 애를 써. –

0

당신은 양식에 Timer 컨트롤이 작업을 수행 할 수 있습니다. 트릭은 타이머 Tick 이벤트가 바깥 쪽 루프를 바꾸도록하여 타이머의 각 "틱"이 한 번의 반복을 수행하도록하는 것입니다. 다음 단계를 따르십시오.

  1. 양식에 Timer 컨트롤을 추가하십시오. 여기서 가정 해 보겠습니다. timer1입니다.
  2. 타이머 속성에서 Interval을 1000으로 설정하십시오. 그러면 초당 1 회 "틱"합니다. Enabled 속성이 False으로 설정되어 있는지 확인하십시오.
  3. 번호의 생성 된 배열과 다음 라벨의 인덱스를 추적 할 양식에 민간 분야의 몇 가지를 추가는 인구 수 :

    private int[] generatedArray; 
    private int nextControlIndex = 0; 
    
  4. 형태로 타이머를 두 번 클릭 Tick 이벤트 처리기를 추가하십시오. 마지막으로

    private void timer1_Tick(object sender, EventArgs e) 
    { 
        int i = nextControlIndex++; 
        if (i < panel1.Controls.Count && i < generatedArray.Length) 
        { 
         panel1.Controls[i].Text = generatedArray[i].ToString(); 
    
         foreach (DataGridViewRow currentRow in dataGridView1.Rows) 
         { 
          foreach (DataGridViewCell currentCell in currentRow.Cells) 
          { 
           if (Convert.ToInt32(currentCell.Value) == generatedArray[i] && 
            currentCell.Style.BackColor == Color.Yellow) 
           { 
            currentCell.Style.BackColor = Color.Green; 
            green++; 
            panel1.Controls[i].BackColor = Color.Green; 
           } 
          } 
         } 
        } 
        else 
        { 
         timer1.Stop(); 
        } 
    } 
    
  5. 민간 분야를 설정하고 타이머를 시작하는 당신의 generator() 방법에 대한 코드를 변경 : 처리기에 다음 코드를 추가

    public void generator() // присваивание текстбоксам значений 
    { 
        generatedArray = getUniqueRandomArray(1, 81, 20).ToArray(); 
        nextControlIndex = 0; 
        green = 0; 
        timer1.Start(); 
    } 
    

을 그리고 그것 뿐이다 . 이제 generator() 함수를 호출 할 때마다 레이블에 점진적으로 채워 져야합니다.

+0

고마워,하지만 지금은 문제가 녹색 색상을 할당 후 타이머가 중지됩니다. 및이 타이머와 연결된 다른 UI 문제. – Syncmaxim