如果表单没有焦点,则不会调用OnPaint

时间:2011-05-27 20:14:11

标签: c# winforms

我有一个自定义绘画的用户控件。构造函数正确地设置样式,从我所知道的。基本代码:

public partial class LineChart2 : UserControl
{
    public LineChart2()
    {
        InitializeComponent();

        //Set control styles to eliminate flicker on redraw and to redraw on resize
        this.SetStyle(
            ControlStyles.ResizeRedraw |
            ControlStyles.UserPaint |
            ControlStyles.AllPaintingInWmPaint |
            ControlStyles.DoubleBuffer,
            true);

        SetDefaultValues();
    }

    protected override void OnPaint(PaintEventArgs e)
    {
    // breakpoint set here for verification
        Paint~misc stuff(e.Graphics);

        base.OnPaint(e);
    }

    private void UpdateGraph()
    {
    // this is called when the data that the control depends on changes
        ~update stuff();

        this.Invalidate();
        //this.Refresh();
    }
}

控件包含在标准WinForm的Panel中。

我已经尝试过无效和刷新。

使用Invalidate()时,只要包含的表单具有焦点,控件就会正确重绘。绘图很顺利。当我将焦点切换到另一种形式时,即使事件仍在触发,绘图也会停止,并且仍然会调用this.Invalidate()。表格在屏幕上仍然完全可见。

使用Refresh()时,无论表单是否具有焦点,控件都会重绘,但绘图会不断闪烁,就像绕过双缓冲机制一样。

那么无论焦点如何,如何获取Invalidate消息以正确调用OnPaint方法?

3 个答案:

答案 0 :(得分:1)

Documentation说:

  

调用Invalidate方法不会   强制同步涂料;强迫一个   同步绘制,调用更新   调用Invalidate后的方法   方法

您是否尝试在Update之后致电Invalidate

答案 1 :(得分:1)

您也可以尝试使用Invalidate(true)来触发子控件重绘。

答案 2 :(得分:1)

您不应该经常强制控件重绘(更新或刷新)。 UI可能无法响应,其他控件可能无法更新,因为您将所有UI注意力强制同步刷新。 正确的方法是仅在UI准备好时才绘制。为此,您需要一个渲染循环。每次UI准备好绘制内容时,都会触发ApplicationLoopDoWork。这段时间取决于机器速度和重新绘制的内容。

该课程基于this post on Tom Miller's Blog

这是我用来控制它的类。 仅在ApplicationLoopDoWork呼叫上进行更新。

    using System;
    using System.Runtime.InteropServices;
    using System.Threading;
    using System.Windows.Forms;

    namespace Utilities.UI
    {
    /// <summary>
    /// WinFormsAppIdleHandler implements a WinForms Render Loop (max FPS possible).
    /// Reference: http://blogs.msdn.com/b/tmiller/archive/2005/05/05/415008.aspx
    /// </summary>
    public sealed class WinFormsAppIdleHandler
    {
        private readonly object _completedEventLock = new object();
        private event EventHandler _applicationLoopDoWork;

        //PRIVATE Constructor
        private WinFormsAppIdleHandler()
        {
            Enabled = false;
            SleepTime = 10;

        }

        /// <summary>
        /// Singleton from:
        /// http://csharpindepth.com/Articles/General/Singleton.aspx
        /// </summary>
        private static readonly Lazy<WinFormsAppIdleHandler> lazy = new Lazy<WinFormsAppIdleHandler>(() => new WinFormsAppIdleHandler());
        public static WinFormsAppIdleHandler Instance { get { return lazy.Value; } }

        private bool _enabled = false;

        /// <summary>
        /// Gets or sets if must fire ApplicationLoopDoWork event.
        /// </summary>
        public bool Enabled
        {
            get { return _enabled; }
            set {
                if (value)
                    Application.Idle += Application_Idle;
                else
                    Application.Idle -= Application_Idle;

                _enabled = value;
            }
        }

        /// <summary>
        /// Gets or sets the minimum time betwen ApplicationLoopDoWork fires.
        /// </summary>
        public int SleepTime { get; set; }

        /// <summary>
        /// Fires while the UI is free to work. Sleeps for "SleepTime" ms.
        /// </summary>
        public event EventHandler ApplicationLoopDoWork
        {
            //Reason of using locks:
            //http://stackoverflow.com/questions/1037811/c-thread-safe-events
            add
            {
                lock (_completedEventLock)
                    _applicationLoopDoWork += value;
            }

            remove
            {
                lock (_completedEventLock)
                    _applicationLoopDoWork -= value;
            }
        }

        /// <summary>
        ///Application idle loop.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Application_Idle(object sender, EventArgs e)
        {
            //Try to update interface
            while (Enabled && IsAppIdle())
            {
                OnApplicationIdleDoWork(EventArgs.Empty);
                //Give a break to the processor... :)
                //8 ms -> 125 Hz
                //10 ms -> 100 Hz
                Thread.Sleep(SleepTime);
            }
        }

        private void OnApplicationIdleDoWork(EventArgs e)
        {
            var handler = _applicationLoopDoWork;
            if (handler != null)
            {
                handler(this, e);
            }
        }

        /// <summary>
        /// Gets if the app is idle.
        /// </summary>
        /// <returns></returns>
        public static bool IsAppIdle()
        {
            bool isIdle = false;
            try
            {
                Message msg;
                isIdle = !PeekMessage(out msg, IntPtr.Zero, 0, 0, 0);
            }
            catch (Exception e)
            {
                //Should never get here... I hope...
                MessageBox.Show("IsAppStillIdle() Exception. Message: " + e.Message);
            }
            return isIdle;
        }

        #region  Unmanaged Get PeekMessage
        // http://blogs.msdn.com/b/tmiller/archive/2005/05/05/415008.aspx
        [System.Security.SuppressUnmanagedCodeSecurity] // We won't use this maliciously
        [DllImport("User32.dll", CharSet = CharSet.Auto)]
        public static extern bool PeekMessage(out Message msg, IntPtr hWnd, uint messageFilterMin, uint messageFilterMax, uint flags);

        #endregion
    }
}