Having a child form switch between MdiChild and Normal? - c#

I am currently trying the recreate the Docking Panel/forms of visual studio. I want to apply the same system to a different project to handle different window views, but first I want to recreate it.
So far I've been switching the MDiParent property between the MainForm and null, but haven't been able to recreate the smooth transition seen in Visual Studio. It's always janky and half the time I find out the MouseUP event I tie the switch to rarely works (I'm currently using the Location change event).
To move the form, I've been using this handy code block I found;
#region Form Movement
public const int WM_NCLBUTTONDOWN = 0xA1;
public const int HT_CAPTION = 0x2;
[System.Runtime.InteropServices.DllImportAttribute("user32.dll")]
public static extern int SendMessage(IntPtr hWnd, int Msg, int wParam, int lParam);
[System.Runtime.InteropServices.DllImportAttribute("user32.dll")]
public static extern bool ReleaseCapture();
private void MoveForm()
{
ReleaseCapture();
SendMessage(Handle, WM_NCLBUTTONDOWN, HT_CAPTION, 0);
}
#endregion
And to Handle the Dock/UnDock, I'm using this code;
private void DockToMain()
{
Point f = Form1.Main.PointToClient(this.Location);
Point newP = new Point(f.X - 25, f.Y - 51);
try { this.MdiParent = Form1.Main; } catch { }
this.Location = newP;
}
private void UnDockFromMain()
{
Point f = Form1.Main.PointToScreen(this.Location);
Point newP = new Point(f.X + 25, f.Y + 51);
try { this.MdiParent = null; } catch { }
this.Location = newP;
}
Is there a way to recreate the Dock Panel/Form in Winforms? Am I even on the right track here, or is there a better way I'm not seeing?

Just to Answer this, Will is Completely Right. I Somehow got it to kind of work, but it's far too glitchy to be of any use. Just Switch to WPF (it isn't too difficult to make the jump from WinForms) and use the Avalon dock control from xceedsoftware's " Extended WPF Toolkit"

Related

Window.Left is wrong when moving a maximized Window with Windows + Shift + Arrow Keys

I encountered a problem in WPF (C#), I need to position a popup on a certain point within the parent window.
I get the parent position with Application.Current.MainWindow.Left and Application.Current.MainWindow.Top, it works as long as I don't move the window from one monitor to the other with the Windows shortcut Windows + Shift + ◄/►. If I use the shortcut the properties stay the same as they were before moving the window.
The window has to be WindowState.Maximized, it works if it is not maximized.
I also tried using Window.GetWindow(this) instead of Application.Current.MainWindow, result is the same.
It seems as if the positionchanged event doesn't occur for the Application.Current.MainWindow and it doesn't update the Left and Top properties.
I didn't find anything on this on SO or Google.
A workaround, hint or solution are greatly appreciated.
Try to use this:
WindowInteropHelper windowInteropHelper = new WindowInteropHelper(Application.Current.MainWindow);
Screen screen = System.Windows.Forms.Screen.FromHandle(windowInteropHelper.Handle);
The Screen Bounds property provides the coordinates of the whole window and the WorkingArea the boundaries of the area without titlebar and docked windows.
You should use the win32 API to retrieve the information you want as the events exposed by .NET aren't enough to detect this scenario.
There are two things you have to do:
Listen to the WndProc message that corresponds to a window movement (full list here). The one we want is WM_MOVE and is equal to 0x0003. See this thread for details.
Be able to determine the real location of the Window even when it's in Maximized state, which we can do by using the GetWindowRect method. See this thread for details.
Here would be the assembled code that prints the top-left location of your Window when it is moved, including using the shortcut you describe.
public partial class MainWindow : Window {
HwndSource source;
const short WM_MOVE = 0x0003;
public MainWindow() {
InitializeComponent();
// Loaded event needed to make sure the window handle has been created.
Loaded += MainWindow_Loaded;
}
private void MainWindow_Loaded(object sender, RoutedEventArgs e) {
// Subscribe to win32 level events
source = HwndSource.FromHwnd(new WindowInteropHelper(this).Handle);
source.AddHook(new HwndSourceHook(WndProc));
}
private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) {
if (msg == WM_MOVE) {
Console.WriteLine("Window has moved!");
GetWindowRect(new HandleRef(this, new WindowInteropHelper(this).Handle), out RECT rect);
Console.WriteLine("New location is " + (rect.Left, rect.Top));
}
return IntPtr.Zero;
}
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool GetWindowRect(HandleRef hWnd, out RECT lpRect);
[StructLayout(LayoutKind.Sequential)]
public struct RECT {
public int Left;
public int Top;
public int Right;
public int Bottom;
}
}

Altering the Window Bounds in a WM_MOVING handler without triggering Aero Shake

I am having the Problem, that if I alter the LParam of a WM_MOVING message to keep my Form at a certain position, which is legal according to this , the Windows Aero Shake feature gets triggered and all other Windows minimize. The behaviour can be reproduced by creating a Windows Forms Project in Visual Studio and pasting the following code into the Form:
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace FormsTest
{
public partial class ShakeTest : Form
{
[StructLayout(LayoutKind.Sequential, Pack = 1, Size = 16)]
public struct RECT
{
public int left;
public int top;
public int right;
public int bottom;
}
public const int WM_MOVING = 0x0216;
public ShakeTest()
{
InitializeComponent();
}
protected override void WndProc(ref Message m)
{
switch (m.Msg)
{
case WM_MOVING:
{
RECT rec;
rec.bottom = 500;
rec.left = 100;
rec.top = 100;
rec.right = 500;
Marshal.StructureToPtr(rec, m.LParam, true);
m.Result = new IntPtr(1);
}
break;
}
base.WndProc(ref m);
}
}
}
If you now grab the Window's title bar and move the mouse around a bit, the Shake gesture should get triggered, even though the Window isn't moving at all.
I tested this on Windows 10 only so far.
So my question is, can i disable the Shake feature for a certain Window or Process? If not, can i prevent Windows from thinking that im shaking the Window any other Way?
Thanks!
Frankly, I don't know if that is possible to disable Aero Shake for a particular window. But I can suggest a workaround that will prevent the Aero Shake from triggering.
There's a possibility to direct all the mouse input to only one window (and this will also hide the mouse events from the Aero Shake handler). Check the SetCapture description at MSDN. Also we will need a GetCapture and ReleaseCapture functions.
There's one remark: once you have set the mouse capture to a window - you are responsible for handling all the mouse input. So to achieve the goal you'll need to implement your own handler that will move the window. Fortunately, it's not that difficult.
Here's a code sample with a dialog that changes its size when it gets moving and restores the size when the moving is finished. Aero Shake is also never triggered.
public partial class ShakeTest : Form
{
[DllImport("user32.dll")]
static extern IntPtr SetCapture(IntPtr hWnd);
[DllImport("user32.dll")]
static extern IntPtr GetCapture();
[DllImport("user32.dll")]
static extern bool ReleaseCapture();
public const int WM_LBUTTONUP = 0x0202;
public const int WM_MOUSEMOVE = 0x0200;
public const int WM_NCLBUTTONDOWN = 0x00A1;
public const int HTCAPTION = 2;
private Point _lastCursorPos;
private Size _origianlSize;
public ShakeTest()
{
InitializeComponent();
}
protected override void WndProc(ref Message m)
{
switch (m.Msg)
{
// We need to get the moment when the user clicked on a non-client area
case WM_NCLBUTTONDOWN:
// We are interested only in a click on a title bar
if ((int) m.WParam == HTCAPTION)
{
// Set the capture so all the mouse input will be handled by this window
SetCapture(Handle);
// Keep the current cursor position to use it during the moving.
_lastCursorPos = Cursor.Position;
// Keep the original window size.
_origianlSize = Size;
// And change the dialog size to whatever you want
Size = new Size(300, 300);
}
break;
// Once we got the capture, we need to handle mouse moving by ourself
case WM_MOUSEMOVE:
// Check that our window has the capture
if (GetCapture() == Handle)
{
// Change the position of a window
Left += Cursor.Position.X - _lastCursorPos.X;
Top += Cursor.Position.Y - _lastCursorPos.Y;
_lastCursorPos = Cursor.Position;
}
break;
// When the left mouse button is released - it's time to release the mouse capture
case WM_LBUTTONUP:
// Check that our window has the capture
if (GetCapture() == Handle)
{
// Release the mouse capture
ReleaseCapture();
// Restore the size
Size = _origianlSize;
}
break;
}
base.WndProc(ref m);
}
}

Windows screensaver in C# using WPF crashing during preview mode

I have just started to learn C# and am trying to write a screensaver. My App.xaml.cs contains the following code for when the /p argument is used:
else if (arg1 == "/p")
{
if (e.Args[1] == null)
{
System.Windows.Forms.MessageBox.Show("Invalid or missing window handle.");
System.Windows.Application.Current.Shutdown();
}
IntPtr previewHandle = new IntPtr(long.Parse(e.Args[1]));
System.Windows.Application.Current.Run(new MainWindow(previewHandle));
}
Then in my MainWindow.xaml.cs, this construct handles the preview call:
public MainWindow(IntPtr previewHandle)
{
App.Current.Properties["isPreviewMode"] = true;
App.Current.Properties["previewHandle"] = previewHandle;
InitializeComponent();
}
After this, it crashes. In my MainWindow.xaml I have Loaded="MainWindow_Loaded".
In MainWindows.xaml.cs this is MainWindow_Loaded:
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
if ((bool)App.Current.Properties["isPreviewMode"])
{
IntPtr myHandle = new WindowInteropHelper(this).Handle;
SetParent(myHandle, (IntPtr)App.Current.Properties["previewHandle"]);
SetWindowLong(myHandle, -16, new IntPtr(GetWindowLong(myHandle, -16) | 0x40000000));
Rectangle ParentRect;
GetClientRect((IntPtr)App.Current.Properties["previewHandle"], out ParentRect);
this.Top = ParentRect.X;
this.Left = ParentRect.Y;
this.Height = ParentRect.Height;
this.Width = ParentRect.Width;
}
ssimage.Source = new BitmapImage(new Uri("pack://application:,,,/Resources/EmbeddedImage.PNG"));
}
[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);
I have other code in the App.xaml.cs to handle changing the images on a timer and other code in MainWindow.xaml.cs to handle mouse movement, clicks and keypresses. Everything works fine when running the screensaver normally. It is just the preview that fails. What am I doing wrong?
To my knowledge, WPF does not allow you to retarget the window handle, so performing screensaver previews in WPF using your technique is impossible.
However, there is a workaround: if you configure WPF to render to a bitmap target (see RenderTargetBitmap), you could then blit that bitmap onto the desired destination window handle - but this would involve an unholy mix of GDI and WPF and probably have awful runtime performance; I doubt it would be worth the effort.
What I ended up doing was using the WPF windows to display when the screensaver runs normally in full screen with /s. I created a new regular windows form previewForm.cs with a picturebox to use for the preview. That works fine and there is no performance issues. I assume the picturebox is using GDI.
This is my modified App.xaml.cs for handling the /p argument:
else if (arg1 == "/p")
{
if (e.Args[1] == null)
{
System.Windows.Forms.MessageBox.Show("Invalid or missing window handle.");
System.Windows.Application.Current.Shutdown();
}
IntPtr previewHandle = new IntPtr(long.Parse(e.Args[1]));
pw = new previewForm(previewHandle);
GetImages();
pw.Show();
}
and my previewForm.cs construct to handle the preview:
public previewForm(IntPtr previewHandle)
{
InitializeComponent();
IntPtr myHandle = this.Handle;
SetParent(myHandle, previewHandle);
SetWindowLong(myHandle, -16, new IntPtr(GetWindowLong(myHandle, -16) | 0x40000000));
Rectangle ParentRect;
GetClientRect(previewHandle, out ParentRect);
this.Size = ParentRect.Size;
this.pictureBox1.Size = ParentRect.Size;
this.Location = new Point(0, 0);
}
So I used a mix of a WPF form and a regular windows form to accomplish this. And the performance is fine. Only uses like 14-18MB of RAM and practically no CPU.

Changing window transparency using P/Invoke

I'm having problems with changing a window's transparency.
What I'm trying to accomplish
I develop a WPF application, which has some interaction with another application (a game developed in Unity3D). My goal is to "integrate" the game into my WPF app to make it look like the game is a natural part of the WPF app. So when I move my main window, I use PInvoke to move the game's window, too, I use PInvoke to hide the window borders of the game window etc. This all works decently.
However, a problem arises when I try to hide the game without closing it. My first approach was to use PInvoke to set the game window as hidden:
[DllImport("user32.dll")]
static extern bool ShowWindow(IntPtr hwnd, int show);
const int SW_HIDE = 0;
const int SW_Show = 5;
ShowWindow(Handle, SW_HIDE);
Using this function call the window disappeares, but as a consequence, the CPU usage of the game process jumps from 1% to 20%. So far, I have no clue why that is. Maybe it's related to Unity, maybe not. (CPU goes back to normal when I show the window again, though).
My second approach was not to hide the window using the ShowWindow-call but to set the window's opacity to 0 (as suggested in this thread). On my setup, this works just fine (without the CPU going crazy), but on other setups, the window will stay visible.
Updating the graphics driver solves the issue sometimes, but not always. I have an old Vista machine where everything works, and I also tried it on newer Dell Laptops with Windows 10 installed, where it does not work. I don't have the issue on my Mac with Windows 10 and Boot Camp. So I have no clue what causes this problem.
Did anyone experience similiar issues? Is there another way to hide a window or does anyone know why CPU goes insane when using the ShowWindow()-method?
Any hint is much appreciated.
EDIT
Would be nice to know why this post is downvoted so much, but okay. I think especially the CPU usage thing is an interesting issue and I am terribly sorry if someone thinks it's obvious and I am a moron.
For those who are also interested, here is a minimal WPF reconstruction of my problem:
MainWindow.xaml
<Window x:Class="SampleWpfApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="1024" Width="1280"
KeyDown="MainWindow_OnKeyDown">
<Grid>
</Grid>
</Window>
MainWindow.xaml.cs
public partial class MainWindow : Window
{
const int SW_HIDE = 0;
const int SW_SHOW = 5;
private Process gameProcess;
private IntPtr gameWindowHandle;
private bool windowHidden = false;
public MainWindow()
{
InitializeComponent();
this.SizeChanged += OnSizeChanged;
}
private void OnSizeChanged(object sender, SizeChangedEventArgs e)
{
StartGame();
}
private void MainWindow_OnKeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Space)
{
if (windowHidden)
{
NativeMethods.ShowWindow(gameWindowHandle, SW_HIDE);
windowHidden = false;
}
else
{
NativeMethods.ShowWindow(gameWindowHandle, SW_SHOW);
windowHidden = true;
}
}
}
private void StartGame()
{
var mainWindowHandle = new WindowInteropHelper(this).Handle;
var processStartInfo = new ProcessStartInfo(#"Path\To\SampleUnityApp.exe");
processStartInfo.Arguments += "-parentHWND " + mainWindowHandle.ToInt32() + " " + Environment.CommandLine + " ";
processStartInfo.CreateNoWindow = true;
processStartInfo.UseShellExecute = false;
gameProcess = Process.Start(processStartInfo);
gameProcess.WaitForInputIdle();
NativeMethods.EnumChildWindows(mainWindowHandle,
(hwnd, lparam) =>
{
// Set the window handle of the game
gameWindowHandle = hwnd;
return 0;
}, IntPtr.Zero);
}
static class NativeMethods
{
[DllImport("user32.dll")]
internal static extern bool ShowWindow(IntPtr hwnd, int show);
[DllImport("user32.dll")]
internal static extern bool EnumChildWindows(IntPtr hwnd, WindowEnumProc func, IntPtr lParam);
internal delegate int WindowEnumProc(IntPtr hwnd, IntPtr lparam);
}
}
The SampleUnityApp I used is just a blank Unity project with a main camera. No other game object. No code I wrote. It's created with Unity 5.3.4f1 Personal.
Screenshot of TaskManager with window visible
Screenshot of TaskManager with window hidden

C# WinForms Controls Rendering

I have a WinForms application which uses four panels in one form to hold and show information, controls etc.. Those panels are hidden or shown depending on the button pressed on the form - I hope you get the idea :) The panels are transparent and the forms holds the background image.
Now to the problem - if the background of the form is an image the controls on a panel that changes it's state to shown need too much time too render - there is kind of a blink and you can see how the controls render one after another. Has anyone encountered this before?
ADDITIONAL INFO
the problem disappears when I fill the background with a solid color (not image!)
I already tried using different kinds of images (png, bmp, jpg, low res, small color palette etc. with no effect)
I really need the background image
I would really want to avoid converting to WPF - simply because I don't have too much time.
I will be grateful for any help.
add a panel on your form and Dock it to middle, Use your background image to this panel... and also try the following code
MainPanel.SuspendLayout();
panel1.Visible= true;
panel2.Visible= false;
MainPanel.ResumeLayout();
if your okay with win32 API,
solution 1)
[DllImport("user32.dll")]
public static extern bool LockWindowUpdate(IntPtr hWndLock);
on button click:
try
{
LockWindowUpdate(this.Handle);
//code here
}
finally
{
LockWindowUpdate(IntPtr.Zero);
}
solution 2) Use SendMessage() with WM_SETREDRAW (better one)
private const int WM_SETREDRAW = 0x000B;
private const int WM_USER = 0x400;
private const int EM_GETEVENTMASK = (WM_USER + 59);
private const int EM_SETEVENTMASK = (WM_USER + 69);
[DllImport("user32", CharSet = CharSet.Auto)]
private extern static IntPtr SendMessage(IntPtr hWnd, int msg, int wParam, IntPtr lParam);
IntPtr eventMask = IntPtr.Zero;
on button click:
try
{
// Stop redrawing:
SendMessage(panel1.Handle, WM_SETREDRAW, 0, IntPtr.Zero);
// Stop sending of events:
eventMask = SendMessage(panel1.Handle, EM_GETEVENTMASK, 0, IntPtr.Zero);
// code here
}
finally
{
// turn on events
SendMessage(panel1.Handle, EM_SETEVENTMASK, 0, eventMask);
// turn on redrawing
SendMessage(panel1.Handle, WM_SETREDRAW, 1, IntPtr.Zero);
}

Categories