Unhook Window into its original State - c#

I hook a window into a Panel using this code:
SetParent(win, pnlApp.Handle);
SetWindowLong(win, GWL_STYLE, WS_VISIBLE);
MoveWindow(win, 0, 0, pnlApp.Width, pnlApp.Height, true);
where win is the Window handle, as an IntPtr. Here think about Notepad, it will hook very well into a Panel.
But when I try to release (unhook) it using this code:
SetParent(win, (IntPtr)0);
It's shown without the Window frame! I mean it will release into the Desktop again, but it has not any Window frame.
How to solve this issue?

In relation to the code in the question:
SetWindowLong() should be replaced by SetWindowLongPtr() - see the warning in the Docs. The latter calls the former in case the calling code requires it.
You need to call GetWindowLongPtr() to get the current Window Styles, then add or remove Styles as needed; store the original value: it will be used to restore the previous state.
► In the example, the WS_SYSMENU Style is removed, hence the Window will not show the System Menu and all the Buttons usually located in the Caption. It's just an example, to see how this works (it may not be effective with all Windows).
SetParent() returns the Handle of the previous Parent. Store this value, it will be needed to restore the Window to its previous owner, whatever that is. If the Window is a top-level Window, the previous Parent appears to be the Desktop, so you expect IntPtr.Zero. It might not be, you can get a Handle that is not IntPtr.Zero. Just store the returned value, see what that is, if you're interested :)
You should check the return value of both GetWindowLongPtr() and SetWindowLongPtr(): if it's 0, then there was an error. You can use Marshal.GetLastWin32Error() to get the error code. Note that SetWindowLongPtr() doesn't clear the error code, so you have to clear it yourself, calling SetLastError(0).
You can use MoveWindow() to reposition the Windows inside the new Parent's bounds; I suggest to call SetWindowPos() instead, you have more options. See the code here.
Disclaimer: keep in mind what's in Raymond Chen's blog post:
Is it legal to have a cross-process parent/child or owner/owned window relationship
In other words, it's a just because you asked thing :)
Set these Fields (or whatever fits):
windowdHwnd is the Handle of the Window to re-parent
oldParent is the Handle of the previous Parent, returned by SetParent()
oldStyles will store the previous Window's Styles, returned by GetWWindowLongPtr()
IntPtr windowdHwnd = IntPtr.Zero;
IntPtr oldParent = (IntPtr)(-1);
int oldStyles = 0;
// [...]
When you have the Handle of the Window you care about, set windowdHwnd to this value. Call this code after (it could be a method, pass the Window Handle and the Container Control that will be the new Parent - here, a Control named somePanel).
// Already parented, resets the previous styles, clean up and return
if (windowdHwnd != IntPtr.Zero && oldParent != (IntPtr)(-1)) {
NativeMethods.SetParent(windowdHwnd, oldParent);
NativeMethods.SetWindowLongPtr(windowdHwnd, NativeMethods.GWL_Flags.GWL_STYLE, new IntPtr(oldStyles));
windowdHwnd = IntPtr.Zero;
oldParent = (IntPtr)(-1);
return;
}
// Store the existing Styles, to restore when the Window is dismissed
oldStyles = NativeMethods.GetWindowLongPtr(windowdHwnd, NativeMethods.GWL_Flags.GWL_STYLE);
if (oldStyles == IntPtr.Zero) {
int error = Marshal.GetLastWin32Error(); // Show the error code or throw an exception
return;
}
// Removes the System Menu from the Window: it will also remove the Buttons from the Caption
int newStyle = oldStyles.ToInt32()^ (int)NativeMethods.WinStyles.WS_SYSMENU;
NativeMethods.SetLastError(0);
// Sets the new Styles
IntPtr result = NativeMethods.SetWindowLongPtr(windowdHwnd, NativeMethods.GWL_Flags.GWL_STYLE, (IntPtr)newStyle);
if (result == IntPtr.Zero) {
int error = Marshal.GetLastWin32Error(); // Show the error code or throw an exception
return;
}
oldParent = NativeMethods.SetParent(windowdHwnd, somePanel.Handle);
// Repositions the Window and shows it, if needed
var flags = NativeMethods.SWP_Flags.SWP_ASYNCWINDOWPOS | NativeMethods.SWP_Flags.SWP_SHOWWINDOW;
NativeMethods.SetWindowPos(windowdHwnd, IntPtr.Zero, 0, 0, somePanel.Width, somePanel.Height, flags);
You can write (this just removes):
int newStyle = oldStyles &~(int)NativeMethods.WinStyles.WS_SYSMENU;
instead of (this switches on/off):
int newStyle = oldStyles ^ (int)NativeMethods.WinStyles.WS_SYSMENU;
NativeMethods class:
using System.Runtime.InteropServices;
public class NativeMethods
{
[Flags]
public enum SWP_Flags : uint
{
/// <summary>Retains the current size (ignores the cx and cy parameters).</summary>
SWP_NOSIZE = 0x0001,
/// <summary>Retains the current position (ignores X and Y parameters).</summary>
SWP_NOMOVE = 0x0002,
/// <summary>Retains the current Z order (ignores the hWndInsertAfter parameter).</summary>
SWP_NOZORDER = 0x0004,
/// <summary>Does not redraw changes. If this flag is set, no repainting of any kind occurs. This applies to
/// the client area, the nonclient area (including the title bar and scroll bars), and any part of the parent
/// window uncovered as a result of the window being moved. When this flag is set, the application must
/// explicitly invalidate or redraw any parts of the window and parent window that need redrawing.</summary>
SWP_NOREDRAW = 0x0008,
/// <summary>Does not activate the window. If this flag is not set, the window is activated and moved to the
/// top of either the topmost or non-topmost group (depending on the setting of the hWndInsertAfter
/// parameter).</summary>
SWP_NOACTIVATE = 0x0010,
/// <summary>Draws a frame (defined in the window's class description) around the window.</summary>
SWP_DRAWFRAME = 0x0020,
/// <summary>Applies new frame styles set using the SetWindowLong function. Sends a WM_NCCALCSIZE message to
/// the window, even if the window's size is not being changed. If this flag is not specified, WM_NCCALCSIZE
/// is sent only when the window's size is being changed.</summary>
SWP_FRAMECHANGED = 0x0020,
/// <summary>Displays the window.</summary>
SWP_SHOWWINDOW = 0x0040,
/// <summary>Hides the window.</summary>
SWP_HIDEWINDOW = 0x0080,
/// <summary>Discards the entire contents of the client area. If this flag is not specified, the valid
/// contents of the client area are saved and copied back into the client area after the window is sized or
/// repositioned.</summary>
SWP_NOCOPYBITS = 0x0100,
/// <summary>Does not change the owner window's position in the Z order.</summary>
SWP_NOOWNERZORDER = 0x0200,
/// <summary>Same as the SWP_NOOWNERZORDER flag.</summary>
SWP_NOREPOSITION = 0x0200,
/// <summary>Prevents the window from receiving the WM_WINDOWPOSCHANGING message.</summary>
SWP_NOSENDCHANGING = 0x0400,
/// <summary>Internal use.</summary>
SWP_NOCLIENTSIZE = 0x0800,
/// <summary>Internal use.</summary>
SWP_NOCLIENTMOVE = 0x1000,
/// <summary>Prevents generation of the WM_SYNCPAINT message.</summary>
SWP_DEFERERASE = 0x2000,
/// <summary>If the calling thread and the thread that owns the window are attached to different input queues,
/// the system posts the request to the thread that owns the window. This prevents the calling thread from
/// blocking its execution while other threads process the request.</summary>
SWP_ASYNCWINDOWPOS = 0x4000
}
[Flags]
public enum WinStyles : uint
{
WS_BORDER = 0x00800000, //The window has a thin-line border.
WS_CAPTION = 0x00C00000, //The window has a title bar (includes the WS_BORDER style).
WS_CHILD = 0x40000000, //The window is a child window. A window with this style cannot have a menu bar. This style cannot be used with the WS_POPUP style.
WS_CHILDWINDOW = 0x40000000, //Same as the WS_CHILD style.
WS_CLIPCHILDREN = 0x02000000, //Excludes the area occupied by child windows when drawing occurs within the parent window. This style is used when creating the parent window.
WS_CLIPSIBLINGS = 0x04000000, //Clips child windows relative to each other; that is, when a particular child window receives a WM_PAINT message, the WS_CLIPSIBLINGS style clips all other overlapping child windows out of the region of the child window to be updated. If WS_CLIPSIBLINGS is not specified and child windows overlap, it is possible, when drawing within the client area of a child window, to draw within the client area of a neighboring child window.
WS_DISABLED = 0x08000000, //The window is initially disabled. A disabled window cannot receive input from the user. To change this after a window has been created, use the EnableWindow function.
WS_DLGFRAME = 0x00400000, //The window has a border of a style typically used with dialog boxes. A window with this style cannot have a title bar.
WS_GROUP = 0x00020000, //The window is the first control of a group of controls. The group consists of this first control and all controls defined after it, up to the next control with the WS_GROUP style. The first control in each group usually has the WS_TABSTOP style so that the user can move from group to group. The user can subsequently change the keyboard focus from one control in the group to the next control in the group by using the direction keys.
//You can turn this style on and off to change dialog box navigation. To change this style after a window has been created, use the SetWindowLong function.
WS_HSCROLL = 0x00100000, //The window has a horizontal scroll bar.
WS_ICONIC = 0x20000000, //The window is initially minimized. Same as the WS_MINIMIZE style.
WS_MAXIMIZE = 0x01000000, //The window is initially maximized.
WS_MAXIMIZEBOX = 0x00010000, //The window has a maximize button. Cannot be combined with the WS_EX_CONTEXTHELP style. The WS_SYSMENU style must also be specified.
WS_MINIMIZE = 0x20000000, //The window is initially minimized. Same as the WS_ICONIC style.
WS_MINIMIZEBOX = 0x00020000, //The window has a minimize button. Cannot be combined with the WS_EX_CONTEXTHELP style. The WS_SYSMENU style must also be specified.
WS_OVERLAPPED = 0x00000000, //The window is an overlapped window. An overlapped window has a title bar and a border. Same as the WS_TILED style.
WS_OVERLAPPEDWINDOW = WS_OVERLAPPED | //The window is an overlapped window. Same as the WS_TILEDWINDOW style.
WS_CAPTION |
WS_SYSMENU |
WS_THICKFRAME |
WS_MINIMIZEBOX |
WS_MAXIMIZEBOX,
WS_POPUP = 0x80000000, //The windows is a pop-up window. This style cannot be used with the WS_CHILD style.
WS_POPUPWINDOW = WS_POPUP | //The window is a pop-up window. The WS_CAPTION and WS_POPUPWINDOW styles must be combined to make the window menu visible.
WS_BORDER |
WS_SYSMENU,
WS_SIZEBOX = 0x00040000, //The window has a sizing border. Same as the WS_THICKFRAME style.
WS_SYSMENU = 0x00080000, //The window has a window menu on its title bar. The WS_CAPTION style must also be specified.
WS_TABSTOP = 0x00010000, //The window is a control that can receive the keyboard focus when the user presses the TAB key. Pressing the TAB key changes the keyboard focus to the next control with the WS_TABSTOP style.
//You can turn this style on and off to change dialog box navigation. To change this style after a window has been created, use the SetWindowLong function. For user-created windows and modeless dialogs to work with tab stops, alter the message loop to call the IsDialogMessage function.
WS_THICKFRAME = 0x00040000, //The window has a sizing border. Same as the WS_SIZEBOX style.
WS_TILED = 0x00000000, //The window is an overlapped window. An overlapped window has a title bar and a border. Same as the WS_OVERLAPPED style.
WS_TILEDWINDOW = WS_OVERLAPPED | //The window is an overlapped window. Same as the WS_OVERLAPPEDWINDOW style.
WS_CAPTION |
WS_SYSMENU |
WS_THICKFRAME |
WS_MINIMIZEBOX |
WS_MAXIMIZEBOX,
WS_VISIBLE = 0x10000000, //The window is initially visible. This style can be turned on and off by using the ShowWindow or SetWindowPos function.
WS_VSCROLL = 0x00200000, //The window has a vertical scroll bar.
}
public enum GWL_Flags : int
{
GWL_USERDATA = -21,
GWL_EXSTYLE = -20,
GWL_STYLE = -16,
GWL_ID = -12,
GWLP_HWNDPARENT = -8,
GWLP_HINSTANCE = -6,
GWL_WNDPROC = -4,
DWLP_MSGRESULT = 0x0,
DWLP_DLGPROC = 0x4,
DWLP_USER = 0x8
}
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern IntPtr GetWindowLongPtr(IntPtr hWnd, GWL_Flags nIndex);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern IntPtr SetWindowLongPtr(IntPtr hWnd, GWL_Flags nIndex, IntPtr dwNewLong);
[DllImport("user32.dll", SetLastError = true)]
internal static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int x, int y, int cx, int cy, SWP_Flags uFlags);
[DllImport("kernel32.dll")]
internal static extern void SetLastError(uint dwErrCode);
}

Related

new window postion inside the panel in windows form app [duplicate]

I'm trying to set childForm as the child of the main Excel window using the SetParent API through PInvoke:
Form childForm = new MyForm();
IntPtr excelHandle = (IntPtr) excelApplication.Hwnd;
SetParent(childForm.Handle, excelHandle);
childForm.StartPosition = FormStartPosition.Manual;
childForm.Left = 0;
childForm.Top = 0;
As you can see above, my intention is also to position the child in the top left corner of Excel window. However, for some reason the childForm always ends up at some weird location.
What is it that I am doing wrong?
While all answers here suggest perfectly logical approaches, none of them worked for me. Then I tried MoveWindow. For some reason I don't understand, it did the job.
Here's the code:
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool MoveWindow(IntPtr hWnd, int X, int Y, int nWidth, int nHeight, bool bRepaint);
...
Form childForm = new MyForm();
IntPtr excelHandle = (IntPtr) excelApplication.Hwnd;
SetParent(childForm.Handle, excelHandle);
MoveWindow(childForm.Handle, 0, 0, childForm.Width, childForm.Height, true);
When using SetParent on a form that is currently a child of the desktop (in other words, one without a parent
set), you must set the WS_CHILD style and remove the WS_POPUP style. (See the Remarks section of the MSDN entry.) Windows requires that all owned windows have the WS_CHILD style set. This could also be causing the left and top properties to report/set the wrong values because the form doesn't know who it's daddy is. You can fix this by calling SetWindowLong after SetParent, but before you try to set the location:
//Remove WS_POPUP style and add WS_CHILD style
const UInt32 WS_POPUP = 0x80000000;
const UInt32 WS_CHILD = 0x40000000;
int style = GetWindowLong(this.Handle, GWL_STYLE);
style = (style & ~(WS_POPUP)) | WS_CHILD;
SetWindowLong(this.Handle, GWL_STYLE, style);
It depends on your ShowDialog call I believe. If you call ShowDialog without the parent paremeter, the parent is reset.
You could create a wrapper class that implements IWin32Window and returns the HWND to excel. Then you could pass that to the ShowDialog call of childForm.
You could also query the position of the excel application using GetWindowPos and then set the childForm accordingly.
Try a few things to diagnose the problem:
Put a breakpoint after setting Left
and Top, do Left and Top read zero?
Call SetParent last.
Make a method that sets Left and Top
again, and BeginInvoke the method.
Make sure your child window is really
the child. To do this call
ShowDialog, and try to click the
parent window. Make sure windows
prevents focus to the parent window.
Assuming you know how to get the hwnds of the windows you want to set z-order of, you can use this pInvoke:
public stati class WindowsApi
{
[DllImport("user32.dll")]
public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter,
int X, int Y, int cx, int cy, uint uFlags);
}
public class WindowZOrderPositioner
{
public void SetZOrder(IntPtr targetHwnd, IntPtr insertAfter)
{
IntPtr nextHwnd = IntPtr.Zero;
WindowsAPI.SetWindowPos(targetHwnd, insertAfter, 0, 0, 0, 0, SetWindowPosFlags.NoMove | SetWindowPosFlags.NoSize | SetWindowPosFlags.NoActivate);
}

Turn off 'Show window contents while dragging' setting while displaying my C# windows forms

There is a window display setting called 'Show window contents while dragging'.
http://www.thewindowsclub.com/disable-show-windows-contents-while-dragging
When this setting is turned on, if you drag a window the window will immediately repaint at the new position. If you resize a window, it will repaint the window for each new window size immediately, even if you are still holding down the mouse button.
When the setting is turned off, dragging or resizing the window simply shows an outline of the new window position or size until you release the mouse button and then it will paint the window at the new position or size.
I would like to display my forms in my C# WinForms application with 'Show window contents while dragging' setting turned off. However as this is an operating setting, I would only like this to be effective for when my forms are displayed regardless of what the setting is set to in the OS.
Is there a way to achieve this using some WinAPI calls to change the behavior specifically for my winforms?
If not, is there a way that I can change the setting programatically before my form is displayed and reset it after my form has closed? Would performing this change require admin rights (because I don't want that)?
The following code below will use the system setting of 'Show window content while dragging' for window re-positioning, while it will temporarily set it to off, while resizing the window and then set it back to the system setting.
This gives you flicker free form resizing in windows forms.
Since this property is a user setting that modifies the HKEY_CURRENT_USER\Control Panel\Desktop\DragFullWindows registry key, it should not require admin rights.
[DllImport("user32.dll", EntryPoint = "SystemParametersInfo", CharSet = CharSet.Auto)]
public static extern int GetSystemParametersInfo(int uAction, int uParam, out int lpvParam, int fuWinIni);
[DllImport("user32.dll", EntryPoint = "SystemParametersInfo", CharSet = CharSet.Auto)]
public static extern int SetSystemParametersInfo(int uAction, int uParam, int lpvParam, int fuWinIni);
private const int SPI_GETDRAGFULLWINDOWS = 38;
private const int SPI_SETDRAGFULLWINDOWS = 37;
private const int WM_SYSCOMMAND = 0x0112;
private const int SC_SIZE = 0xF000;
//change 'Show window content while dragging' to false while resizing
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_SYSCOMMAND && (m.WParam.ToInt32() & 0xfff0) == SC_SIZE)
{
int isDragFullWindow;
GetSystemParametersInfo(SPI_GETDRAGFULLWINDOWS, 0, out isDragFullWindow, 0);
if (isDragFullWindow != 0)
SetSystemParametersInfo(SPI_SETDRAGFULLWINDOWS, 0, 0, 0);
base.WndProc(ref m);
if (isDragFullWindow != 0)
SetSystemParametersInfo(SPI_SETDRAGFULLWINDOWS, 1, 0, 0);
}
else
{
base.WndProc(ref m);
}
}
//reduce control flickering and black stripes when window is resized
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
cp.ExStyle |= 0x02000000; // Turn on WS_EX_COMPOSITED
return cp;
}
}

How to keep border on a fixed form?

I want my Windows Forms form to keep the window border, while having no title bar and being non-resizable (fixed) (similarly to window previews, when one hovers mouse over button on the taskbar):
Setting ControlBox to false and Text to "" removes the title bar and keeps the border as I want to, but the border is visible only if the form is sizeable. When I set the FormBorderStyle to one of the Fixed* styles, the border disappears:
How may I achieve the described behavior?
You can pinvoke SetWindowsLong and adjust window styles:
// run in LINQpad
private const int GWL_STYLE = -16;
private const int WS_SIZEBOX = 0x040000;
[DllImport("user32.dll", SetLastError = true)]
private static extern int GetWindowLong(IntPtr hWnd, int nIndex);
[DllImport("user32.dll")]
private static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);
void Main()
{
var form = new Form();
form.ControlBox = false;
form.FormBorderStyle = FormBorderStyle.FixedDialog;
form.Show();
SetWindowLong(form.Handle, GWL_STYLE, GetWindowLong(form.Handle, GWL_STYLE) | WS_SIZEBOX);
}
After that you would have to prevent resizing manually though.
I just played around with a project of mine and set FormBorderStyle to FixedSingle through the Design view, and the window seems to keep the border for Windows 8. I initially had text in the title, which was forcing the border to render. I removed the text and the border no longer rendered, so as a hacky solution I just input an empty string, by hitting backspace a few times. This made the border show up and remain fixed.

C#: how to disable form move?

The MSV-Studio description for Locked is "The Locked property determines if we can move or resize the control" so I set the winforms Locked property to true but the form is still movable.
What is the correct way to prevent the form from moving?
Maximize it. Thanks, JackN. ;-)
I use the following code to display a form dialog window for a corporate security application written in-house - one of the requirements was that the form could not be moved, resized or live under any other form. Anyway, see below for a start...
/// <seealso href="http://msdn.microsoft.com/en-us/library/ms633548(v=vs.85).aspx"/>
/// <seealso href="http://msdn.microsoft.com/en-us/library/ms633545(v=vs.85).aspx"/>
public class ShowMessage
{
const int SW_SHOWMAXIMIZED = 3; //for maximising (if desired)
const int SW_SHOW = 5; //for simply activating the form (not needed)
const int SW_SHOWNORMAL = 1; //displays form at original size and position (what we use here)
const UInt32 SWP_NOSIZE = 0x0001; //cannot be resized
const UInt32 SWP_NOMOVE = 0x0002; //cannot be moved
static readonly IntPtr HWND_TOPMOST = new IntPtr(-1); //always lives at the top
const UInt32 TOPMOST_FLAGS = SWP_NOMOVE | SWP_NOSIZE; //sets the flags for no resize / no move
[DllImport("user32.dll")]
static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
[DllImport("user32.dll")]
static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);
/// <summary>
/// Displays the passed form using the parameters set in the base ShowMessage class
/// </summary>
/// <param name="frm">A Windows Form object</param>
/// <example><code>ShowMessage.ShowTopmost(new myForm());</code></example>
public static void ShowTopmost(Form frm)
{
ShowWindow(frm.Handle, SW_SHOWNORMAL); //shows the form
SetWindowPos(frm.Handle, HWND_TOPMOST, 0, 0, 0, 0, TOPMOST_FLAGS); //sets the form position as topmost, centered
}
}
Then I simply call
ShowMessage.ShowTopmost(new frmMessage());
I'm not saying it's the only way or the right way, but it a way to do it.
It's generally bad form to prevent the user from moving the window. The user should be able to have the window wherever he wants. Preventing resizing is one thing, preventing moving is another. I'm not aware of any C# native way of doing this, but you can probably hook down into Win32 to prevent the window from moving.
You might be able to use the Move event of the form and set the form back to the starting position. You would have to capture and store (in memory) the starting position.

How do I know when a different screensaver has been selected in the display properties dialog?

Ok so Google is not my friend tonight...
I have a screen saver, CC.Votd (Full source on Codeplex), and I've just started to implement the preview mode (/p argument) which is working ok. When it's in preview mode I make my form a child to the little computer monitor window and it draws in there.
This works fine and my application exits if the display properties dialog goes away.
The issue is that if I select my screen saver from the list and then select a different screen saver mine continues to run and draws over the newly selected screen saver's preview.
So how do I know when a different screen saver is selected and mine should close?
Edit: For Anon, here's the code I'm using to make my form a child of the preview window:
P/invokes:
[DllImport("user32.dll")]
static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
[DllImport("user32.dll")]
static extern int SetWindowLong(IntPtr hWnd, int nIndex, IntPtr dwNewLong);
[DllImport("user32.dll", SetLastError = true)]
static extern int GetWindowLong(IntPtr hWnd, int nIndex);
[DllImport("user32.dll")]
static extern bool GetClientRect(IntPtr hWnd, out Rectangle lpRect);
The code:
SetParent(Handle, _PreviewHandle);
SetWindowLong(Handle, -16, new IntPtr(GetWindowLong(Handle, -16) | 0x40000000));
Rectangle parentRectangle;
GetClientRect(_PreviewHandle, out parentRectangle);
Size = parentRectangle.Size;
Location = new Point(0, 0);
Complete form code: http://ccvotd.codeplex.com/SourceControl/changeset/view/40085#862458
Forgot to mention that I tried using IsWindowVisible() and that didn't work since the preview window is still visible and has the same handle as when my screen saver was selected.
Edit: Before I added the SetParent() and associated calls my application would continue to run after the display dialog was closed so I think that part is working and something different happens when the user selects a different screen saver.
As John K suggested I've been looking at my form with Spy++. I never see the WS_CHILD style applied. However all my debugging suggests it should be. I modified the code to:
long style = GetWindowLong(Handle, -16);
System.Diagnostics.Trace.WriteLine("Original Style: " + style);
style &= ~0x800000000;
style |= 0x40000000;
System.Diagnostics.Trace.WriteLine("Adjusted Style: " + style);
SetWindowLong(Handle, -16, new IntPtr(style));
System.Diagnostics.Trace.WriteLine("After Set Style: " + GetWindowLong(Handle, -16));
SetParent(Handle, _PreviewHandle);
System.Diagnostics.Trace.WriteLine("After Set Parent: " + GetWindowLong(Handle, -16));
And the style is the same on the last three traces, two of which should be getting the value from the form itself. Going to research my native API calls and clean up their declarations to see what I can figure out.
Thanks for all the help so far!
Solution: The problem ended up being that I was setting several properties of the form that resulted in the underlying .NET control overwriting my new styles. So changing:
SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint, true);
Capture = true;
if (!_IsPreview)
{
// Removed ...
}
else
{
SetWindowLong(Handle, -16, new IntPtr(GetWindowLong(Handle, -16) | 0x40000000));
SetParent(Handle, _PreviewHandle);
Rectangle parentRectangle;
GetClientRect(_PreviewHandle, out parentRectangle);
Size = parentRectangle.Size;
Location = new Point(0, 0);
}
ShowInTaskbar = false;
DoubleBuffered = true;
BackgroundImageLayout = ImageLayout.Stretch;
To:
SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint, true);
BackgroundImageLayout = ImageLayout.Stretch;
Capture = true;
DoubleBuffered = true;
ShowInTaskbar = false;
if (!_IsPreview)
{
// Removed ...
}
else
{
SetWindowLong(Handle, -16, new IntPtr(GetWindowLong(Handle, -16) | 0x40000000));
SetParent(Handle, _PreviewHandle);
Rectangle parentRectangle;
GetClientRect(_PreviewHandle, out parentRectangle);
Size = parentRectangle.Size;
Location = new Point(0, 0);
}
Fixed the problem. Simple mistake :-)
The correct way to solve it... override CreateParams:
protected override CreateParams CreateParams
{
get
{
CreateParams createParams = base.CreateParams;
if (!DesignMode && _IsPreview)
{
createParams.Style |= 0x40000000;
}
return createParams;
}
}
Once apon a time trying to change the WS_CHILD style of a window after it was already created would just quietly fail. I think they changed that in current versions of windows, but to be sure, you should really be creating your preview form as a child window from the start.
I have a hunch that your window isn't ending up a child window of the preview.
you could try this.
SetParent(Handle, _PreviewHandle);
SetWindowLong(Handle, -16, new IntPtr(GetWindowLong(Handle, -16) | 0x40000000));
SetParent(Handle, _PreviewHandle);
SetParent after you change your window style to WS_CHILD.
Also, you may not have the WS_POPUP style on your form, but if you do, you want to remove it.
int style = GetWindowLong(Handle, -16);
style &= ~0x800000000;
style |= 0x40000000;
SetWindowLong(Handle, -16, new IntPtr(style));
What's happening here is that SetParent sets the parent of child windows, but it sets the owner of WS_POPUP and WS_OVERLAPPED windows.

Categories