WPF C# MoveWindow issue under Windows 11 - c#

I have an application with at least 2 separate windows.
The secondary window is too far from the PC to use the mouse, so I have a method that temporarily brings that specific window to the current main display, changes are done, then the window is sent back.
This worked well under Windows 10, however under Windows 11, the window seems to disappear and is nowhere to be seen during the initial call. It can however be sent back (from wherever it was hiding) to the secondary monitor.
Here is some code to position the window (normal MoveWindow):
// Position is assigned in the constructor of the second window
public System.Drawing.Rectangle Position { get; set; }
[DllImport("user32.dll", SetLastError = true)]
private static extern bool MoveWindow(IntPtr hWnd, int X, int Y, int nWidth, int nHeight, bool bRepaint);
protected override void OnSourceInitialized(EventArgs e)
{
base.OnSourceInitialized(e);
MoveW();
}
public void MoveW()
{
WindowInteropHelper wih = new(this);
IntPtr hWnd = wih.Handle;
if (!Position.IsEmpty)
{
_ = MoveWindow(hWnd, Position.Left, Position.Top, Position.Width, Position.Height, false);
}
}
and here is how I am bringing the window to the current display (work perfect Win10):
// Getting the coordinates of the MainWindow
var screen = System.Windows.Forms.Screen.FromHandle(new WindowInteropHelper(App.Current.MainWindow).Handle);
System.Drawing.Rectangle rect = screen.WorkingArea;
// Simply passing them to second window needing to be moved
if (!rect.IsEmpty)
{
var wih = new WindowInteropHelper(this);
IntPtr hWnd = wih.Handle;
MoveWindow(hWnd, rect.Left, rect.Top, rect.Width, rect.Height, false);
}
Here is the link for MoveWindow
I have created a small GitHub project to illustrate the issue. If you have 2 screens and win10 and 11, get it from here.
Any suggestions?

As far as I noticed, on Windows 11, WindowState.Maximized seems to prevent the window from being shown after its location is changed by MoveWindow function.
So the workround is returing to WindowState.Normal before calling MoveWindow. It would be something like below.
WindowState state = this.WindowState;
try
{
this.WindowState = WindowState.Normal;
MoveWindow(hWnd, rect.Left, rect.Top, rect.Width, rect.Height, false);
}
finally
{
this.WindowState = state;
}

Related

Having a child form switch between MdiChild and Normal?

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"

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.

Use Chrome or Firefox in Winforms application ?

Is there a way to use Firefox or Chrome (whichever is installed) in a .NET application? I am not thinking about putting gecko or webkit engine in my application but using the browser installed on the system instead (just like the WebBrowser control uses IE). I heard that it is possible via ActiveX controls but didn't find any more info about it.
Well you could use user32.dll to set the parent window of the specified child window (here firefox or chrome).
This is what the result look likes:
First of all I have two small problems and 1 bigger one:
As you can see firefox is not maximized, therefore some content is missing (still looking for a way to fix that [help appriciated])
Because you have to start the process first and then set the parent of the window, Firefox is running outside of your application for a small amount of time.
The biggest problem: The program your are trying to "bind" to your application mustn't run when running your application, because it cannot set the parent of firefox to your program
MSDN for explanation of the methods: http://msdn.microsoft.com/en-us/library/windows/desktop/ff468919(v=vs.85).aspx
[DllImport("user32.dll", SetLastError = true)]
private static extern bool MoveWindow(IntPtr hwnd, int x, int y, int nWidth, int nHeight, bool repaint);
//hwnd: A handle to the window | x: The new position of the left side of the window. | y: The new position of the top of the window.
//nWidth: The new width of the window. | nHeight: The new height of the window.
//repaint: Indicates whether the window is to be repainted. If this parameter is TRUE, the window receives a message. If the parameter is FALSE, no repainting of any kind occurs.
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
Process ffprocess = new Process();
private void openProgramAndSetParent()
{
string str = #"C:\Program Files\Mozilla Firefox\firefox.exe"; //Enter full path to Firefox or Chrome
ffprocess.StartInfo.FileName = str;
ffprocess.StartInfo.WindowStyle = ProcessWindowStyle.Maximized;
ffprocess.Start();
IntPtr ptr = IntPtr.Zero;
while ((ptr = ffprocess.MainWindowHandle) == IntPtr.Zero)
{
Application.DoEvents();
}
Thread.Sleep(1000);
SetParent(ffprocess.MainWindowHandle, panel1.Handle);
MoveWindow(ffprocess.MainWindowHandle, 0, 0, this.Width, this.Height -100, true);
}
private void Form1_Resize(object sender, EventArgs e)
{
try
{
MoveWindow(ffprocess.MainWindowHandle, 0, 0, this.Width, this.Height, true);
}
catch (Exception)
{}
}

Windows forms MenuStrip DropDown inside a desktop application bar

This is a duplicate of a question I posted at Windows Dev Center. Still awaiting replies, so I thought I would try it here. My apologies if the formatting goes awry.
This is currently being written and debugged using Windows 7 Professional, SP1.
The application is located at the top of the desktop, and the working area is appropriately resized via a hook into the SystemParametersInfo function. The MenuStrip appears as it should, with the exception that any dropdown from the MenuStrip shows as detached from the MenuStrip itself (as if it is being drawn on the new working area, as opposed to the form containing the MenuStrip). For example:
Application TopLevel: true
Application Height: 150
Application Location: 0,0 on Desktop (prior to working area's resize)
MenuStrip Height: 25
MenuStrip Location: 0,0 inside Parent Form
MenuStrip DropDown Location: x,2 (where x is a valid and acceptable value) this is being drawn on the resized working area (i.e. beneath the form)
I have attempted correcting this with a custom Renderer to no present avail. I tried to override WndProc (as follows) so as to see what exactly was occurring, but that resulted in a stackoverflow halfway through drawing the application.
protected override void WndProc(ref Message m)
{
if (mainForm.Visible)
{
MessageBox.Show("ID: " + m.Msg.ToString() + "\n" + m.ToString());
}
base.WndProc(ref m);
}
I suspect I've run this into the ground by now, but if you require any further explanation, just let me know. Just hoping someone can point me in the appropriate direction as to where I should look.
Hopefully this will answer the question regarding why I used SystemParametersInfo:
#region TEMPORARY FIX TO ACHIEVE APPBAR
public RECT normalWorkingRECT = new RECT();
public RECT newWorkingRECT = new RECT();
public struct RECT
{
public int left;
public int top;
public int right;
public int bottom;
};
[DllImport("user32.dll", EntryPoint="SystemParametersInfo")]
public static extern bool SystemParametersInfo(uint uiAction, uint uiParam, ref RECT pvParam, uint fWinIni);
//call after scale is set
private void setAppBar()
{
//make the parent = the desktop
if (!this.GetTopLevel())
{
this.SetTopLevel(true);
}
this.Width = SystemInformation.PrimaryMonitorSize.Width;
//set old Working Area
SystemParametersInfo(0x0030, 0, ref normalWorkingRECT, 0);
//change work area based on size of main form
newWorkingRECT.left = normalWorkingRECT.left;
newWorkingRECT.top = normalWorkingRECT.top + this.DesktopBounds.Height + 4;
newWorkingRECT.right = normalWorkingRECT.right;
newWorkingRECT.bottom = normalWorkingRECT.bottom;
SystemParametersInfo(0x002F, 0, ref newWorkingRECT, 0x0002);
}
//called on close
private void unsetAppBar()
{
//get current work area to compare
RECT testRECT = new RECT();
SystemParametersInfo(0x0030, 0, ref testRECT, 0);
//if no change, resize to normal working rect
if (newWorkingRECT.top == testRECT.top &&
newWorkingRECT.bottom == testRECT.bottom &&
newWorkingRECT.left == testRECT.left &&
newWorkingRECT.right == testRECT.right)
{
SystemParametersInfo(0x002F, 0, ref normalWorkingRECT, 0x0002);
}
//if there is a change, resize to current working rect - this.height
else
{
testRECT.top -= this.DesktopBounds.Height + 4;
SystemParametersInfo(0x002F, 0, ref testRECT, 0x0002);
}
}
#endregion
EDIT: Added image as requested and code to show reason for SystemParametersInfo.

Drawing C# graphics without using Windows Forms

Could someone provide an example for drawing graphics without using Windows Forms? I have an app that doesn't have a console window or Windows form, but i need to draw some basic graphics (lines and rectangles etc.)
Hope that makes sense.
This should give you a good start:
[TestFixture]
public class DesktopDrawingTests {
private const int DCX_WINDOW = 0x00000001;
private const int DCX_CACHE = 0x00000002;
private const int DCX_LOCKWINDOWUPDATE = 0x00000400;
[DllImport("user32.dll")]
private static extern IntPtr GetDesktopWindow();
[DllImport("user32.dll")]
private static extern IntPtr GetDCEx(IntPtr hwnd, IntPtr hrgn, uint flags);
[Test]
public void TestDrawingOnDesktop() {
IntPtr hdc = GetDCEx(GetDesktopWindow(),
IntPtr.Zero,
DCX_WINDOW | DCX_CACHE | DCX_LOCKWINDOWUPDATE);
using (Graphics g = Graphics.FromHdc(hdc)) {
g.FillEllipse(Brushes.Red, 0, 0, 400, 400);
}
}
}
Something like this?
using System.Drawing;
Bitmap bmp = new Bitmap(200, 100);
Graphics g = Graphics.FromImage(bmp);
g.DrawLine(Pens.Black, 10, 10, 180, 80);
The question is a little unfocused. Specifically - where do you want to draw the lines and rectangles? Generally speaking, you need a drawing surface, usually provided by a windows form.
Where does the need to avoid windows forms come from?
Are you using another kind of window?
For a windows form you could use code similar to this:
namespace WindowsFormsApplication1 {
public partial class Form1 : Form {
public Form1() {
InitializeComponent();
}
protected override void OnPaint(PaintEventArgs e) {
base.OnPaint(e);
e.Graphics.DrawLine(new Pen(Color.DarkGreen), 1,1, 3, 20 );
e.Graphics.DrawRectangle(new Pen(Color.Black), 10, 10, 20, 32 );
}
}
}
You can generally do this with any object that lets you get a handle for a "Graphics" object (like a printer).
Right, the way i've done it is with a windows form, but make the background transparent, and then get rid of all the borders...
Thanks for the replys anyway..
J

Categories