【问题标题】:Better algorithm to fade a winform更好的算法来淡化winform
【发布时间】:2012-09-11 22:42:00
【问题描述】:

在搜索淡化 winform 的代码时,我在 MSDN 论坛上看到了这个 page

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

哪一个更高效?

如果有更好的表格淡化算法,我会很高兴的。

谢谢。

【问题讨论】:

  • @DarrenDavies 在 Win32,IIRC 上,Sleep(0) 是将线程的剩余时间返回给调度程序,以让其他线程有机会运行。也许在 .NET 上是一样的。 MSDN states: "如果您指定 0 毫秒,线程将放弃其时间片的剩余部分但保持就绪状态。"
  • 简单地将int 更改为double 怎么样?
  • vb.net Single 在 C# 中是“float”,而不是 int。
  • 您可以查看 Windows API - codeproject.com/Articles/43058/…

标签: c# winforms fading


【解决方案1】:

忘记计时器(双关语)。

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

在对对象应用任何透明度之前,您应该检查对象是否已被处置。我使用表单作为对象,但您可以传递任何支持透明度的对象,只要它正确转换即可。

【讨论】:

  • 当我从后台工作人员完成事件中调用 FadeIn 时,'await' 行会导致死锁。我怎样才能避免这种情况?
  • @NineTails 你试过将 FadeIn 方法包装在 this.BeginInvoke(new Action(() => FadeIn(...))) 中吗?
  • @drake7707 谢谢。不过,我之前用 'SynchronizationContext.SetSynchronizationContext(new WindowsFormsSynchronizationContext());' 解决了我的问题
  • 我在 WPF 中尝试了上述方法,它在没有线程修改的情况下完美运行(我确实必须删除对 Windows 窗体的引用,但这很容易)。我只需要先将 Window 不透明度设置为 0 才能使淡入工作。
【解决方案2】:

所以,首先,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();
}

【讨论】:

  • 这是一个精明的评论,我特别喜欢你坚持“应该避免 DoEvents”的事实......它真的应该。我也做了一些非常相似的事情,但是你错过了一些重要的事情:你应该在某个时候停止计时器!!!在 Tick 处理程序中,包括检查 if (Opacity &gt;= 1.0) { timer.Stop(); timer.Dispose(); }
  • @khovanskiiªn 谢谢,我完全忘了停止计时器;编辑。
【解决方案3】:

我写了一个专门用于淡入淡出表单的类。它甚至支持ShowDialog 和DialogResults。

我已经扩展了它,因为我需要新功能,并且愿意接受建议。你可以看看这里:

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

示例用法

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

【讨论】:

    【解决方案4】:
    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);
    }
    

    【讨论】:

      【解决方案5】:

      我将 Victor Stoddard 的方法应用于启动画面。我在 Form_Load 事件中使用它来淡入淡出,在 FormClosing 事件中使用它来淡出。 注意:在调用 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       
      }
      

      【讨论】:

        【解决方案6】:

        过去,我曾使用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
        

        【讨论】:

        • 这是 Visual Basic。
        • 问题在 C# 中
        【解决方案7】:

        Victor Stoddard 很接近,但我发现如果它开始快速增加不透明度并在接近完全不透明度 1 时变慢,那么渐变会更令人愉悦。以下是对他的代码的轻微修改:

            using System.Threading.Tasks;
            using System.Windows.Forms;
        
            public partial class EaseInForm : Form
            {
                public EaseInForm()
                {
                    InitializeComponent();
                    this.Opacity = 0;
                }
        
                private async void Form_Load(object sender, EventArgs e)
                {
                    while (this.Opacity < 1.0)
                    {
                        var percent = (int)(this.Opacity * 100);
                        await Task.Delay(percent);
                        this.Opacity += 0.04;
                    }
                }
            }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-11-29
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多