I need to move several external application windows at the same time (together) as a group to simulate scrolling functionality.
If your first thought is "why would you want to do that" or "you should never do that", just pretend that it's a good idea and would be fantastic.
So far I have tried the PInvoke methods MoveWindow, SetWindowPos, and (BeginDeferWindowPos, DeferWindowPos, EndDeferWindowPos).
All of these methods produce about the same performance. Moving one window is super snappy, two windows is no big deal, but once I try to move three or more windows things start to get ugly. Here is a snippet using the deferred method to show you how I am doing things and keeping track of performance.
private void multiMoveWindows(int moveAmt, int startingIndex)
{
int tempX;
int howmanyTimes = 0;
int numOfWidgetsToMove = 0;
int moveAmtRemaining = moveAmt;
IntPtr MultiWindowStructure;
System.Diagnostics.Stopwatch t = new Stopwatch();
WidgetTracker[] tmp_WhichWidgets = new WidgetTracker[10];
foreach (KeyValuePair<string, WidgetTracker> entry in m_dictWT)
{
WidgetTracker tmp = new WidgetTracker();
tmp = entry.Value;
if (tmp.WIndex >= startingIndex)
{
tmp_WhichWidgets[numOfWidgetsToMove] = tmp;
++numOfWidgetsToMove;
}
}
for (int i = 0; i < moveAmtRemaining; i++)
{
MultiWindowStructure = Native_Methods.BeginDeferWindowPos(numOfWidgetsToMove);
foreach (WidgetTracker tmp in tmp_WhichWidgets)
{
if (tmp != null)
{
tmp.surveilWidget();
tempX = tmp.WidgetRectLeft - 1;
MultiWindowStructure = Native_Methods.DeferWindowPos(
MultiWindowStructure, tmp.WidgetHandle, HWND.NOTOPMOST, tempX, tmp.WidgetRectTop, 0, 0, SWP.NOREDRAW | SWP.NOZORDER | SWP.NOACTIVATE | SWP.NOSIZE);
howmanyTimes++;
}
}
t.Start();
Native_Methods.EndDeferWindowPos(MultiWindowStructure);
t.Stop();
}
MessageBox.Show(howmanyTimes.ToString() + " move requests made." + Environment.NewLine +
"Execution of EndDeferWindowPos took " + t.Elapsed.Seconds.ToString() + " sec " + t.Elapsed.Milliseconds.ToString() + " ms." );
}
And here is the result for moving 4 windows, 320px left, 1px at a time:
As you can imagine, waiting 14.5 seconds to see 4 windows make their way across the screen 320 pixels is painful to watch.
I've added a few SWP flags and improved performance, but 7.5 seconds is still not workable.
- How can this be accomplished in a fluid manner?
Yes, I realize that I can move more than 1px at a time, but that won't resolve the underlying issue. The way I understand it, using any of the PInvoke methods to move windows posts messages to the WinAPI message pump (WndProc()) and waits for the message to be closed / caught / handled before posting the next message. I think this is where I am spending that 14.5 seconds - waiting for messages to be handled.
I have tried using SWP ASYNCWINDOWPOS and the result is horrendous. Each window only gets a few dozen of the messages seemingly at random so that windows are overlapping and as a whole the train barely moves.
The closest I've come to a solution is to assign each "widget" a parent (for example a WindowsFormsHost + an MDI window) and putting them in a form/window and then scrolling that form/window. However I have several reasons for not wanting to do that.
Please help!
Update: Further information available on CodeProject where I have duplicated this question.
Update
Adding SWP flags helped a lot but what I determined to actually be slowing things down so much was my test application. Moving 4 of these WPF windows at a time I got it down to about 8.5 seconds.
When I changed out my test window for a standard empty notepad, that dropped to 0.6 seconds which would be okay for my purposes. Therefore I'm pretty confident that the code is sound. So then the question becomes, how to minimize window flicker when implementing this code with a time-dependent move-offset (controlled motion) routine.
This is still a work in progress so I'd appreciate any suggestions.