2012-09-19 8 views
7

Durante la ricerca del codice per svanire una winform, mi sono imbattuto in questo page nel forum MSDN.Algoritmo migliore per svanire una winform

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

Il for ciclo ha un incremento non intero e, da una domanda precedente ho chiesto, che non è una buona tecnica di programmazione (dovuta alla rappresentazione inesatta della maggior parte dei decimali).

Mi è venuta in mente questa alternativa.

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

Quale di questi è più efficiente?

Se esiste un algoritmo migliore per la dissolvenza di un modulo, sarei molto felice se è incluso.

Grazie.

+0

@DarrenDavies su Win32, IIRC, il 'sonno (0)' è stato quello di restituire il tempo rimanente del thread per lo scheduler per dare altro thread la possibilità di correre. Forse su .NET questo è lo stesso. [Stati MSDN] (http://msdn.microsoft.com/en-us/library/windows/desktop/ms686298 (v = vs.85) .aspx): "_Se si specifica 0 millisecondi, il thread rinuncerà al resto del suo intervallo di tempo ma rimane pronto._ " –

+0

Che ne dici di cambiare semplicemente' int' in 'double'? –

+0

Un singolo vb.net è "float" in C#, non int. –

risposta

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

è più efficiente come il numero di divisioni in virgola mobile sono più macchina costosa rispetto rispetto a variabile aggiunte punti (che non incidono vm-flag). Detto questo, è possibile ridurre il numero di iterazioni di 1/2 (ovvero passaggio di modifica in i + = 0,02). La riduzione dell'opacità dell'1% NON è evidente dal cervello umano e sarà anche meno costosa, accelerandola quasi del 100% in più.

EDIT:

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

In passato, ho usato AnimateWindow per fade in/out di un modulo generato che azzera su tutta la mia applicazione in SystemColor.WindowColor.

Questo piccolo e grazioso trucco dà l'effetto di nascondere/scambiare/mostrare schermate in un'interfaccia simile a una procedura guidata. Non ho fatto questo genere di cose per un po ', ma ho usato P/Invoke in VB e ho eseguito l'API in un thread a parte.

So che la tua domanda è in C#, ma è più o meno la stessa cosa. Ecco alcuni deliziosi VB che ho tirato fuori e che non guardo dal 2006! Ovviamente sarebbe facile adattarlo per sfumare la tua forma dentro e fuori.

<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

Questo è Visual Basic. – carefulnow1

+0

La domanda era in C# –

12

Quindi, prima di tutto, application.DoEvents should be avoided a meno che tu non sappia quello che stai facendo e sono sicuro che questo è sia un uso appropriato di esso, e che si sta utilizzando in modo corretto. Sono abbastanza certo che non è il caso qui.

Quindi, come stai controllando la velocità dello sbiadimento? In pratica, stai semplicemente lasciando che il computer svanisca più rapidamente che può e basandosi sul sovraccarico inerente delle operazioni (e dei processi in background) per farla impiegare più tempo. Non è davvero un ottimo design. È meglio specificare il tempo necessario alla dissolvenza dall'inizio, in modo che sia coerente tra le macchine. È possibile utilizzare Timer per eseguire il codice agli intervalli impostati appropriati e assicurarsi che il thread dell'interfaccia utente non sia bloccato per la durata della dissolvenza (senza utilizzare DoEvents).

È sufficiente modificare lo duration in basso per modificare la durata della dissolvenza e modificare lo steps per determinare quanto "è irregolare". Ho impostato su 100 perché è ciò che il codice stava facendo in precedenza. In realtà, probabilmente non ne hai bisogno di così tanti e puoi semplicemente abbassarlo appena prima che inizi a diventare instabile. (Più bassi sono i passaggi, meglio sarà.)

Inoltre, non dovresti essere così preoccupato delle prestazioni per qualcosa di simile. La dissolvenza è qualcosa che deve essere misurata sulla scala di circa un secondo o meno (per un essere umano è in grado di percepirlo) e per qualsiasi computer in questi giorni può farlo, molto più di questo in un secondo non è nemmeno divertente. Questo non consumerà praticamente nessuna CPU in termini di calcolo nel corso di un secondo, quindi cercare di ottimizzarlo è sicuramente una micro-ottimizzazione.

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

Questo è un commento esperto, e mi piace in particolare il fatto che tu insisti che "I tentativi devono essere evitati" ... ESSA DOVREBBE DOVERLO. Faccio anche qualcosa di molto simile, ma ti manca qualcosa di importante: dovresti interrompere il timer ad un certo punto !!! All'interno del gestore di tick, includi il controllo 'if (Opacity> = 1.0) {timer.Stop(); timer.Dispose(); } ' – fernandoespinosa

+0

@ khovanskiiªn Grazie, ho completamente dimenticato di fermare il timer; modificato in. – Servy

15

Dimenticare i timer (gioco di parole).

Con Visual Studio 4.5 o versione successiva, è possibile eseguire l'attività await in ritardo. Un vantaggio di questo metodo è che è asincrono, a differenza di un ciclo Sleep o DoEvents, che blocca l'applicazione durante la dissolvenza (e gli altri summenzionati problemi 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  
} 

Usage:

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

Si dovrebbe verificare se l'oggetto è disposto prima di applicare alcuna trasparenza ad esso. Ho usato un modulo come oggetto, ma puoi passare qualsiasi oggetto che supporti la trasparenza fintanto che viene proiettato correttamente.

+0

Quando chiamo FadeIn da un evento background worker completato, la riga 'await' causa un deadlock. Come posso evitare questo? –

+1

@NineTails hai provato a inserire il metodo FadeIn in un this.BeginInvoke (nuova azione (() => FadeIn (...)))? – drake7707

+1

@ drake7707 Grazie. Tuttavia, ho già risolto il problema con "SynchronizationContext.SetSynchronizationContext (new WindowsFormsSynchronizationContext());" –

1

Ho applicato l'approccio di Victor Stoddard a uno splashScreen. L'ho usato nell'evento Form_Load per fadeIn e FormClosing per fadeOut. NOTA: ho dovuto impostare l'opacità del modulo su 0 prima di chiamare il metodo fadeIn.

Qui potete vedere l'ordine degli eventi rizzati da una WinForm (ciclo di vita): 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  
}