https://*.com/questions/5817632/beginupdate-endupdate-for-datagridview-request
SuspendLayout()
并且ResumeLayout()
不要暂停绘图,只能暂停布局操作。给这个家伙一个机会:
public static class ControlHelper
{
#region Redraw Suspend/Resume
[DllImport("user32.dll", EntryPoint = "SendMessageA", ExactSpelling = true, CharSet = CharSet.Ansi, SetLastError = true)]
private static extern int SendMessage(IntPtr hwnd, int wMsg, int wParam, int lParam);
private const int WM_SETREDRAW = 0xB;
public static void SuspendDrawing(this Control target)
{
SendMessage(target.Handle, WM_SETREDRAW, 0, 0);
}
public static void ResumeDrawing(this Control target) { ResumeDrawing(target, true); }
public static void ResumeDrawing(this Control target, bool redraw)
{
SendMessage(target.Handle, WM_SETREDRAW, 1, 0);
if (redraw)
{
target.Refresh();
}
}
#endregion
}
MDI窗体不闪烁方法测试通过:
//.net 4.0用OptimizedDoubleBuffer
this.SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint, true);
this.UpdateStyles();
真正有效的方法:在最上层窗体加上
protected override CreateParams CreateParams {
get {
CreateParams cp = base.CreateParams;
cp.ExStyle |= 0x02000000; // Turn on WS_EX_COMPOSITED
return cp;
}
}
在下层的窗体和自定义控件加上
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
cp.Style &= ~0x02000000; // Turn off WS_CLIPCHILDREN
return cp;
}
}
注意:如果加错地方或人品不好,某些时候可能会造成控件绘制略微不正常。
**** 如果人品爆发的话,貌似在下层窗体直接加cp.Style &= ~0x02000000就行,不需要在上层窗体加cp.ExStyle |= 0x02000000;
**** 注意下层窗体代码在ListBox或者ListView的Anchor设有Right,且窗体BackColor与控件背景不同的时候,可能会发现控件初始化显示不正常。需要做一下MdiParent.Refresh或者取消Right
引用MSDN中对CreateParams的说明:
在你开发的重载控件中不要重写这个属性,通过这个属性控制控件的某些风格。只有在你封装Windows控件或者想实现某些WinForm没有提供的风格(比如Layered Window)控制的时候再使用这个属性。更多信息请参照MSDN上对CreateWindow方法和CreateWindowEx方法的参数CREATESTRUCT结构体的文档注释 。
简述为何CreateParams能够实现这样高级的样式控制,因为从CreateWindow和CreateWindowEx的名字就可以看出,CreateParam是传递给这俩个方法的参数,而这两个方法又是在窗体创建的时候调用的。所以,CreateParam才能够实现如此强大的样式控制。
**********
由于窗体上控件多,且有背景的情况下,控件设为背景设为透明,会导致窗体的刷新很慢很卡,从而窗体在闪烁,卡顿。
之前一直在网上搜寻解决的办法,试过了很多什么双缓冲啊之类的,发现效果并不大。
最后找到下面的方法可以解决了。但是奇怪的是,在有些电脑上运行时会发生窗体不刷新的问题。然后就参考下面的那个网址,最下面的答案。
http://*.com/questions/5859826/datagridview-draws-wrong
个人总结一下,可能是系统版本的问题所导致的。网上有说是因为“xp特有的双缓冲绘图机制”。后来用上这个WS_EX_COMPOSITED(用双缓冲从下到上绘制窗口的所有子孙),再开启窗体的透明样式,问题都解决了。
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
cp.ExStyle |= 0x02000000; // Turn on WS_EX_COMPOSITED
if (this.IsXpOr2003 == true)
{
cp.ExStyle |= 0x00080000; // Turn on WS_EX_LAYERED
this.Opacity = 1;
}
return cp;
}
} //防止闪烁
private Boolean IsXpOr2003
{
get
{
OperatingSystem os = Environment.OSVersion;
Version vs = os.Version;
if (os.Platform == PlatformID.Win32NT)
if ((vs.Major == 5) && (vs.Minor != 0))
return true;
else
return false;
else
return false;
}
}
**********
节点更新要使用BeginUpdate和EndUpdate
这一对操作对于需要批量操作更新控件的情景有比较好的效果,比如初始化时批量添加了大量节点。坏处就在于不能即时更新。所以,对于频繁的更新节点并希望立即反映到界面的情况不适用。如果使用并且没有禁掉清除界面消息的话,则控件看起来就会不停的闪烁,而且以白底为主,内容几乎不可见(这个视频繁程度而定)。因为界面更新都在EndUpdate处完成,操作太多导致EndUpdate阻塞时间过长,且清空在先,更新在后,导致界面看起来长时间处于空白状态。
某些情况下可以使用禁止背景更新
protected override void WndProc(ref Message m)
{
if (m.Msg == 0x0014) return;// 禁掉清除背景消息
base.WndProc(ref m);
}
public ListViewNF()
{
// 开启双缓冲
this.SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint, true);
// Enable the OnNotifyMessage event so we get a chance to filter out
// Windows messages before they get to the form's WndProc
this.SetStyle(ControlStyles.EnableNotifyMessage, true);
}
protected override void OnNotifyMessage(Message m)
{
//Filter out the WM_ERASEBKGND message
if (m.Msg != 0x14)
{
base.OnNotifyMessage(m);
}
}
******************************************
采用LockWindowUpdate API
[DllImport("user32.dll")]
static extern bool LockWindowUpdate(IntPtr hWndLock);
LockWindowUpdate(panelContainer.Handle);
// Clear Panel
panelContainer.Controls.Clear();
// my temporary TextBox
TextBox myT ;
for (int lauf=0; lauf < 200; lauf++)
{
// Create New TextBox
myT = new TextBox();
// Add TextBox to the Panel
panelContainer.Controls.Add(myT);
}
// redraw the window
LockWindowUpdate(IntPtr.Zero);
frmChild1.Hide( ); // 隐藏当前显示的子窗体 LockWindowUpdate(this.Handle); // 锁定父窗体
frmChild2.Show( ); // 显示窗体等其他需要再显示前做的事
LockWindowUpdate (IntPtr.Zero); // 解锁父窗体
RedrawWindow (this.Handle, IntPtr.Zero, IntPtr.Zero,
RDW_ERASE | RDW_INVALIDATE | RDW_ALLCHILDREN); // (0x04 | 0x01 | 0x80)立即强制重绘父窗体及其所有子窗体
效果好转,但人眼还能看到一些花屏现象,仍不能一次全部完整显示。
3. 使用Windows API中的SendMessage函数:
class
DrawingControl
{
[DllImport(
"user32.dll"
)]
public
static
extern
int
SendMessage(IntPtr hWnd, Int32 wMsg,
bool
wParam, Int32 lParam);
private
const
int
WM_SETREDRAW = 11;
public
static
void
SuspendDrawing( Control parent )
{
SendMessage(parent.Handle, WM_SETREDRAW,
false
, 0);
}
public
static
void
ResumeDrawing( Control parent )
{
SendMessage(parent.Handle, WM_SETREDRAW,
true
, 0);
parent.Refresh();
}
}
frmChild1.Hide( ); SendMessage(this.Handle, WM_SETDRAW, false, null); // 禁止窗体中的绘制操作 -----
frmChild2.Show( ); // 显示窗体等其他需要再显示前做的事
SendMessage(this.Handle, WM_SETDRAW, true, null); // 解除禁止绘制操作 ----- 2
RedrawWindow (this.Handle, IntPtr.Zero, IntPtr.Zero,
RDW_ERASE | RDW_INVALIDATE | RDW_ALLCHILDREN); // (0x04 | 0x01 | 0x80)立即强制重绘父窗体及其所有子窗
SendMessage函数中,发送消息 WM_SETREDRAW,设置SETREDRAW为FALSE,导致窗口不进行绘制。
此时,看到的窗体是假的,现象:
鼠标形状是后面应用程序的形状;
鼠标划过,后面的应用程序就显示出来了。
人眼看到的就是“花屏”。