问题描述
|
我有一个自定义绘画的用户控件。据我所知,构造函数可以正确设置样式。基本代码:
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的面板中。
我已经尝试了无效和刷新。
使用Invalidate()时,只要控件包含的窗体具有焦点,控件将正确重绘。绘图是平滑的。当我将焦点切换到另一种形式时,即使事件仍在触发,绘制也会停止,并且this.Invalidate()仍在被调用。该窗体仍在屏幕上完全可见。
使用Refresh()时,无论窗体是否具有焦点,控件都将重绘,但是图形会不断闪烁,就像绕过双缓冲机制一样。
因此,无论焦点如何,如何获取Invalidate消息以正确调用OnPaint方法?
解决方法
文档说:
调用Invalidate方法不会
强制同步喷涂;强迫
同步绘制,调用更新
调用Invalidate之后的方法
方法。
您是否尝试过在
Invalidate
之后呼叫Update
?
, 您也可以尝试Invalidate(true)来触发子控件重新绘制。
, 您不应如此频繁地强制控件重绘(更新或刷新)。 UI可能无法响应,其他控件可能无法更新,因为您将所有UI注意力都放在了强制同步刷新上。
正确的方法是仅在UI准备就绪时才进行绘制。为此,您需要一个渲染循环。 UI每次准备绘制内容时都会触发ApplicationLoopDoWork。周期取决于机器速度和要重绘的内容。
该课程基于Tom Miller的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);
}
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
}
}