Winforms Wait to draw Until Controls Added [duplicate] - c#

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

Related

How to capture data in a window

I have a desktop application installed on my machine. When I start a program some kind of window gets open. let's say, something like this (just example):
So, I want to write an application in C# that will find this window and capture some data from it.
What tools should I look at? I want to go with a path of least resistance.
I need to capture images, text from textboxes, and also find controls by text and click on them.
I suggest you use the cool but little-known UI Automation API for this work.
For this, the first thing to test is launch the associated UISpy tool. It will display a tree of all accessible windows on screen. It also is able to run some actions like pressing a menu, selecting an item, etc. This is using what's called UI Automation Control Patterns, which provide a way to categorize and expose a control's functionality independent of the control type or the appearance of the control.
So, if you can automate this application with UI Spy, you also can do the exact same thing using .NET code (UISpy is itself simply using the underlying API).
Here is an interesting tutorial article about UI automation programming: The Microsoft UI Automation Library
You should start enumerating handles of all windows for that process :
https://stackoverflow.com/a/2584672/351383
Then for each handle get information about text and position, with position infomation you can take screenshots of desktop on that position to get images AFAIK there is no other way to get images from a window of running application.
When you got screen positions of the controls then use from link below to simulate left mouse click, search windows for some text and then click on some point inside control, here is the method that will click a point :
https://stackoverflow.com/a/10355905/351383
I put toghether quick class to gather that data for process :
public static class ProcessSpy
{
public static List<ProcessSpyData> GetDataForProcess(string processName)
{
var result = new List<ProcessSpyData>();
Process myProc = Process.GetProcessesByName(processName).FirstOrDefault();
if (myProc != null)
{
var myHandles = EnumerateProcessWindowHandles(myProc);
foreach (IntPtr wndHandle in myHandles)
{
result.Add(new ProcessSpyData(wndHandle));
}
}
return result;
}
delegate bool EnumThreadDelegate(IntPtr hWnd, IntPtr lParam);
[DllImport("user32.dll")]
static extern bool EnumThreadWindows(int dwThreadId, EnumThreadDelegate lpfn, IntPtr lParam);
static IEnumerable<IntPtr> EnumerateProcessWindowHandles(Process prc)
{
var handles = new List<IntPtr>();
foreach (ProcessThread thread in prc.Threads)
EnumThreadWindows(thread.Id, (hWnd, lParam) => { handles.Add(hWnd); return true; }, IntPtr.Zero);
return handles;
}
}
public class ProcessSpyData
{
private const uint WM_GETTEXT = 0x000D;
[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, int wParam, StringBuilder lParam);
[DllImport("user32.dll")]
private static extern bool GetClientRect(IntPtr hWnd, out RECT lpRect);
[StructLayout(LayoutKind.Sequential)]
private struct RECT
{
int left, top, right, bottom;
public Rectangle ToRectangle()
{
return new Rectangle(left, top, right - left, bottom - top);
}
}
[DllImport("user32.dll")]
static extern bool ClientToScreen(IntPtr hWnd, ref Point lpPoint);
public IntPtr WindowHandle { get; private set; }
public string WindowText { get; private set; }
public Rectangle ClientRect { get; private set; }
public Rectangle ScreenPos { get; private set; }
public ProcessSpyData(IntPtr windowHandle)
{
this.WindowHandle = windowHandle;
GetWindowText();
GetWindowSize();
}
private void GetWindowText()
{
StringBuilder message = new StringBuilder(1024);
SendMessage(this.WindowHandle, WM_GETTEXT, message.Capacity, message);
this.WindowText = message.ToString();
}
private void GetWindowSize()
{
var nativeRect = new RECT();
GetClientRect(this.WindowHandle, out nativeRect);
this.ClientRect = nativeRect.ToRectangle();
Point loc = this.ClientRect.Location;
ClientToScreen(this.WindowHandle, ref loc);
this.ScreenPos = new Rectangle(loc, this.ClientRect.Size);
}
}
That should get you started, but you have to be aware if app is using non standard controls then there is no way to get text out of it with this method, and for images maybe you will get better results looking at executable resources.
UPDATE
Geting controls text for various control types (MFC, winforms, Delphi VCL etc.) would be very hard task, but for winforms see excelent Managed Windows API, they even have some sort of spy application in tools, look at that.
What kind of data are you trying to capture?
You may try listening to windows messages or reading the memory.
Depending on how much of these type of tasks you are going to be doing in the future (or how important this one is) you could try investing in something like Ranorex Spy (Ranorex studio is ott).
Link: http://www.ranorex.com/product/tools/ranorex-spy.html
there is no other way than to inject the application you want to inspect. This is how UISpy actually runs. This is also why UISpy should be run with Administrative credential.

RichTextBox BeginUpdate() EndUpdate() Extension Methods Not Working

I have a richTextBox I am using to perform some syntax highlighting. This is a small editing facility so I have not written a custom syntax highlighter - instead I am using Regexs and updating upon the detection of an input delay using an event handler for the Application.Idle event:
Application.Idle += new EventHandler(Application_Idle);
in the event handler I check for the time the text box has been inactive:
private void Application_Idle(object sender, EventArgs e)
{
// Get time since last syntax update.
double timeRtb1 = DateTime.Now.Subtract(_lastChangeRtb1).TotalMilliseconds;
// If required highlight syntax.
if (timeRtb1 > MINIMUM_UPDATE_DELAY)
{
HighlightSyntax(ref richTextBox1);
_lastChangeRtb1 = DateTime.MaxValue;
}
}
But even for relatively small highlights the RichTextBox flickers heavily and it has no richTextBox.BeginUpdate()/EndUpdate() methods. To overcome this I found this answer to a similar dilemma by Hans Passant (Hans Passant has never let me down!):
using System;
using System.Windows.Forms;
using System.Runtime.InteropServices;
class MyRichTextBox : RichTextBox
{
public void BeginUpdate()
{
SendMessage(this.Handle, WM_SETREDRAW, (IntPtr)0, IntPtr.Zero);
}
public void EndUpdate()
{
SendMessage(this.Handle, WM_SETREDRAW, (IntPtr)1, IntPtr.Zero);
}
[DllImport("user32.dll")]
private static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wp, IntPtr lp);
private const int WM_SETREDRAW = 0x0b;
}
However, this gives me odd behaviour upon an update; the cursor dies/freezes and shows nothing but odd looking stripes (see image below).
I clearly can't use an alternative thread to update the UI, so what am I doing wrong here?
Thanks for your time.
Try modifying the EndUpdate to also call Invalidate afterwards. The control doesn't know it needs to do some updating, so you need to tell it:
public void EndUpdate()
{
SendMessage(this.Handle, WM_SETREDRAW, (IntPtr)1, IntPtr.Zero);
this.Invalidate();
}

Can I choose a custom image for C# Windows Application Drag Drop functions?

I am writing a small project where I would like to make use of drag and drop functionalty to ease some of the operations for the end user. To make the application a little more appealing, I would like to display the object being dragged. I have found some resources with WPF, but I don't know any WPF, so it becomes a bit tough to bite down on that whole subject for this single task. I would like to know how this can be done with "regular" C# Windows Forms. So far, all drag drop tutorials I've found just talk about the drop effects which is just a preset of a few icons.
WPF sounds like something I want to learn after this project.
The blog link provided by #Jesper gives the two or three key nuggets of info, but I think it is worth bringing it into S.O. for posterity.
Set up the custom cursor
The code below allows you to use an arbitrary image for your cursor
public struct IconInfo
{
public bool fIcon;
public int xHotspot;
public int yHotspot;
public IntPtr hbmMask;
public IntPtr hbmColor;
}
[DllImport("user32.dll")]
public static extern IntPtr CreateIconIndirect(ref IconInfo icon);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GetIconInfo(IntPtr hIcon, ref IconInfo pIconInfo);
public static Cursor CreateCursor(Bitmap bmp, int xHotSpot, int yHotSpot)
{
IconInfo tmp = new IconInfo();
GetIconInfo(bmp.GetHicon(), ref tmp);
tmp.xHotspot = xHotSpot;
tmp.yHotspot = yHotSpot;
tmp.fIcon = false;
return new Cursor(CreateIconIndirect(ref tmp));
}
Set up the drag and drop event handling
This is well covered in other tutorials and answers. The specific events we are concerned about here are GiveFeedback and DragEnter, on any control where you want the custom cursor to apply.
private void DragSource_GiveFeedback(object sender, GiveFeedbackEventArgs e)
{
e.UseDefaultCursors = 0;
}
private void DragDest_DragEnter(object sender, DragEventArgs e)
{
Cursor.Current = CreateCursor(bitmap, 0, 0);
}
You need to hide the default cursor and create your own window containing your custom image and then move that window with the position of the mouse.
You might also take a look at http://web.archive.org/web/20130127145542/http://www.switchonthecode.com/tutorials/winforms-using-custom-cursors-with-drag-drop
UPDATE 2015-11-26
Updated the link to point to archive.org's last snapshot

C#: Getting label injected into native app's statusbar pane to show up

I am writing a .Net COM DLL that runs inside a native Windows application.
I am attempting to inject an additional pane into this app's statusbar, and it does not have any specific implementation to do so, so I am trying to subclass the app's statusbar myself.
I am using the Win32 API SetParent() to switch the parent of a label control from a .Net form to the msctls_statusbar32 instance. I used a label because it is the closest implementation to a native "static" class control that I could find without writing my own control.
Somehow I've even managed to get NativeWindow to successfully hook in to both the statusbar and my label's messages (though at the moment it just passes them all to the next WndProc), and I've assigned matching styles and styleExs to my label's window, and I can see my label as a child with the msctls_statusbar32 as its parent. Everything looks like it should be working correctly, but it's not. My control does not show up in the parent app's statusbar.
What I don't understand is why it is not showing up. Nearly everything I can think of matches correctly -- granted, the class for my label is "WindowsForms10.STATIC.app.0.378734a" and not "static", but other than that it is on the correct process and thread, has matching window styles (at least the hex value... Spy++ seems to enumerate them differently), and for all purposes pretty much blends in with the rest of the controls. Would anybody know what else needs to be done to get it to be visible?
(I had originally gone the route of CreateWindowEx and setting WNDPROC callbacks but I could not get the app to work... it would freeze for a minute or so and then unfreeze, and I would notice my window disappeared from the window tree)
Thank you!
you can try working with existing status bar control; what you can do is to reset text to an existing section of it or add a new one; also you would probably need to set up new widths to existing sections of the statusbar. You can find details on how to work with statusbar control here:msdn Status Bars
Please find an example of how you could do it below. I actually tried it with c# com object used by win32 application and it seem to work fine for me.
[ComVisible(true)]
[Guid("CC5B405F-F3CD-417E-AA00-4638A12A2E94"),
ClassInterface(ClassInterfaceType.None)]
public class TestInterface : ITestInterface // see declaration of the interface below
{
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr FindWindowEx(IntPtr parentHandle, IntPtr childAfter, string className, IntPtr windowTitle);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, int wParam, IntPtr lParam);
public const int SB_SETTEXT = 1035;
public const int SB_SETPARTS = 1028;
public const int SB_GETPARTS = 1030;
public unsafe void Test()
{
IntPtr mainWindowHandle = Process.GetCurrentProcess().MainWindowHandle;
// find status bar control on the main window of the application
IntPtr statusBarHandle = FindWindowEx(mainWindowHandle, IntPtr.Zero, "msctls_statusbar32", IntPtr.Zero);
if (statusBarHandle != IntPtr.Zero)
{
// set text for the existing part with index 0
IntPtr text = Marshal.StringToHGlobalAuto("test text 0");
SendMessage(statusBarHandle, SB_SETTEXT, 0, text);
Marshal.FreeHGlobal(text);
// create new parts width array
int nParts = SendMessage(statusBarHandle, SB_GETPARTS, 0, IntPtr.Zero).ToInt32();
nParts++;
IntPtr memPtr = Marshal.AllocHGlobal(sizeof(int) * nParts);
int partWidth = 100; // set parts width according to the form size
for (int i = 0; i < nParts; i++)
{
Marshal.WriteInt32(memPtr, i*sizeof(int), partWidth);
partWidth += partWidth;
}
SendMessage(statusBarHandle, SB_SETPARTS, nParts, memPtr);
Marshal.FreeHGlobal(memPtr);
// set text for the new part
IntPtr text0 = Marshal.StringToHGlobalAuto("new section text 1");
SendMessage(statusBarHandle, SB_SETTEXT, nParts-1, text0);
Marshal.FreeHGlobal(text0);
}
}
}
[ComVisible(true)]
[Guid("694C1820-04B6-4988-928F-FD858B95C880")]
public interface ITestInterface
{
[DispId(1)]
void Test();
}
hope this helps, regards
Many possible reasons:
The .Net Label control blows up when it finds it does not have a WinForms parent.
The native Status bar is drawing over the label control becuase of incorrect Z-Order.
The Label control is not visible.
As it turns out the answer was dumbfoundingly simple... the label's X and Y coords were out of the display area of the statusbar parent. Moving them to (0, 0) and it shows up right there! Of course, now the problems have moved on to: C# WinForms control in .Net COM Server won't redraw

How do you prevent a RichTextBox from refreshing its display?

I have a RichTextBox where I need to update the Text property frequently, but when I do so the RichTextBox "blinks" annoyingly as it refreshes all throughout a method call.
I was hoping to find an easy way to temporarily suppress the screen refresh until my method is done, but the only thing I've found on the web is to override the WndProc method. I've employed this approach, but with some difficulty and side effects, and it makes debugging harder, too. It just seems like there's got to be a better way of doing this. Can someone point me to a better solution?
Here is complete and working example:
private const int WM_USER = 0x0400;
private const int EM_SETEVENTMASK = (WM_USER + 69);
private const int WM_SETREDRAW = 0x0b;
private IntPtr OldEventMask;
[DllImport("user32.dll", CharSet=CharSet.Auto)]
private static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);
public void BeginUpdate()
{
SendMessage(this.Handle, WM_SETREDRAW, IntPtr.Zero, IntPtr.Zero);
OldEventMask = (IntPtr)SendMessage(this.Handle, EM_SETEVENTMASK, IntPtr.Zero, IntPtr.Zero);
}
public void EndUpdate()
{
SendMessage(this.Handle, WM_SETREDRAW, (IntPtr)1, IntPtr.Zero);
SendMessage(this.Handle, EM_SETEVENTMASK, IntPtr.Zero, OldEventMask);
}
I asked the original question, and the answer that worked best for me was BoltBait's use of SendMessage() with WM_SETREDRAW. It seems to have fewer side effects than the use of the WndProc method, and in my application performs twice as fast as LockWindowUpdate.
Within my extended RichTextBox class, I just added these two methods, and I call them whenever I need to stop restart repainting while I'm doing some processing. If I were wanting to do this from outside of the RichTextBox class, I think it would work by just replacing "this" with the reference to your RichTextBox instance.
private void StopRepaint()
{
// Stop redrawing:
SendMessage(this.Handle, WM_SETREDRAW, 0, IntPtr.Zero);
// Stop sending of events:
eventMask = SendMessage(this.Handle, EM_GETEVENTMASK, 0, IntPtr.Zero);
}
private void StartRepaint()
{
// turn on events
SendMessage(this.Handle, EM_SETEVENTMASK, 0, eventMask);
// turn on redrawing
SendMessage(this.Handle, WM_SETREDRAW, 1, IntPtr.Zero);
// this forces a repaint, which for some reason is necessary in some cases.
this.Invalidate();
}
Found here: http://bytes.com/forum/thread276845.html
I ended up sending a WM_SETREDRAW via SendMessage to disable then reenable
followed by an Invalidate() after I finished updating. That seemed to work.
I've never tried this method. I have written an application with a RTB that has syntax highlighting and used the following in the RTB class:
protected override void WndProc(ref Message m)
{
if (m.Msg == paint)
{
if (!highlighting)
{
base.WndProc(ref m); // if we decided to paint this control, just call the RichTextBox WndProc
}
else
{
m.Result = IntPtr.Zero; // not painting, must set this to IntPtr.Zero if not painting otherwise serious problems.
}
}
else
{
base.WndProc(ref m); // message other than paint, just do what you normally do.
}
}
Hope this helps.
Could you just store the Text into a string, do your manipulations on the string, and at the end of the method, store it back into the Text property?
I would suggest looking at LockWindowUpdate
[DllImport("user32.dll", EntryPoint="LockWindowUpdate", SetLastError=true,
ExactSpelling=true, CharSet=CharSet.Auto,
CallingConvention=CallingConvention.StdCall)]
Try this out:
myRichTextBox.SuspendLayout();
DoStuff();
myRichTextBox.ResumeLayout();

Categories

Resources