Meilleur algorithme pour effacer un winform

En cherchant du code pour fondre un winform, je suis tombé sur cette page sur le forum MSDN.

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

La boucle for a un incrément non-entier et, d’après une question précédente que j’ai posée, ce n’est pas une bonne technique de programmation (en raison de la représentation inexacte de la plupart des décimales).

Je suis venu avec cette alternative.

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

Lequel de ceux-ci est plus efficace?

S’il existe un meilleur algorithme pour atténuer un formulaire, je serai très heureux s’il est inclus.

Merci.

Oubliez les timers (jeu de mots).

Avec Visual Studio 4.5 ou supérieur, vous pouvez simplement await une tâche retardée. Un avantage de cette méthode est qu’elle est asynchrone, contrairement à une boucle de thread Sleep ou à une boucle DoEvents , qui bloque l’application pendant le fondu (et les autres problèmes DoEvents susmentionnés).

 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); } 

Vous devez vérifier si l’object est supprimé avant de lui appliquer une transparence. J’ai utilisé un formulaire en tant qu’object, mais vous pouvez transmettre tout object prenant en charge la transparence, à condition qu’il soit correctement converti.

Donc, tout d’abord, application.DoEvents devrait être évité à moins que vous ne sachiez vraiment ce que vous faites et soyez sûr que c’est à la fois une utilisation appropriée et que vous l’utilisez correctement. Je suis à peu près certain que ce n’est pas le cas ici.

Ensuite, comment contrôlez-vous la vitesse de la décoloration? En gros, vous laissez simplement l’ordinateur s’éteindre le plus rapidement possible et vous vous fiez à la surcharge inhérente aux opérations (et aux processus en arrière-plan) pour le rallonger. Ce n’est vraiment pas très bon design. Vous feriez mieux de spécifier le temps que devrait prendre le fondu depuis le début pour qu’il soit cohérent entre les machines. Vous pouvez utiliser un Timer pour exécuter du code aux intervalles définis appropriés et vous assurer que le thread d’interface utilisateur n’est pas bloqué pendant la durée du fondu (sans utiliser DoEvents ).

Modifiez simplement la duration ci-dessous pour modifier la durée du fondu et modifiez les steps pour déterminer son “agitation”. Je l’ai fixé à 100 parce que c’est effectivement ce que faisait votre code auparavant. En réalité, vous n’avez probablement pas besoin de beaucoup et vous pouvez simplement baisser un peu avant qu’il commence à s’agiter. (Plus les marches sont basses, meilleure sera la performance.)

De plus, vous ne devriez pas être aussi inquiet à propos des performances pour quelque chose comme ça. Le fondu est quelque chose qui devra être mesuré à l’échelle d’environ une seconde ou pas beaucoup moins (pour qu’un humain puisse le percevoir) et pour tout ordinateur, de nos jours, il peut le faire, bien plus que cela. dans une seconde ce n’est même pas drôle. Cela ne consumra pratiquement pas de processeur en termes de calcul en une seconde, donc l’optimiser est très certainement une micro-optimisation.

 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(); } 
 for (double i = 0; i < 1; i+=0.01) { this.Opacity = i; Application.DoEvents(); System.Threading.Thread.Sleep(0); } 

est plus efficace car le nombre de divisions en virgule flottante est plus onéreux en machine que par rapport aux ajouts en virgule flottante (qui n'affectent pas vm-flags). Cela dit, vous pouvez réduire le nombre d'itérations de 1/2 (c'est-à-dire que l'étape de changement passe à i + = 0,02). La réduction d'opacité de 1% n'est PAS perceptible par le cerveau humain et sera également moins chère, en l'accélérant de près de 100% de plus.

MODIFIER:

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

Exemple de formulaire se fanant

J’ai écrit une classe spécialement pour les formes de fondu. Il supporte même ShowDialog et DialogResults.

Je l’ai développé car j’ai besoin de nouvelles fonctionnalités et je suis ouvert aux suggestions. Vous pouvez jeter un oeil ici:

https://gist.github.com/nathan-fiscaletti/3c0514862fe88b5664b10444e1098778

Exemple d’utilisation

 private void Form1_Shown(object sender, EventArgs e) { Fader.FadeIn(this, Fader.FadeSpeed.Slower); } 

J’ai appliqué l’approche de Victor Stoddard à un écran de démarrage. Je l’ai utilisé dans l’événement Form_Load pour fadeIn et l’événement FormClosing pour fadeOut. REMARQUE: je devais définir l’opacité du formulaire sur 0 avant d’appeler la méthode fadeIn.

Vous pouvez voir ici l’ordre des événements générés par un winform (cycle de vie): 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 } 

Auparavant, j’utilisais AnimateWindow pour AnimateWindow ou AnimateWindow en fondu un formulaire généré qui masquait l’intégralité de l’application dans SystemColor.WindowColor .

Cette petite astuce permet de masquer / permuter / afficher des écrans dans une interface de type assistant. Je n’ai pas fait ce genre de chose depuis un moment, mais j’ai utilisé P / Invoke en VB et ai exécuté l’API dans un thread qui lui est propre.

Je sais que votre question est en C #, mais c’est à peu près la même chose. Voici quelques belles VB que j’ai creusées et que je n’ai pas regardées depuis 2006! De toute évidence, il serait facile d’adapter cela à l’affaiblissement de votre propre forme.

  _ 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