c# scrolling using mousewheel without scrollbar - c#

I'm trying to dynamically change the view of a panel that contains buttons in my winforms application using the mousewheel. I'm basically changing the location of the panel using the code below however whenever I use the mousewheel to scroll, the scrollbars can be seen. They sort of flask/ flicker into view when I change the location. Is there any way to stop the scrollbars from appearing?
location -= 40;
this.pnl.VerticalScroll.Value = location;
this.pnl.AutoScrollPosition = new Point(0, location);

You cannot simple change it from winforms, since it is being automatically displayed by windows. There is a not so great solution for this issue.I had a similar requirement and what I used was this :
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool ShowScrollBar(IntPtr hWnd, int wBar, bool bShow);
private enum ScrollBarDirection
{
SB_HORZ = 0,
SB_VERT = 1,
SB_CTL = 2,
SB_BOTH = 3
}
protected override void WndProc(ref System.Windows.Forms.Message m)
{
ShowScrollBar(panel1.Handle, (int)ScrollBarDirection.SB_VERT, false);
base.WndProc(ref m);
}
Import the win32 dll user32.dll
Call the ShowScrollBar method in it with required parameters as shown in above example

Related

Move window without a mouse drag gesture

I have a window (subclass of System.Windows.Forms.Form) without a border in order to apply a custom style to it. Moving the window when the mouse button is down seems to be easy. Either by sending a WM_NCLBUTTONDOWN HT_CAPTION or a WM_SYSCOMMAND 0xF102 message the window can be dragged to a new location. As soon as the mouse button is up though, it seems to be impossible to move the window.
One could send WM_SYSCOMMAND SC_MOVE message but then the cursor moves at the top center of the window and awaits for the user to press any arrow key in order to hook the window for move -which is awkward at the least. I tried to fake a key press/release sequence but that of course didn't work as I called SendMessage with the current form Handle as argument but I guess the message should not be sent to the current form.
The desired behavior is: click a button (ie the mouse button is released) move the form where cursor goes, click again to release the form. Is that possible with winapi? Unfortunately I am not familiar with it.
Addendum
Sending a key input: I tried to use SendInput as SendMessage is supposed to be bad practice. Still it didn't hook the window. I tried to read and print the winapi error code with Marshal.GetLastWin32Error() and I got a 5 which is access denied. The curious thing was that I received the messages after the move sequence ended (ie I manually pressed a key or mouse button). No idea how to work around this.
Using the IMessageFilter (IVSoftware's answer): this is what I ended up doing but that has 2 issues: moving the window with its Location property has lag compared to the native way (no big deal for now) and also it doesn't receive mouse messages that occur outside the main form. That means it won't work a. for multiscreen environments b. if the cursor moves outside the forms of the application. I could create full screen transparent forms for every monitor that will serve as an "message canvas" but still... why not give the OS way a chance.
As I understand it, the desired behavior is to enable the "Click to Move" (one way or another) and then click anywhere on a multiscreen surface and have the borderless form follow the mouse to the new position. One solution that seems to work in my brief testing is to pinvoke the WinApi SetWindowsHookEx to install a global low level hook for WH_MOUSE_LL in order to intercept WM_LBUTTONDOWN.
*This answer has been modified in order to track updates to the question.
Low-level global mouse hook
public MainForm()
{
InitializeComponent();
using (var process = Process.GetCurrentProcess())
{
using (var module = process.MainModule!)
{
var mname = module.ModuleName!;
var handle = GetModuleHandle(mname);
_hook = SetWindowsHookEx(
HookType.WH_MOUSE_LL,
lpfn: callback,
GetModuleHandle(mname),
0);
}
}
// Unhook when this `Form` disposes.
Disposed += (sender, e) => UnhookWindowsHookEx(_hook);
// A little hack to keep window on top while Click-to-Move is enabled.
checkBoxEnableCTM.CheckedChanged += (sender, e) =>
{
TopMost = checkBoxEnableCTM.Checked;
};
// Compensate move offset with/without the title NC area.
var offset = RectangleToScreen(ClientRectangle);
CLIENT_RECT_OFFSET = offset.Y - Location.Y;
}
readonly int CLIENT_RECT_OFFSET;
IntPtr _hook;
private IntPtr callback(int code, IntPtr wParam, IntPtr lParam)
{
var next = IntPtr.Zero;
if (code >= 0)
{
switch ((int)wParam)
{
case WM_LBUTTONDOWN:
if (checkBoxEnableCTM.Checked)
{
_ = onClickToMove(MousePosition);
// This is a very narrow condition and the window is topmost anyway.
// So probably swallow this mouse click and skip other hooks in the chain.
return (IntPtr)1;
}
break;
}
}
return CallNextHookEx(IntPtr.Zero, code, wParam, lParam);
}
}
Perform the move
private async Task onClickToMove(Point mousePosition)
{
// Exempt clicks that occur on the 'Enable Click to Move` button itself.
if (!checkBoxEnableCTM.ClientRectangle.Contains(checkBoxEnableCTM.PointToClient(mousePosition)))
{
// Try this. Offset the new `mousePosition` so that the cursor lands
// in the middle of the button when the move is over. This feels
// like a semi-intuitive motion perhaps. This means we have to
// subtract the relative position of the button from the new loc.
var clientNew = PointToClient(mousePosition);
var centerButton =
new Point(
checkBoxEnableCTM.Location.X + checkBoxEnableCTM.Width / 2,
checkBoxEnableCTM.Location.Y + checkBoxEnableCTM.Height / 2);
var offsetToNow = new Point(
mousePosition.X - centerButton.X,
mousePosition.Y - centerButton.Y - CLIENT_RECT_OFFSET);
// Allow the pending mouse messages to pump.
await Task.Delay(TimeSpan.FromMilliseconds(1));
WindowState = FormWindowState.Normal; // JIC window happens to be maximized.
Location = offsetToNow;
}
checkBoxEnableCTM.Checked = false; // Turn off after each move.
}
In the code I used to test this answer, it seemed intuitive to center the button where the click takes place (this offset is easy to change if it doesn't suit you). Here's the result of the multiscreen test:
WinApi
#region P I N V O K E
public enum HookType : int { WH_MOUSE = 7, WH_MOUSE_LL = 14 }
const int WM_LBUTTONDOWN = 0x0201;
delegate IntPtr HookProc(int code, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr SetWindowsHookEx(HookType hookType, HookProc lpfn, IntPtr hMod, int dwThreadId);
[DllImport("user32.dll")]
static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam,
IntPtr lParam);
[DllImport("user32.dll", SetLastError = true)]
static extern bool UnhookWindowsHookEx(IntPtr hhk);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr GetModuleHandle(string lpModuleName);
#endregion P I N V O K E
Here is a possible solution that I will go with after all. It's not that IVSoftware's answer doesn't work, it does, I tried it. It's that my solution has some set of advantages relevant to what I am trying to do. The main points are:
Utilizing the IMessageFilter (thanks to SwDevMan81's answer) which reminded me that the correct way to process messages "globally" is not to override WndProc)
Laying out a set of transparent windows on all screens in order to receive mouse move messages everywhere.
Pros
It works without having to make any P/Invokes
It allows more tricks to be done like for example leverage the transparent forms to implement a "move border instead of form" functionality (though I didn't test it, paint might be tricky)
Can be easily applied for resize as well.
Can work with mouse buttons other than the left/primary.
Cons
It has too many "moving parts". At least for my taste. Laying out transparent windows all over the place? Hm.
It has some corner cases. Pressing Alt+F4 while moving the form will close the "canvas form". That can be easily mitigated but there might be others as well.
There must be an OS way to do this...
The code (basic parts; full code on github)
public enum WindowMessage
{
WM_MOUSEMOVE = 0x200,
WM_LBUTTONDOWN = 0x201,
WM_LBUTTONUP = 0x202,
WM_RBUTTONDOWN = 0x204,
//etc. omitted for brevity
}
public class MouseMessageFilter : IMessageFilter
{
public event EventHandler MouseMoved;
public event EventHandler<MouseButtons> MouseDown;
public event EventHandler<MouseButtons> MouseUp;
public bool PreFilterMessage(ref Message m)
{
switch (m.Msg)
{
case (int)WindowMessage.WM_MOUSEMOVE:
MouseMoved?.Invoke(this, EventArgs.Empty);
break;
case (int)WindowMessage.WM_LBUTTONDOWN:
MouseDown?.Invoke(this, MouseButtons.Left);
break;
//etc. omitted for brevity
}
return false;
}
}
public partial class CustomForm : Form
{
private MouseMessageFilter windowMoveHandler = new();
private Point originalLocation;
private Point offset;
private static List<Form> canvases = new(SystemInformation.MonitorCount);
public CustomForm()
{
InitializeComponent();
windowMoveHandler.MouseMoved += (_, _) =>
{
Point position = Cursor.Position;
position.Offset(offset);
Location = position;
};
windowMoveHandler.MouseDown += (_, button) =>
{
switch (button)
{
case MouseButtons.Left:
EndMove();
break;
case MouseButtons.Middle:
CancelMove();
break;
}
};
moveButton.MouseClick += (_, _) =>
{
BeginMove();
};
}
private void BeginMove()
{
Application.AddMessageFilter(windowMoveHandler);
originalLocation = Location;
offset = Invert(PointToClient(Cursor.Position));
ShowCanvases();
}
//Normally an extension method in another library of mine but I didn't want to
//add a dependency just for that
private static Point Invert(Point p) => new Point(-p.X, -p.Y);
private void ShowCanvases()
{
for (int i = 0; i < Screen.AllScreens.Length; i++)
{
Screen screen = Screen.AllScreens[i];
Form form = new TransparentForm
{
Bounds = screen.Bounds,
Owner = Owner
};
canvases.Add(form);
form.Show();
}
}
private void EndMove()
{
DisposeCanvases();
}
private void DisposeCanvases()
{
Application.RemoveMessageFilter(windowMoveHandler);
for (var i = 0; i < canvases.Count; i++)
{
canvases[i].Close();
}
canvases.Clear();
}
private void CancelMove()
{
EndMove();
Location = originalLocation;
}
//The form used as a "message canvas" for moving the form outside the client area.
//It practically helps extend the client area. Without it we won't be able to get
//the events from everywhere
private class TransparentForm : Form
{
public TransparentForm()
{
StartPosition = FormStartPosition.Manual;
FormBorderStyle = FormBorderStyle.None;
ShowInTaskbar = false;
}
protected override void OnPaintBackground(PaintEventArgs e)
{
//Draws a white border mostly useful for debugging. For example that's
//how I realised I needed Screen.Bounds instead of WorkingArea.
ControlPaint.DrawBorder(e.Graphics, new Rectangle(Point.Empty, Size),
Color.White, 2, ButtonBorderStyle.Solid,
Color.White, 2, ButtonBorderStyle.Solid,
Color.White, 2, ButtonBorderStyle.Solid,
Color.White, 2, ButtonBorderStyle.Solid);
}
}
}

How do I use the native hand cursor in my C# application? [duplicate]

Okay, so you know how in Windows Vista and Windows 7 MS changed the Hand Cursor (the one that shows up when you hover over a hyperlink), and added more detail to it so it's antialiased and nice and smooth around the edges?
Well, why isn't it like that in Windows Forms apps?
I'm sick off looking at a crappy hand cursor that looks like it was drawn by a caveman.
Is there a way to programmatically tell it to display the one that's actually installed in the system? I looked in the Cursors folder in my Windows directory, and the old hand cursor isn't even there! So why is WinForms still using the old one? How can I 'upgrade' it?
Yes, the WinForms controls still use the old-school hand cursor, as shipped with Windows 98/2000. It lacks the anti-aliasing effects that the one included with the Aero cursors does. This is because the .NET Framework includes its own hard-coded cursor, which it uses instead of the system default. I presume this is because early versions of .NET were targeting operating systems like Windows 95 that didn't come bundled with this cursor, but haven't done the archaeology to prove it.
Fortunately, it's easy enough to force it to use the right one. You just have to tell the operating system you want it to use the default hand cursor, and then it will be correct no matter what version of Windows the user runs your program on, and even if they've changed their mouse cursors from the default theme.
The simplest way of doing that is to subclass the existing control, override the WndProc function to intercept the WM_SETCURSOR message, and tell it to use the system IDC_HAND cursor. You just need a little bit of P/Invoke magic.
The following code is an example of how that might look using the LinkLabel control:
public class LinkLabelEx : LinkLabel
{
private const int WM_SETCURSOR = 0x0020;
private const int IDC_HAND = 32649;
[DllImport("user32.dll", CharSet=CharSet.Auto, SetLastError=true)]
private static extern IntPtr LoadCursor(IntPtr hInstance, int lpCursorName);
[DllImport("user32.dll", CharSet=CharSet.Auto)]
private static extern IntPtr SetCursor(IntPtr hCursor);
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_SETCURSOR)
{
// Set the cursor to use the system hand cursor
SetCursor(LoadCursor(IntPtr.Zero, IDC_HAND));
// Indicate that the message has been handled
m.Result = IntPtr.Zero;
return;
}
base.WndProc(ref m);
}
}
Excuse me for resurrecting a year-old thread!!!
After messing around with the original solution and taking a look at the reflected LinkLabel source code, I "finally" found a quick yet clean way of doing it :
using System.Runtime.InteropServices;
namespace System.Windows.Forms {
public class LinkLabelEx : LinkLabel {
private const int IDC_HAND = 32649;
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr LoadCursor(IntPtr hInstance, int lpCursorName);
private static readonly Cursor SystemHandCursor = new Cursor(LoadCursor(IntPtr.Zero, IDC_HAND));
protected override void OnMouseMove(MouseEventArgs e) {
base.OnMouseMove(e);
// If the base class decided to show the ugly hand cursor
if(OverrideCursor == Cursors.Hand) {
// Show the system hand cursor instead
OverrideCursor = SystemHandCursor;
}
}
}
}
This class actually does what we want: It shows the proper system hand cursor without flickering and does this only on the LinkArea of the control.
This post solves problems of the other posts:
It respects to the link location and shows the hand just when cursor is on link
It doesn't flicker on mouse move
You need to change the cursor to system hand cursor. To do so, you need to handle WM_SETCURSOR and check if OverrideCursor is Cursors.Hand then change it to the system cursor by calling SetCursor:
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
public class MyLinkLabel : LinkLabel
{
const int IDC_HAND = 32649;
const int WM_SETCURSOR = 0x0020;
const int HTCLIENT = 1;
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern IntPtr LoadCursor(IntPtr hInstance, int lpCursorName);
[DllImport("user32.dll", ExactSpelling = true, CharSet = CharSet.Auto)]
static extern IntPtr SetCursor(HandleRef hcursor);
static readonly Cursor SystemHandCursor =
new Cursor(LoadCursor(IntPtr.Zero, IDC_HAND));
protected override void WndProc(ref Message msg)
{
if (msg.Msg == WM_SETCURSOR)
WmSetCursor(ref msg);
else
base.WndProc(ref msg);
}
void WmSetCursor(ref Message m)
{
if (m.WParam == (IsHandleCreated ? Handle : IntPtr.Zero) &&
(unchecked((int)(long)m.LParam) & 0xffff) == HTCLIENT) {
if (OverrideCursor != null) {
if (OverrideCursor == Cursors.Hand)
SetCursor(new HandleRef(SystemHandCursor, SystemHandCursor.Handle));
else
SetCursor(new HandleRef(OverrideCursor, OverrideCursor.Handle));
}
else {
SetCursor(new HandleRef(Cursor, Cursor.Handle));
}
}
else {
DefWndProc(ref m);
}
}
}
Sorry for getting this old post back, but i also have some kind of solution for this.
If you need to apply the systemcursor application wide without touching old controls, use this at applicationstart:
private static void TrySetCursorsDotHandToSystemHandCursor()
{
try
{
typeof(Cursors).GetField("hand", BindingFlags.Static | BindingFlags.NonPublic)
.SetValue(null, SystemHandCursor);
}
catch { }
}
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr LoadCursor(IntPtr hInstance, int lpCursorName);
private static readonly Cursor SystemHandCursor = new Cursor(LoadCursor(IntPtr.Zero, 32649 /*IDC_HAND*/));
Doing that without creating a new control wee need to change the Control's Cursor AND create a custom linklabel or else it wouldn't work
we create the custom linklabel by adding a label changing the font underline and changing it's fore color and add an click event
Private Const IDC_HAND As Integer = 32649
<DllImport("user32.dll", CharSet:=CharSet.Auto, SetLastError:=True)>
Private Shared Function LoadCursor(ByVal hInstance As IntPtr, ByVal lpCursorName As Integer) As IntPtr
End Function
Private Shared ReadOnly SystemHandCursor As Cursor = New Cursor(LoadCursor(IntPtr.Zero, IDC_HAND))
'add the cursor to custom linklabel
CustomLinkLabel1.Cursor = SystemHandCursor
sorry only have vb .net code you might use an online converter
EDIT: some code was missing

Changing the color of the title bar in WinForm

Is it possible to change the color of the title bar of a WinForm in C#?
__________________________
[Form1_______________-|[]|X] <- I want to change the color of this
| |
| |
| |
|__________________________|
I solved this problem. This is the code:
[DllImport("User32.dll", CharSet = CharSet.Auto)]
public static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC);
[DllImport("User32.dll")]
private static extern IntPtr GetWindowDC(IntPtr hWnd);
protected override void WndProc(ref Message m)
{
base.WndProc(ref m);
const int WM_NCPAINT = 0x85;
if (m.Msg == WM_NCPAINT)
{
IntPtr hdc = GetWindowDC(m.HWnd);
if ((int)hdc != 0)
{
Graphics g = Graphics.FromHdc(hdc);
g.FillRectangle(Brushes.Green, new Rectangle(0, 0, 4800, 23));
g.Flush();
ReleaseDC(m.HWnd, hdc);
}
}
}
What you can do is set the FormBorderStyle property to None and do what ever you want with the form using GDI.
I created a C# class using #Jonas Kohls answer from here
The class works like a charm and makes it easy to work with multiple forms just call DarkTitleBarClass.UseImmersiveDarkMode(Handle, true); in your load method.
I used this when upgrading some old WinForms apps so its WinForm friendly but only tested on win 8,10 and 11
Example image below Winforms .NetCore 6.0
using System.Runtime.InteropServices;
namespace Myapp.Classes
{
internal class DarkTitleBarClass
{
[DllImport("dwmapi.dll")]
private static extern int DwmSetWindowAttribute(IntPtr hwnd, int attr,
ref int attrValue, int attrSize);
private const int DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1 = 19;
private const int DWMWA_USE_IMMERSIVE_DARK_MODE = 20;
internal static bool UseImmersiveDarkMode(IntPtr handle, bool enabled)
{
if (IsWindows10OrGreater(17763))
{
var attribute = DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1;
if (IsWindows10OrGreater(18985))
{
attribute = DWMWA_USE_IMMERSIVE_DARK_MODE;
}
int useImmersiveDarkMode = enabled ? 1 : 0;
return DwmSetWindowAttribute(handle, attribute, ref useImmersiveDarkMode, sizeof(int)) == 0;
}
return false;
}
private static bool IsWindows10OrGreater(int build = -1)
{
return Environment.OSVersion.Version.Major >= 10 && Environment.OSVersion.Version.Build >= build;
}
}
}
[EDIT] Don't forget to add a app.manifest and uncomment the appropriate supported OS.
If you want to have a title bar different from others, consider getting a Community license for this
I tried once and it worked. I just had to install it and change Form to SfForm
This is the using statement
using Syncfusion.WinForms.Controls;
The references for WinForms are Syncfusion.Core.WinForms and Syncfusion.Shared.Base
This is easy to do:
Right-click on the desktop, and select "Personalize".
Click on the "Window Color" tile at the bottom of the screen.
Choose your new color.
If your computer is configured to use the Aero theme, you can choose from one of the standard colors or mix one of your own.
If you're using the Classic theme, you'll see a "Window Color and Appearance" dialog you can use to set colors. Click on the title bar the sample desktop, the one called "Active Window", and then use the "Color 1" and "Color 2" drop-down boxes to pick a new color.
I can only assume this is what you meant, because there is absolutely no excuse to change only the color of your application's title bar. There's a reason that this is a system-wide setting.
Always obey the user's preferences. If they wanted your title bar to be a different color, they would choose a different color.

Disable Painting of the VScrollbar in a System.Windows.Forms.RichTextBox

I have a custom control inherited from RichTextBox.
This control has the ability to "disable" rich text editing.
I achive this by just setting the Rtf property to the text property during the TextChanged event.
this is how my code looks like:
private bool lockTextChanged;
void RichTextBox_TextChanged(object sender, EventArgs e)
{
// prevent StackOverflowException
if (lockTextChanged) return;
// remember current position
int rtbstart = rtb.SelectionStart;
int len = rtb.SelectionLength;
// prevent painting
rtb.SuspendLayout();
// set the text property to remove the entire formatting.
lockTextChanged = true;
rtb.Text = rtb.Text;
rtb.Select(rtbstart, len);
lockTextChanged = false;
rtb.ResumeLayout(true);
}
That worked well. However in a large text with like 200 lines the controls jitters (you see the first lines of text for the wink).
To prevent that from happening I filter the WM_PAINT between SuspendLayout() and ResumeLayout()
private bool layoutSuspended;
public new void SuspendLayout()
{
layoutSuspended = true;
base.SuspendLayout();
}
public new void ResumeLayout()
{
layoutSuspended = false;
base.ResumeLayout();
}
public new void ResumeLayout(bool performLayout)
{
layoutSuspended = false;
base.ResumeLayout(performLayout);
}
private const int WM_PAINT = 0x000F;
protected override void WndProc(ref System.Windows.Forms.Message m)
{
if (!(m.Msg == WM_PAINT && layoutSuspended))
base.WndProc(ref m);
}
that did the trick, the RichTextBox isn't jittering anymoe.
That's what I wanted to achive, except one thing:
The scrollbar is still jittering everytime I type text to my control.
Now my question:
Does anyone have a clue for me how to prevent the scrollbar from redrawing during Suspend/Resume Layout?
SuspendLayout() isn't going to have an effect, there are no child controls inside an RTB that need to be arranged. RTB is missing the Begin/EndUpdate() methods that most controls have, although it supports it. It suspends painting, although I'm not so sure it suspends updates to the scrollbar. Add them as follows:
public void BeginUpdate() {
SendMessage(this.Handle, WM_SETREDRAW, (IntPtr)0, IntPtr.Zero);
}
public void EndUpdate() {
SendMessage(this.Handle, WM_SETREDRAW, (IntPtr)1, IntPtr.Zero);
}
// P/invoke declarations
private const int WM_SETREDRAW = 0xb;
[System.Runtime.InteropServices.DllImport("user32.dll")]
private extern static IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wp, IntPtr lp);
The better way to prevent the user from editing text is to set the ReadOnly property to True. Removing the scrollbar entirely is possible too by overriding CreateParams.

Winforms Wait to draw Until Controls Added [duplicate]

This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
How do I suspend painting for a control and its children?
I am adding a couple hundred controls to a form and the form flickers until its done as it adds each control, is there anyway to stop this?
The answer is the same as the answer to this question:
How do I suspend painting for a control and its children?
(Answer copied for convenience: originally from: https://stackoverflow.com/users/36860/ng5000)
At my previous job we struggled with getting our rich UI app to paint instantly and smoothly. We were using standard .Net controls, custom controls and devexpress controls.
After a lot of googling and reflector usage I came across the WM_SETREDRAW win32 message. This really stops controls drawing whilst you update them and can be applied, IIRC to the parent/containing panel.
This is a very very simple class demonstrating how to use this message:
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();
}
}
There are fuller discussions on this - google for C# and WM_SETREDRAW, e.g.
C# Jitter
Suspending Layouts
might want to surround your code with SuspendLayout and ResumeLayout properties of the Form
this.SuspendLayout();
//create controls
this.ResumeLayout(true);
The following is the same solution of ng5000 but doesn't use P/Invoke.
public static class SuspendUpdate
{
private const int WM_SETREDRAW = 0x000B;
public static void Suspend(Control control)
{
Message msgSuspendUpdate = Message.Create(control.Handle, WM_SETREDRAW, IntPtr.Zero,
IntPtr.Zero);
NativeWindow window = NativeWindow.FromHandle(control.Handle);
window.DefWndProc(ref msgSuspendUpdate);
}
public static void Resume(Control control)
{
// Create a C "true" boolean as an IntPtr
IntPtr wparam = new IntPtr(1);
Message msgResumeUpdate = Message.Create(control.Handle, WM_SETREDRAW, wparam,
IntPtr.Zero);
NativeWindow window = NativeWindow.FromHandle(control.Handle);
window.DefWndProc(ref msgResumeUpdate);
control.Invalidate();
}
}

Categories

Resources