This is somewhat of a mundane question but it seems to me there is no in-built method for it in WPF. There only seems to be the WindowState property which being an enum does not help since i cannot tell whether the Window was in the Normal or Maximized state before being minimized.
When clicking the taskbar icon the window is being restored just as expected, assuming its prior state, but i cannot seem to find any defined method which does that.
So i have been wondering if i am just missing something or if i need to use some custom interaction logic.
(I'll post my current solution as answer)
Not sure this will work for everybody, but I ran into this today and someone on the team suggested "have you tried Normal"?
Turns out he was right. The following seems to nicely restore your window:
if (myWindow.WindowState == WindowState.Minimized)
myWindow.WindowState = WindowState.Normal;
That works just fine, restoring the window to Maximized if needed. It seems critical to check for the minimized state first as calling WindowState.Normal a second time will "restore" your window to its non-maximized state.
SystemCommands class has a static method called RestoreWindow that restores the window to previous state.
SystemCommands.RestoreWindow(this); // this being the current window
[Note : SystemCommands class is part of .NET 4.5+ (MSDN Ref) for projects that target to earlier versions of Framework can use the WPF Shell extension (MSDN Ref)]
WPF's point of view is that this is an OS feature. If you want to mess around with OS features you might have to get your hands dirty. Luckily they have provided us with the tools to do so. Here is a UN-minimize method that takes a WPF window and uses WIN32 to accomplish the effect without recording any state:
public static class Win32
{
public static void Unminimize(Window window)
{
var hwnd = (HwndSource.FromVisual(window) as HwndSource).Handle;
ShowWindow(hwnd, ShowWindowCommands.Restore);
}
[DllImport("user32.dll")]
private static extern bool ShowWindow(IntPtr hWnd, ShowWindowCommands nCmdShow);
private enum ShowWindowCommands : int
{
/// <summary>
/// Activates and displays the window. If the window is minimized or
/// maximized, the system restores it to its original size and position.
/// An application should specify this flag when restoring a minimized window.
/// </summary>
Restore = 9,
}
}
For some reason,
WindowState = WindowState.Normal;
didn't work for me.
So I used following code & it worked..
Show();
WindowState = WindowState.Normal;
Here is how i get it to restore right now: I handle the StateChanged event to keep track of the last state that was not Minimized
WindowState _lastNonMinimizedState = WindowState.Maximized;
private void Window_StateChanged(object sender, EventArgs e)
{
if (this.WindowState != System.Windows.WindowState.Minimized)
{
_lastNonMinimizedState = WindowState;
}
}
To restore i then have to set that WindowState respectively:
this.WindowState = _lastNonMinimizedState;
Hmmm, the accepted answer did not work for me. The "maximized" window, when recalled from the task bar would end up centering itself (displaying in its Normal size, even though its state is Maximized) on the screen and things like dragging the window by its title bar ended up not working. Eventually (pretty much by trial-and-error), I figured out how to do it. Thanks to #H.B. and #Eric Liprandi for guiding me to the answer! Code follows:
private bool windowIsMinimized = false;
private WindowState lastNonMinimizedState = WindowState.Normal;
private void Window_StateChanged(object sender, EventArgs e)
{
if (this.windowIsMinimized)
{
this.windowIsMinimized = false;
this.WindowState = WindowState.Normal;
this.WindowState = this.lastNonMinimizedState;
}
else if (this.WindowState == WindowState.Minimized)
{
this.windowIsMinimized = true;
}
}
private void Window_MinimizeButtonClicked(object sender, MouseButtonEventArgs e)
{
this.lastNonMinimizedState = this.WindowState;
this.WindowState = WindowState.Minimized;
this.windowIsMinimized = true;
}
private void Window_MaximizeRestoreButtonClicked(object sender, MouseButtonEventArgs e)
{
if (this.WindowState == WindowState.Normal)
{
this.WindowState = WindowState.Maximized;
}
else
{
this.WindowState = WindowState.Normal;
}
this.lastNonMinimizedState = this.WindowState;
}
In native Windows you can restore your window to a previous state with ShowWindow(SW_RESTORE):
Activates and displays the window. If the window is minimized or maximized, the system restores it to its original size and position. An application should specify this flag when restoring a minimized window.
There's surely .Net counterpart to that.
Related
I'm creating a new window in On_Click method. First I tried this;
public partial class MainWindow : Window
{
CustomerOperations customerOperationsWindow;
public MainWindow()
{
customerOperationsWindow = new CustomerOperations();
InitializeComponent();
}
private void btnCustomer_Click(object sender, RoutedEventArgs e)
{
customerOperationsWindow.Owner = this;
customerOperationsWindow.Show();
}
}
It's not working so I started creating the window instance every time the user clicks on the Customers button. And I used the following codes.
private void btnCustomer_Click(object sender, RoutedEventArgs e)
{
CustomerOperations customerOperationsWindow = new CustomerOperations();
customerOperationsWindow.Owner = this;
customerOperationsWindow.Show();
}
In the new window, If user clicks to Main button, I want to navigate to main window.
private void btnMain_Click(object sender, RoutedEventArgs e)
{
this.Close();
this.Owner.Show();
}
First question: Does this.Close() releases the window instance?
Second question: Is this usage correct?
What do you think is the best practice?
Thank you all.
Window.Close() will dispose all resources allocated by the instance. That's why you cannot show it again once it was closed.
If you want to reuse the same Window instance, you should cancel the closing procedure to prevent disposal of internal resources and collapse the Window instead (by setting Window.Visibility to Visibility.Collapsed - Visibility.Collapsed is also the default value of an instantiated Window before Window.Show() is called).
Alternatively hide the Window by calling Window.Hide() (which will set the Visibility to Visibility.Hidden) instead of Window.Close().
Calling Window.Show will also set the window's visibility to Visibility.Visible.
As a matter of fact, showing a Window by setting Window.Visibility is the asynchronous version of Window.Show().
Generally, you switch between Window instances by using the Window.Activate method. Calling Window.Show on a Window that is currently showing/visible, does nothing.
public partial class MainWindow : Window
{
CustomerOperations CustomerOperationsWindow { get; }
public MainWindow()
{
InitializeComponent();
this.CustomerOperationsWindow = new CustomerOperations();
// Consider to move this logic to CustomerOperations class,
// where you can override the OnClosing method instead of subscribing to the event
this.CustomerOperationsWindow.Closing += CollapseWindow_OnClosing;
}
// Cancel close to prevent disposal and collapse Window instead
private void CollapseWindow_OnClosing(object sender, CancelEventArgs e)
{
e.Cancel = true;
this.CustomerOperationsWindow.Visibility = Visibility.Collapsed;
this.CustomerOperationsWindow.Owner.Activate();
}
private void btnCustomer_Click(object sender, RoutedEventArgs e)
{
this.CustomerOperationsWindow.Owner = this;
// Calling Show will set the Visibility to Visibility.Visible
this.CustomerOperationsWindow.Show();
}
}
Creating a Window instance allocates unmanaged resources. If this happens very frequently, you will keep the garbage collector busy. From a performance point of view you may want to avoid it and prefer to reuse the same instance.
In a common scenario this is not necessary. But since Window exposes a Hide() method, you may consider to use it instead of Close().
If you want to switch to the parent window, you can use the code this.Owner.Activate(); and if you want to close the current window, first this.Owner.Activate(); and then this.Close();.
When you enter this.Close(), the compiler does not execute the following lines after reaching it. And when a sample window still exists there is no need to recreate it
private void btnMain_Click(object sender, RoutedEventArgs e)
{
this.Owner.Activate();
this.Close();
}
I have three windows. FirstWindow, SecondWindow and ThirdWindow. FirstWindow has button and click on this button opens the SecondWindow. Analogously, SecondWindow has button and click on this button opens the ThirdWindow. Owner property of the SecondWindow is set as FirstWindow and Owner property of the ThirdWindow is set as SecondWindow. The scenario discribing problem:
Open all windows in a row. It will be looked like this:
Then minimize all windows by click on corresponding icon at top right corner of ThirdWindow.
If you will try to maximize all windows by clicking on FirstLevelWindow or ThirdLevelWinow in taskbar - all will be ok, three windows will be maximized. But if you will click on SecondWindow you will see this:
How can I fix it, or it is just WPF bug? I can give archived expample project if it helps.
UPDATE
Minimize window - click "_" icon, left icon in iconbar of the window. All windows are modal, i.e it opens with ShowDialog() method, not with Show() method. So if you minimize third window - all the windows will be minimized.
Here the code if you don't want download project by link:
FirstWindow XAML:
<Button Click="OpenChildWindow"
Content="ChildWindow"/>
FirstWindow .cs:
private void OpenChildWindow(Object sender, RoutedEventArgs e)
{
var window = new SecondLevelWindow();
window.Owner = this;
window.ShowDialog();
}
SecondWindow XAML:
<Button Click="OpenChildWindow"
Content="ChildWindow"/>
SecondWindow .cs:
private void OpenChildWindow(Object sender, RoutedEventArgs e)
{
var window = new ThirdLevelWindow();
window.Owner = this;
window.ShowDialog();
}
ThirdWindow is empty window without any content.
Here link to example project
I've just found, that bug is not reproduced if property ResizeMode of ThirdWindow is set to "NoResize". Mb it will be usefull information.
Well, I admit I have no idea what is going on. Did you try to add a fourth window? This become even stranger: the second window bring back the third, but the fourth is still not back.
Anyway, If I had to manage this problem, I would keep a reference of my childWindow in each parent Window. This way on any interesting event (like activate on the second window in your example) I could manage the state of my child as required (WindowState.Normal in your case).
It could be something like that: in xaml of secondWindow:
Activated="SecondLevelWindow_OnActivated"
And then in code behind:
private ThirdLevelWindow _window;
public SecondLevelWindow()
{
InitializeComponent();
}
private void OpenChildWindow(Object sender, RoutedEventArgs e)
{
_window = new ThirdLevelWindow ();
_window.Owner = this;
_window.ShowDialog();
}
public void SecondLevelWindow_OnActivated(object sender, EventArgs e)
{
if (_window != null)
{
_window.WindowState = WindowState.Normal;
}
}
This is a start, but you could also inspect your current state to define the state of your child.
Hope it helps.
I'm using RadWindow for WPF in my application.
I called RadWindow.ShowDialog() to show the RadWindow.
But when I minimize this RadWindow, it will disappear and then I can not restore it because it is not contained in Taskbar of Windows 7.
I solved this by hiding the Minimized button on RadWindow, but then I encountered other case of this bug. that when I press "Window + D" keys to minimize all Windows, this Radwindow also disappear.
Please help me to fix it,
Many thanks,
T&T Group
If I understand you correctly, you need to set the Owner property of your dialog window to the application's main window.
RadWindow doesn't contain the property "ShowOnTaskBar" like Window have.
you can do something like this code below to work-around :
Window _window;
public MainView()
{
InitializeComponent();
Loaded += MainView_Loaded;
}
void MainView_Loaded(object sender, RoutedEventArgs e)
{
_window = this.ParentOfType<Window>();
if (_window != null)
{
_window.ShowInTaskbar = true;
_window.Title = this.Header.ToString();
}
}
I put several ComboBoxes on a XAML window. When I expand any of them, the DropDown part appears on the upper left corner of the screen.
I use Visual Studio 2008 C# Express. I don't remember this phenomenon when I used Visual Studio 2008 (Trial Version), though I use the same FrameWork (3.5).
It seems to be a bug.
Workaround:
Use Window.Show() instead with a custom logic to simulate the ShowDialog() behavior.
This appears to be a bug in WPF. In my case, I was trying to open a window in the Loaded event of another window. To get around this, I set a timer up to fire, then used a delegate to open the window (cannot open the window in a timer event because the calling thread that opens a window must be STA).
Edit - timer isn't necessary - didn't see the answer above just queue it on the dispatcher...
private delegate void DelegateOpenWindow();
private DelegateOpenWindow m_DelegateOpenWindow;
private Timer loginTimer = new Timer(200);
private void MainWindow1_Loaded(object sender, RoutedEventArgs e)
{
// create delegate used for asynchronous call
m_DelegateOpenWindow= new DelegateOpenWindow(this.OpenWindow);
// start a timer to fire off the open window.
loginTimer.Elapsed += loginTimer_Elapsed;
loginTimer.Enabled = true;
}
void loginTimer_Elapsed(object sender, ElapsedEventArgs e)
{
loginTimer.Enabled = false;
this.Dispatcher.BeginInvoke(m_DelegateOpenWindow);
}
void OpenWindow()
{
MyWindow w = new MyWindow();
w.Owner = this;
w.ShowDialog();
}
I started observing this (and other strange behavioral quirks) yesterday when I tried to "tweak" window sizes, shapes, colors, and invoke a log-on dialog from the Window.Loaded event handler. I had been doing this just fine in each of a dozen+ individual "MVVM" pattern apps. Yesterday, I decided to move this from each app's code behind into a consolidated code-behind base class, since the pre-processing had become common in all those apps. When I did, the drop-downs in two ComboBoxes in the log-in dialog suddenly appeared in the upper left corner of my screen. I seem to have "solved" it by using the following technique (your mileage may vary):
protected void WindowBaseLoadedHandler(object sender, RoutedEventArgs e)
{
...non-essential lines of code removed...
if (DataContext != null)
{
Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() =>
{
/*----------------------------------------------------------------------
* Do we have a View Model? If so, perform standard VM Initialization...
*---------------------------------------------------------------------*/
this.IsEnabled = false;
LoginDlg loginDlg = new LoginDlg();
loginDlg.ShowDialog();
if (!loginDlg.Success)
{
/*-----------------------------------
* Log on failed -- terminate app...
*----------------------------------*/
...termination logic removed...
}
this.IsEnabled = true;
}));
}
WindowBaseLoadedHandler is the Loaded event handler. LoginDlg is a WPF app with a dialog containing two ComboBoxes.
Recap: After I consolidated the code into the Loaded event handler of the base class the ComboBox's drop down lists appeared in the upper left corner of my screen. Once I wrapped the logic into the Dispatcher.BeginInvoke call, the appropriate ComboBox behavior returned with lists below the current item.
I suspect WPF needs the application to return from the Loaded event to complete the layout system's initialization. That doesn't fully explain why it worked before, but I'll have to queue up my desire to hunt that "why" down for some rainy day in the future and celebrate overcoming the latest obstacle for today.
In any event, I hope someone finds this of use.
I'm using the latest .Net 4.5 and WPF framework and I still have this problem. One thing I noticed is that it only happen when there's an attached debugger. When the debugger is not attached, everything works fine.
I had the same problem on Visual Studio 2019.
Using window.Show() can help but it can ruin your design.
The solution is to open the window asynchronously.
var yourDialog= new YourDialog();
yourDialog.Owner = this;
TaskCompletionSource<bool?> completion = new TaskCompletionSource<bool?>();
this.Dispatcher.BeginInvoke(new Action(() =>
completion.SetResult(yourDialog.ShowDialog())));
bool? result = await completion.Task;
You can also create a more elegant solution by making the extension method:
public static class AsyncWindowExtension
{
public static Task<bool?> ShowDialogAsync(this Window self)
{
if (self == null) throw new ArgumentNullException("self");
TaskCompletionSource<bool?> completion = new TaskCompletionSource<bool?>();
self.Dispatcher.BeginInvoke(new Action(() => completion.SetResult(self.ShowDialog())));
return completion.Task;
}
}
And you can use it like this:
await dlgReview.ShowDialogAsync();
It’s a bug in WPF (not the only one, I'm afraid). It happened when I opened another window in the Loaded Event, something like:
private void Window_Loaded(object sender, RoutedEventArgs e)
{
Window selectionWindow = new SelectionWindow();
bool? result = selectionWindow.ShowDialog();
if (result == true)
RecordChanged();
}
I already found a workabout.
I have a WPF Window, and I want to determine when a user finishes moving a Window around on the desktop. I hooked up to the LocationChanged event and that's fine, but I can't figure out how to determine when the user stops moving the window (by releasing the left mouse button).
There's no event to help me determine that, something like a LocationChangedEnded event. I tried hooking up to MouseLeftButtonUp but that event is never fired.
Anyone has any ideas?
Two possible approaches would be:
You don't really know when the mouse button is raised. Instead, you wait for the window to stop sending those move events. Set up a short lived timer that starts ticking every time you receive a window move event. Reset the timer if it's already on. When you receive the timer event, e.g. after a few hundred millisecs, you could assume the user stopped moving the window. Even with a high resolution mouse, when holding down the left mouse button and trying to stay still, the jitter will keep sending move events. This approach is documented here.
Attempt to capture mouse notifications from the non-client area of the window. You could set up a window message hook to capture window messages. Once the first window move event is seen, the hook could start looking for WM_NCLBUTTONUP events. This approach avoids the timer and the guessing. However, it makes assumptions about the ways Windows allows the user to position windows, and may fail in some cases, e.g. if the user moves the user with the keyboard only (Alt+Space, M, arrow keys).
You can listen for the WM_ENTERSIZEMOVE event, which should only fire when the move is started. While the user is dragging you may receive WM_MOVING and WM_MOVE events. The latter depends on the their system settings (e.g. the window moves as they drag, versus just dragging an outline). Finally, WM_EXITSIZEMOVE would indicate when they are done.
You want to get the WM_WINDOWPOSCHANGED message, add this to your Window class:
internal enum WM
{
WINDOWPOSCHANGING = 0x0047,
}
[StructLayout(LayoutKind.Sequential)]
internal struct WINDOWPOS
{
public IntPtr hwnd;
public IntPtr hwndInsertAfter;
public int x;
public int y;
public int cx;
public int cy;
public int flags;
}
private override void OnSourceInitialized(EventArgs ea)
{
HwndSource hwndSource = (HwndSource)HwndSource.FromVisual((Window)this);
hwndSource.AddHook(DragHook);
}
private static IntPtr DragHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handeled)
{
switch ((WM)msg)
{
case WM.WINDOWPOSCHANGED:
{
WINDOWPOS pos = (WINDOWPOS)Marshal.PtrToStructure(lParam, typeof(WINDOWPOS));
if ((pos.flags & (int)SWP.NOMOVE) != 0)
{
return IntPtr.Zero;
}
Window wnd = (Window)HwndSource.FromHwnd(hwnd).RootVisual;
if (wnd == null)
{
return IntPtr.Zero;
}
// ** do whatever you need here **
// the new window position is in the pos variable
// just note that those are in Win32 "screen coordinates" not WPF device independent pixels
}
break;
}
return IntPtr.Zero;
}
You may be interested in the answer that I've posted here:
How do you disable Aero Snap in an application?
The answer contains a reliable way to detect the start/end of window moves for the purpose of disabling Aero snap.
This solution is not perfect (kind of patch) but at least it can be useful to someone.
private void Window_Loaded(object sender, RoutedEventArgs e)
{
tmr.Elapsed += Tmr_Elapsed;
}
System.Timers.Timer tmr = new System.Timers.Timer(2000);
private void Tmr_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
tmr.Stop();
Application.Current.Dispatcher.Invoke(() =>
{
//Your code for location changed ended here
});
}
private void Window_LocationChanged(object sender, EventArgs e)
{
tmr.Stop();
tmr.Start();
}
Can change the interval as per your need. I hope it helps someone.