2012-09-19 23 views
7

在搜索代碼以淡出winform時,我在MSDN論壇上發現了這個page更好的算法來淡化winform

for (double i = 0; i < 1; i+=0.01) 
{ 
    this.Opacity = i; 
    Application.DoEvents(); 
    System.Threading.Thread.Sleep(0); 
} 

for環具有非整數遞增,並從以前的問題,我問,這不是一個很好的編程技術(由於大多數小數的不精確表示)。

我想出了這個選擇。

for (double i = 0; i < 100; ++i) 
{ 
    this.Opacity = i/100; 
    Application.DoEvents(); 
    System.Threading.Thread.Sleep(0); 
} 

哪種效率更高?

如果有一個更好的淡出表單算法,如果包含它,我會很高興。

謝謝。

+0

@DarrenDavies的'睡眠(0)'是線程的剩餘時間回到調度給其他線程運行的機會。也許在.NET上這是一樣的。 [MSDN states](http://msdn.microsoft.com/en-us/library/windows/desktop/ms686298(v = vs.85).aspx):「如果指定0毫秒,則線程將放棄餘數的時間片,但仍然準備好._「 –

+0

如何簡單地將'int'更改爲'double'? –

+0

vb.net Single是C#中的「float」,而不是int。 –

回答

3
for (double i = 0; i < 1; i+=0.01) 
{ 
    this.Opacity = i; 
    Application.DoEvents(); 
    System.Threading.Thread.Sleep(0); 
} 

更高效,因爲與浮點加法(不影響vm-flags)相比,浮點除法的數量更加機器化。也就是說,您可以將迭代次數減少1/2(即更改爲i + = 0.02)。人類大腦不會注意到1%的不透明度降低,而且價格也會更便宜,並且將其加快幾乎100%。

編輯:

for(int i = 0; i < 50; i++){ 
    this.Opacity = i * 0.02; 
    Application.DoEvents(); 
    System.Threading.Thread.Sleep(0); 
} 
0

在過去,我用AnimateWindow以淡入/淡出生成的形式在空白中SystemColor.WindowColor我的整個應用程序。

這個巧妙的小技巧提供了隱藏/交換/顯示界面等嚮導屏幕的效果。我暫時還沒有做過這種事,但我在VB中使用了P/Invoke,並且在自己的線程中運行了API。

我知道你的問題是在C#中,但它大致相同。這裏有一些可愛的VB,我從2006年開始就已經挖掘出來,並沒有看過!顯然這很容易適應這種淡入淡出自己的形式。

<DllImport("user32.dll")> _ 
Public Shared Function AnimateWindow(ByVal hwnd As IntPtr, ByVal dwTime As Integer, ByVal dwFlags As AnimateStyles) As Boolean 
End Function 

Public Enum AnimateStyles As Integer 
    Slide = 262144 
    Activate = 131072 
    Blend = 524288 
    Hide = 65536 
    Center = 16 
    HOR_Positive = 1 
    HOR_Negative = 2 
    VER_Positive = 4 
    VER_Negative = 8 
End Enum 

Private m_CoverUp As Form 

Private Sub StartFade() 
    m_CoverUp = New Form() 
    With m_CoverUp 
     .Location = Me.PointToScreen(Me.pnlMain.Location) 
     .Size = Me.pnlMain.Size 
     .FormBorderStyle = System.Windows.Forms.FormBorderStyle.None 
     .BackColor = Drawing.SystemColors.Control 
     .Visible = False 
     .ShowInTaskbar = False 
     .StartPosition = System.Windows.Forms.FormStartPosition.Manual 
    End With 
    AnimateWindow(m_CoverUp.Handle, 100, AnimateStyles.Blend) 'Blocks 
    Invoke(New MethodInvoker(AddressOf ShowPage)) 
End Sub 

Private Sub EndFade() 
    AnimateWindow(m_CoverUp.Handle, 100, AnimateStyles.Blend Or AnimateStyles.Hide) 
    m_CoverUp.Close() 
    m_CoverUp = Nothing 
End Sub 
+0

這是Visual Basic。 – carefulnow1

+0

問題出在C# –

12

所以,第一關,application.DoEvents should be avoided除非你真的知道自己在做什麼,並確信這既是適當地利用它,你使用了正確的。我相當確定這裏的情況都不是這樣。

接下來,你如何控制衰落的速度?你基本上只是讓計算機儘快地褪色,並依靠操作(和後臺進程)的固有開銷來使其花費更長的時間。這真的不是很好的設計。您最好指定淡入淡出從一開始應該花多長時間,以便它在機器之間保持一致。您可以使用Timer以適當的設置間隔執行代碼,並確保UI線程在淡入淡出期間不被阻塞(不使用DoEvents)。

只需修改下面的duration以更改淡入淡出時間,並修改steps以確定它是多麼「不穩定」。我已將它設置爲100,因爲這實際上就是您的代碼之前所做的。事實上,你可能不需要那麼多,你可以降低到它開始變得不穩定之前。 (步驟越低,執行得越好。)

此外,你不應該如此擔心這種事情的表現。淡入淡出是需要在一秒左右的時間內測量出來的,或者說不會少得多(對於一個能夠感知它的人來說),對於現在的任何一臺計算機來說,淡入淡出都可以這樣做,遠遠超過這個在一秒鐘內它甚至不好笑。這在一秒鐘內幾乎不會消耗CPU的計算量,所以試圖優化它肯定是微型優化。

private void button1_Click(object sender, EventArgs e) 
{ 
    int duration = 1000;//in milliseconds 
    int steps = 100; 
    Timer timer = new Timer(); 
    timer.Interval = duration/steps; 

    int currentStep = 0; 
    timer.Tick += (arg1, arg2) => 
    { 
     Opacity = ((double)currentStep)/steps; 
     currentStep++; 

     if (currentStep >= steps) 
     { 
      timer.Stop(); 
      timer.Dispose(); 
     } 
    }; 

    timer.Start(); 
} 
+1

這是一個精明的評論,我特別喜歡你堅持要「避免DoEvents」的事實......它真的應該如此。我也做一些非常相似的事情,但是你錯過了一些重要的東西:你應該停止計時器!在Tick處理程序中,包含檢查'if(Opacity> = 1.0){timer.Stop(); timer.Dispose(); }' – fernandoespinosa

+0

@khovanskiiªn謝謝,我完全忘了停止計時器;編輯。 – Servy

15

忘記定時器(雙關語意)。

使用Visual Studio 4.5或更高版本,您可以僅延遲await任務。此方法的一個優點是它是異步的,不像線程SleepDoEvents循環,該循環在淡入淡出期間(和其他上述DoEvents問題)阻止應用程序。

private async void FadeIn(Form o, int interval = 80) 
{ 
    //Object is not fully invisible. Fade it in 
    while (o.Opacity < 1.0) 
    { 
     await Task.Delay(interval); 
     o.Opacity += 0.05; 
    } 
    o.Opacity = 1; //make fully visible  
} 

private async void FadeOut(Form o, int interval = 80) 
{ 
    //Object is fully visible. Fade it out 
    while (o.Opacity > 0.0) 
    { 
     await Task.Delay(interval); 
     o.Opacity -= 0.05; 
    } 
    o.Opacity = 0; //make fully invisible  
} 

用法:

private void button1_Click(object sender, EventArgs e) 
{ 
    FadeOut(this, 100); 
} 

如果你申請的任何透明度,它之前的對象設置你應該檢查。我使用了一個表單作爲對象,但只要它能正確轉換,您就可以傳遞任何支持透明度的對象。

+0

當我從後臺工作完成的事件調用FadeIn時,'await'行會導致死鎖。我怎樣才能避免這種情況? –

+1

@NineTails你試過在this.BeginInvoke中包裝FadeIn方法(new Action(()=> FadeIn(...)))? – drake7707

+1

@ drake7707謝謝。不過,我以前解決了我的問題'SynchronizationContext.SetSynchronizationContext(new WindowsFormsSynchronizationContext());' –

1

我將Victor Stoddard的方法應用於splashScreen。我在Form_Load事件中使用它將fadeIn和FormClosing事件設置爲fadeOut。 注:我必須在調用fadeIn方法之前將窗體的不透明度設置爲0。

在這裏你可以看到一個winform(生命週期)惜售事件的順序: https://msdn.microsoft.com/en-us/library/86faxx0d(v=vs.110).aspx

private void Splash_Load(object sender, EventArgs e) 
{ 
    this.Opacity = 0.0; 
    FadeIn(this, 70); 
} 


private void Splash_FormClosing(object sender, FormClosingEventArgs e) 
{ 
    FadeOut(this, 30); 
} 

private async void FadeIn(Form o, int interval = 80) 
{ 
    //Object is not fully invisible. Fade it in 
    while (o.Opacity < 1.0) 
    { 
     await Task.Delay(interval); 
     o.Opacity += 0.05; 
    } 
    o.Opacity = 1; //make fully visible  
} 

private async void FadeOut(Form o, int interval = 80) 
{ 
    //Object is fully visible. Fade it out 
    while (o.Opacity > 0.0) 
    { 
     await Task.Delay(interval); 
     o.Opacity -= 0.05; 
    } 
    o.Opacity = 0; //make fully invisible  
} 
相關問題