I have a control in a WPF application that contains a text box and a submit button. The submit button is set as the "default" so that if the user presses Enter while the cursor is in the text box, the click handler for the button is run. The process kicked off by the click handler is lengthy, so I use a wait cursor coded like the following:
public class WaitCursor: IDisposable
{
private readonly System.Windows.Input.Cursor _oldCursor = null;
public WaitCursor()
{
_oldCursor = System.Windows.Input.Mouse.OverrideCursor;
System.Windows.Input.Mouse.OverrideCursor = System.Windows.Input.Cursors.Wait;
// *** 1
}
~WaitCursor()
=> Dispose(false);
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
public void Dispose(bool unused)
{
System.Windows.Input.Mouse.OverrideCursor = _oldCursor;
// *** 2
}
}
If I type in the text box, the mouse cursor disappears. This is standard behaviour on Windows that I have observed in many applications. However, if I type in the text box and then press Enter without moving the mouse, then the mouse cursor is not shown while the application is busy - even if the user is moving it around. Effectively, the mouse cursor becomes invisible whenever it's over my application's windows, and remains that way until the application ceases being busy. This is undesirable.
I tried adding System.Windows.Forms.Cursor.Show() at the position marked with // *** 1 in my code above. This solved the problem of the cursor not being shown. But it introduced a new problem, in that the cursor no longer gets automatically hidden when the user types into text boxes in the application thereafter (for the lifetime of the application). The documentation page on Cursor.Show() says that calls to the Show() and Hide() methods should be paired, so I tried adding System.Windows.Forms.Cursor.Hide() at the position marked with // *** 2. This fixed all observed issues.
But I am not comfortable with this solution, for 2 reasons:
I am using a combination of the facilities provided by System.Windows.Forms.Cursor and System.Windows.Input.Cursor. This feels like it must be incorrect.
I am not comfortable instructing the application to "hide" the cursor when I do not in fact want the cursor to be hidden, even though the observed behaviour is that I merely undo the effect of the earlier call to Show(). It seems like something that isn't the intent of the framework designers (it really looks like what they had in mind was that you would Hide() the cursor and later Show() it) and might therefore break unpredictably.
What's the correct/proper way of solving this problem? If there is an officially sanctioned way to do this then I want to do that.
What about moving (simulated from code behind) the mouse before you start your process?
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
KeyDown += _OnKeyDown;
}
[DllImport("User32.dll")]
private static extern bool SetCursorPos(int X, int Y);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool GetCursorPos(ref Win32Point pt);
[StructLayout(LayoutKind.Sequential)]
internal struct Win32Point
{
public Int32 X;
public Int32 Y;
};
public static Point GetMousePosition()
{
Win32Point w32Mouse = new Win32Point();
GetCursorPos(ref w32Mouse);
return new Point(w32Mouse.X, w32Mouse.Y);
}
private void _OnKeyDown(object sender, KeyEventArgs keyEventArgs)
{
if (keyEventArgs.Key == Key.Enter)
{
Point pos = GetMousePosition();
SetCursorPos((int)pos.X + 1, (int)pos.Y); //move 1 pixel
SetCursorPos((int)pos.X - 1, (int)pos.Y); //move back to original position
//start your process afterwards ..
}
}
My program is a CRM, I used Rad Ribbon Bar, so Many Buttons with images, RadGridView (which some columns contain images) and so many other controls which contain images. It's a mdi parent/child program.
In so many cases while loading a mdi child or working with some grid views the program will hang and give me this error:
OutOfMemoryException occurred in System.Drawing.dll
I tried GC.Collect() on certain parts but no success. For setting images there is no code! for example for setting an image for a button I used its properties in visual studio. I have set All other control images in this way using the properties panel in visual mode.
and These are some designer codes related to drawing:
btnCustomerList.Image = global::MyApp.Properties.Resources.CustomerList32;
gridViewCommandColumn1.Image = global::MyApp.Properties.Resources.ViewShop32;
and When The error comes after a while working with the app, it will appear in Program.cs and in the line Application.Run(new MainForm());:
static void Main()
{
AppDomain.CurrentDomain.SetData("APP_CONFIG_FILE", AppDomain.CurrentDomain.BaseDirectory + "\\Settings.config");
bool ok;
Mutex m = new Mutex(true, WindowsIdentity.GetCurrent().Name.ToString().Split('\\')[1] + "MyApp", out ok);
if (ok)
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
// The Error will cause HERE
Application.Run(new MainForm());
GC.KeepAlive(m);
}
else
Application.Exit();
}
MainForm is the mdi parent which contains Ribbon Bar.
and this is the Full stack trace:
at System.Drawing.Image.FromHbitmap(IntPtr hbitmap, IntPtr hpalette)
at System.Drawing.Image.FromHbitmap(IntPtr hbitmap)
at System.Drawing.Icon.ToBitmap()
at System.Windows.Forms.ThreadExceptionDialog..ctor(Exception t)
at System.Windows.Forms.Application.ThreadContext.OnThreadException(Exception t)
at System.Windows.Forms.Control.WndProcException(Exception e)
at System.Windows.Forms.Control.ControlNativeWindow.OnThreadException(Exception e)
at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
at System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW(MSG& msg)
at System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(IntPtr dwComponentID, Int32 reason, Int32 pvLoopData)
at System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(Int32 reason, ApplicationContext context)
at System.Windows.Forms.Application.ThreadContext.RunMessageLoop(Int32 reason, ApplicationContext context)
at System.Windows.Forms.Application.Run(Form mainForm)
at MyApp.Program.Main() in d:\\MyApp\\Application\\MyApp\\Program.cs:line 36"
UPADTED:
the code for calling mdi-children by clicking on ribbon bar buttons is here:
private void btnCustomerList_Click(object sender, EventArgs e)
{
OpenForm(new FormCustomerList(), "Customer List");
}
private void btnCustomerRelated_Click(object sender, EventArgs e)
{
OpenForm(new FormCustomerRelated(), "Customer Related");
}
and Here is OpenForm method:
private void OpenForm(Form formType, string Caption)
{
foreach (Form nform in Application.OpenForms)
{
if (nform.GetType() == formType.GetType())
{
nform.Activate();
return;
}
}
this.MdiChildren.OfType<Form>().ToList().ForEach(x => x.Dispose());
GC.Collect();
Form form = formType;
form.MdiParent = this;
form.Dock = DockStyle.Fill;
form.Show();
this.Text = Caption;
}
and in every mdi child's form constructor, after InitializeComponent(); I wrote GC.Collect(); also. But as told in comments, the GDI objects in task manager will increase and increase till 10000 objects and then application will crash.
UPADTED: THE MOST ISSUE
It seems I have found the part which cause the most GDI objects. In every form there are some controls like textboxes, drop down list etc. I have set some rules for them, for example if user enter a textbox, its back color should be yellow and after leave it should be white again. So there is a main method which I call in form load to literate through all controls and find the target ones and add for example enter and leave events with the defined rules. something Like This:
private void FormCustomerList_Load(object sender, EventArgs e)
{
ClassCRMControls.AddEventHandler(this);
}
and inside ClassCRMControls class:
public static void AddEventHandler(Control parent)
{
foreach (Control c in parent.Controls)
{
if (c.GetType() == typeof(RadTextBox))
{
c.Enter += new EventHandler(ClassCRMControls.EnterEvent);
c.Leave += new EventHandler(ClassCRMControls.LeaveEvent);
}
else
AddEventHandler(c);
}
}
private static void EnterEvent(object sender, EventArgs e)
{
(sender as RadTextBox).TextBoxElement.TextBoxItem.BackColor = Color.FromArgb(255, 251, 147);
}
private static void LeaveEvent(object sender, EventArgs e)
{
(sender as RadTextBox).TextBoxElement.TextBoxItem.ResetValue(LightVisualElement.BackColorProperty, ValueResetFlags.Local);
}
I have found the source of the problem and it was the custom animated cursor I used for grids and other controls too. I initialize it like this:
this.Cursor = ClassObjects.CreateAnimatedCursor("C:\\aniCur.ani"));
Since I loaded this cursor from the file every time I used it in any way, more and more GDI Objects got created.
So I declared a public static cursor the main() of the respective form like this:
public static Cursor animCur = ClassObjects.CreateAnimatedCursor("C:\\aniCur.ani"));
and then whenever I need to use this cursor I just reference this object public static cursor from the form.
this.Cursor = MainForm.animCur;
That's It :)
How did I find it? I just try to remove (commenting) some codes which I suspected them, then I checked GDI objects in task manager. After some testing it became apparent that the endless loading of new cursor objects was causing the problem.
There can be multiple reasons for OutOfMemoryExceptions. I have discussed 6 of them in another question.
In this case, after the comments and the edit, it became clear that GDI issues occur as well. You can detect these issues by showing an additional column in task manager:
GDIView is a far better application for GDI Leak analysis, because it also tells you the type of the GDI handle that got lost. It also has absolute and relative counters, so you can see how many of them get lost during a particular action.
The number of GDI handles can be configured in Registry. Do not use that as a permanent solution. Instead, with the additional info from GDIView, find the piece of code that leaks the GDI object.
When you run into the limit of GDI handles, the application typically starts looking bad: things are not painted any more and you get black rectangles in some places. However, this behavior is not necessary. In OP's case, black rectangles were not part of the description.
I'm trying to archive the blinking effect as shown in the image with my C# main form. I've Googled a lot and I've seen that it is possible with System.Windows.Shell and TaskbarItemInfo. This seems much more easy than downloading and importing those dlls.
I know how to make a new TaskbarItemInfo, but I don't know how I can connect it to the main form.
Any suggestions how I can do this with the System.Windows.Shell reference?
Cant See the image..are you talking about the windows 7 blinking effect
To make a new TaskbarItemInfo:
public TaskbarItemInfo TaskbarItemInfo { get; set; }
public Form1()
{
InitializeComponent();
this.TaskbarItemInfo = new TaskbarItemInfo();//
}
But I prefer this:
using System.Runtime.InteropServices;
[DllImport("user32.dll")]
public static extern int FlashWindow(IntPtr Hwnd, bool Revert);
// check if window is minimised
private void Form4_Resize(object sender, EventArgs e)
{
if (this.WindowState == FormWindowState.Minimized)
FlashWindow(this.Handle, false);
}
This should work..
Also
// Flash window until it recieves focus
FlashWindow.Flash(this);
// Flash window 5 times
FlashWindow.Flash(this, 5);
// Start Flashing "Indefinately"
FlashWindow.Start(this);
// Stop the "Indefinate" Flashing
FlashWindow.Stop(this);
I'm trying to write an application that senses when someone taps and holds something. I am using windows forms. I tried using the mouse down even but it doesn't appear to fire all the time. This is also going to be a multi touch application. I'm going to have two buttons , and the user can tap and hold one button, while they press on the other button. Or Just press one button. I'm not even sure how a windows form app can handle that.
All the examples inhave seen for a windows touch app use xaml. Is this really the only way to capture tap and hold ??
I'm essentially making an onscreen keyboard here, and I don't think that isnpossible WITHOUT windows forms. Correct me if I am wrong here.
Any help or guidance in this is greatly appreciated. Thanks.
If your program is running on Windows 8, you can use the WM_POINTER API to get the input you need. Override WndProc to capture the messages. You will have to do some P/Invoke to get it working, but it's not terribly hard. Here's some incomplete code to get you started, you'll need to add cases for up, down, and update events for each type of pointer you want to track. Keep track of the pointer IDs to process multi touch. To handle the press-and-hold you'll need to track the time yourself from WM_POINTERDOWN to WM_POINTERUP and act accordingly. Hope this helps.
public const int WM_POINTERDOWN = 0x0246;
public const int WM_POINTERUP = 0x0247;
public const int WM_POINTERUPDATE = 0x0245;
public enum POINTER_INPUT_TYPE : int
{
PT_POINTER = 0x00000001,
PT_TOUCH = 0x00000002,
PT_PEN = 0x00000003,
PT_MOUSE = 0x00000004
}
public static uint GET_POINTERID_WPARAM(uint wParam) { return wParam & 0xFFFF; }
[DllImport("User32.dll")]
public static extern bool GetPointerType(uint pPointerID, out POINTER_INPUT_TYPE pPointerType);
protected override void WndProc(ref Message m)
{
bool handled = false;
uint pointerID;
POINTER_INPUT_TYPE pointerType;
switch(m.Message)
{
case WM_POINTERDOWN:
pointerID = User32.GET_POINTERID_WPARAM((uint)m.WParam);
if (User32.GetPointerType(pointerID, out pointerType))
{
switch (pointerType)
{
case POINTER_INPUT_TYPE.PT_PEN:
// Stylus Down
handled = true;
break;
case POINTER_INPUT_TYPE.PT_TOUCH:
// Touch down
handled = true;
break;
}
}
break;
}
if (handled)
m.Result = (IntPtr)1;
base.WndProc(ref m);
}
This question has been around for a while and might benefit from a simple approach. You can simulate the "tap and hold" (or click and hold) by measuring the time between the MouseDown event and the Click event (which fires before MouseUp). If the time is greater than some value then you cancel the Click and (perhaps) fire your own TapAndHold event. I have created a test control that anyone can use to try this approach out. Just add a UserControl to your test app (I called mine TestTapAndHold) and then paste in the following:
public partial class TestTapAndHold : UserControl
{
private string showText = "Tap Me";
private DateTime mouseDown;
private const int holdTime = 500;
public TestTapAndHold()
{
InitializeComponent();
this.Paint += drawText;
}
public delegate void OnTapAndHold(EventArgs e);
public event OnTapAndHold TapAndHold;
private void drawText(object sender, PaintEventArgs e)
{
using (var drawBrush = new SolidBrush(Color.Black))
{
e.Graphics.DrawString(showText, Font, drawBrush, new Point(5,3));
}
}
protected override void OnClick(EventArgs e)
{
if (DateTime.Now.Subtract(mouseDown).Milliseconds >= holdTime)
{
showText = "Tap Hold";
TapAndHold?.Invoke(e);
} else
{
base.OnClick(e);
showText = "Tapped";
}
Invalidate();
}
private void TestTapAndHold_MouseDown(object sender, MouseEventArgs e)
{
mouseDown = DateTime.Now;
}
}
Build the app and then pop one of the test controls onto a form. You can then add an event handler to your form like:
private void testTapAndHold1_TapAndHold(EventArgs e)
{
MessageBox.Show("You tapped and Held");
}
This general approach enabled me to add "Tap and Hold" functionality to a Windows Forms app running on a Microsoft Surface 4
I'm using a Form to show notifications (it appears at the bottom right of the screen), but when I show this form it steals the focus from the main Form. Is there a way to show this "notification" form without stealing focus?
Hmmm, isn't simply overriding Form.ShowWithoutActivation enough?
protected override bool ShowWithoutActivation
{
get { return true; }
}
And if you don't want the user to click this notification window either, you can override CreateParams:
protected override CreateParams CreateParams
{
get
{
CreateParams baseParams = base.CreateParams;
const int WS_EX_NOACTIVATE = 0x08000000;
const int WS_EX_TOOLWINDOW = 0x00000080;
baseParams.ExStyle |= ( int )( WS_EX_NOACTIVATE | WS_EX_TOOLWINDOW );
return baseParams;
}
}
Stolen from PInvoke.net's ShowWindow method:
private const int SW_SHOWNOACTIVATE = 4;
private const int HWND_TOPMOST = -1;
private const uint SWP_NOACTIVATE = 0x0010;
[DllImport("user32.dll", EntryPoint = "SetWindowPos")]
static extern bool SetWindowPos(
int hWnd, // Window handle
int hWndInsertAfter, // Placement-order handle
int X, // Horizontal position
int Y, // Vertical position
int cx, // Width
int cy, // Height
uint uFlags); // Window positioning flags
[DllImport("user32.dll")]
static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
static void ShowInactiveTopmost(Form frm)
{
ShowWindow(frm.Handle, SW_SHOWNOACTIVATE);
SetWindowPos(frm.Handle.ToInt32(), HWND_TOPMOST,
frm.Left, frm.Top, frm.Width, frm.Height,
SWP_NOACTIVATE);
}
(Alex Lyman answered this, I'm just expanding it by directly pasting the code. Someone with edit rights can copy it over there and delete this for all I care ;) )
This is what worked for me. It provides TopMost but without focus-stealing.
protected override bool ShowWithoutActivation
{
get { return true; }
}
private const int WS_EX_TOPMOST = 0x00000008;
protected override CreateParams CreateParams
{
get
{
CreateParams createParams = base.CreateParams;
createParams.ExStyle |= WS_EX_TOPMOST;
return createParams;
}
}
Remember to omit setting TopMost in Visual Studio designer, or elsewhere.
This is stolen, err, borrowed, from here (click on Workarounds):
https://connect.microsoft.com/VisualStudio/feedback/details/401311/showwithoutactivation-is-not-supported-with-topmost
If you're willing to use Win32 P/Invoke, then you can use the ShowWindow method (the first code sample does exactly what you want).
Doing this seems like a hack, but it seems to work:
this.TopMost = true; // as a result the form gets thrown to the front
this.TopMost = false; // but we don't actually want our form to always be on top
Edit: Note, this merely raises an already created form without stealing focus.
The sample code from pinvoke.net in Alex Lyman/TheSoftwareJedi's answers will make the window a "topmost" window, meaning that you can't put it behind normal windows after it's popped up. Given Matias's description of what he wants to use this for, that could be what he wants. But if you want the user to be able to put your window behind other windows after you've popped it up, just use HWND_TOP (0) instead of HWND_TOPMOST (-1) in the sample.
In WPF you can solve it like this:
In the window put these attributes:
<Window
x:Class="myApplication.winNotification"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Notification Popup" Width="300" SizeToContent="Height"
WindowStyle="None" AllowsTransparency="True" Background="Transparent" ShowInTaskbar="False" Topmost="True" Focusable="False" ShowActivated="False" >
</Window>
The last one attribute is the one you need ShowActivated="False".
I have something similar, and I simply show the notification form and then do
this.Focus();
to bring the focus back on the main form.
Create and start the notification Form in a separate thread and reset the focus back to your main form after the Form opens. Have the notification Form provide an OnFormOpened event that is fired from the Form.Shown event. Something like this:
private void StartNotfication()
{
Thread th = new Thread(new ThreadStart(delegate
{
NotificationForm frm = new NotificationForm();
frm.OnFormOpen += NotificationOpened;
frm.ShowDialog();
}));
th.Name = "NotificationForm";
th.Start();
}
private void NotificationOpened()
{
this.Focus(); // Put focus back on the original calling Form
}
You can also keep a handle to your NotifcationForm object around so that it can be programmatically closed by the main Form (frm.Close()).
Some details are missing, but hopefully this will get you going in the right direction.
This works well.
See: OpenIcon - MSDN and SetForegroundWindow - MSDN
using System.Runtime.InteropServices;
[DllImport("user32.dll")]
static extern bool OpenIcon(IntPtr hWnd);
[DllImport("user32.dll")]
static extern bool SetForegroundWindow(IntPtr hWnd);
public static void ActivateInstance()
{
IntPtr hWnd = IntPtr hWnd = Process.GetCurrentProcess().MainWindowHandle;
// Restore the program.
bool result = OpenIcon(hWnd);
// Activate the application.
result = SetForegroundWindow(hWnd);
// End the current instance of the application.
//System.Environment.Exit(0);
}
You might want to consider what kind of notification you would like to display.
If it's absolutely critical to let the user know about some event, using Messagebox.Show would be the recommended way, due to its nature to block any other events to the main window, until the user confirms it. Be aware of pop-up blindness, though.
If it's less than critical, you might want to use an alternative way to display notifications, such as a toolbar on the bottom of the window. You wrote, that you display notifications on the bottom-right of the screen - the standard way to do this would be using a balloon tip with the combination of a system tray icon.
You can handle it by logic alone too, although I have to admit that the suggestions above where you end up with a BringToFront method without actually stealing focus is the most elegant one.
Anyhow, I ran into this and solved it by using a DateTime property to not allow further BringToFront calls if calls were made already recently.
Assume a core class, 'Core', which handles for example three forms, 'Form1, 2, and 3'. Each form needs a DateTime property and an Activate event that call Core to bring windows to front:
internal static DateTime LastBringToFrontTime { get; set; }
private void Form1_Activated(object sender, EventArgs e)
{
var eventTime = DateTime.Now;
if ((eventTime - LastBringToFrontTime).TotalMilliseconds > 500)
Core.BringAllToFront(this);
LastBringToFrontTime = eventTime;
}
And then create the work in the Core Class:
internal static void BringAllToFront(Form inForm)
{
Form1.BringToFront();
Form2.BringToFront();
Form3.BringToFront();
inForm.Focus();
}
On a side note, if you want to restore a minimized window to its original state (not maximized), use:
inForm.WindowState = FormWindowState.Normal;
Again, I know this is just a patch solution in the lack of a BringToFrontWithoutFocus. It is meant as a suggestion if you want to avoid the DLL file.
I don't know if this is considered as necro-posting, but this is what I did since I couln't get it working with user32's "ShowWindow" and "SetWindowPos" methods. And no, overriding "ShowWithoutActivation" doesn't work in this case since the new window should be always-on-top.
Anyway, I created a helper method that takes a form as parameter; when called, it shows the form, brings it to the front and makes it TopMost without stealing the focus of the current window (apparently it does, but the user won't notice).
[DllImport("user32.dll")]
static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll")]
static extern IntPtr SetForegroundWindow(IntPtr hWnd);
public static void ShowTopmostNoFocus(Form f)
{
IntPtr activeWin = GetForegroundWindow();
f.Show();
f.BringToFront();
f.TopMost = true;
if (activeWin.ToInt32() > 0)
{
SetForegroundWindow(activeWin);
}
}
I know it may sound stupid, but this worked:
this.TopMost = true;
this.TopMost = false;
this.TopMost = true;
this.SendToBack();
I needed to do this with my window TopMost. I implemented the PInvoke method above but found that my Load event wasn't getting called like Talha above. I finally succeeded. Maybe this will help someone. Here is my solution:
form.Visible = false;
form.TopMost = false;
ShowWindow(form.Handle, ShowNoActivate);
SetWindowPos(form.Handle, HWND_TOPMOST,
form.Left, form.Top, form.Width, form.Height,
NoActivate);
form.Visible = true; //So that Load event happens
You don't need to make it anywhere near as complicated.
a = new Assign_Stock();
a.MdiParent = this.ParentForm;
a.Visible = false; //hide for a bit.
a.Show(); //show the form. Invisible form now at the top.
this.Focus(); //focus on this form. make old form come to the top.
a.Visible = true; //make other form visible now. Behind the main form.
Github Sample
Form.ShowWithoutActivation Property
Add this in your child form class
protected override bool ShowWithoutActivation
{
get { return true; }
}
Working Code
Form2
public partial class Form2 : Form
{
Form3 c;
public Form2()
{
InitializeComponent();
c = new Form3();
}
private void textchanged(object sender, EventArgs e)
{
c.ResetText(textBox1.Text.ToString());
c.Location = new Point(this.Location.X+150, this.Location.Y);
c .Show();
//removethis
//if mdiparent 2 add this.focus() after show form
c.MdiParent = this.MdiParent;
c.ResetText(textBox1.Text.ToString());
c.Location = new Point(this.Location.X+150, this.Location.Y);
c .Show();
this.Focus();
////-----------------
}
}
Form3
public partial class Form3 : Form
{
public Form3()
{
InitializeComponent();
//ShowWithoutActivation = false;
}
protected override bool ShowWithoutActivation
{
get { return true; }
}
internal void ResetText(string toString)
{
label2.Text = toString;
}
}
When you create a new form using
Form f = new Form();
f.ShowDialog();
it steals focus because your code can't continue executing on the main form until this form is closed.
The exception is by using threading to create a new form then Form.Show(). Make sure the thread is globally visible though, because if you declare it within a function, as soon as your function exits, your thread will end and the form will disappear.
Figured it out: window.WindowState = WindowState.Minimized;.