Handling System Shutdown in WPF - wpf

How can I override WndProc in WPF?
When my window close, I try to check if the file i'm using was modified, if so, I have to promt the user for "Do you want to save changes?" message, then close the file being used and the window.However, I cannot handle the case when user restarts/shutdown/logoff when my window is still open.I cannot override WndProc since I am developing using WPF.I have also tried using this sample MSDN code.This is what I did
private void loadedForm(object sender, RoutedEventArgs e)
{
HwndSource 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_QUERYENDSESION.)
{
OnWindowClose(this, new CancelEventArgs())
handled = true;
shutdown = true;
}
return IntPtr.Zero;
}
private void OnWindowClose(object sender, CancelEvetArgs e)
{
if (modified)
{
//show message box
//if result is yes/no
e.cancel = false;
//if cancel
e.cancel = true;
}
}
On the XAML file, I also used Closing = "OnWindowClose" however nothing happens when I click yes/no, my application does not close. and if I try to close it again using the close button, I receive an error? why is this so? is it because of the Hook??
What is the equivalent of this in WPF?
private static int WM_QUERYENDSESSION = 0x11;
private static bool systemShutdown = false;
protected override void WndProc(ref System.Windows.Forms.Message m)
{
if (m.Msg==WM_QUERYENDSESSION)
{
systemShutdown = true;
}
// If this is WM_QUERYENDSESSION, the closing event should be
// raised in the base WndProc.
base.WndProc(m);
} //WndProc
private void Form1_Closing(
System.Object sender,
System.ComponentModel.CancelEventArgs e)
{
if (systemShutdown)
// Reset the variable because the user might cancel the
// shutdown.
{
systemShutdown = false;
if (DialogResult.Yes==MessageBox.Show("My application",
"Do you want to save your work before logging off?",
MessageBoxButtons.YesNo))
{
SaveFile();
e.Cancel = false;
}
else{
e.Cancel = true;
}
CloseFile();
}
}

Why not use the Application.SessionEnding event? That seems to be designed to do what you wish and you won't need to handle the windows message directly.
You can set Cancel to true on the SessionEndingCancelEventArgs if want to cancel the shutdown.

You can use Application.SessionEnding event, but if you need to know more on how to test it - see following answer:
https://stackoverflow.com/a/65392006/2338477

Related

When "Show window content while dragging " is disabled, dragged window location value does not update in WPF

The reported issue occurs when the check box in below image(This PC -->Properties-->Advanced System Settings-->(under Performance in Advanced tab click Settings)) is disabled.
Try to get the mouse cursor position while dragging a window in WPF. The location of window is updated only when mouse is pressed and released, not when it window dragged.
public partial class MainWindow : Window
{
private const int WM_MOVING = 0x0216;
private HwndSource _hwndSrc;
private HwndSourceHook _hwndSrcHook;
public MainWindow()
{
InitializeComponent();
this.Loaded += MainWindow_Loaded;
this.Unloaded += MainWindow_Unloaded;
}
private void MainWindow_Unloaded(object sender, RoutedEventArgs e)
{
_hwndSrc.RemoveHook(_hwndSrcHook);
_hwndSrc.Dispose();
_hwndSrc = null;
}
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
_hwndSrc = HwndSource.FromDependencyObject(this) as HwndSource;
_hwndSrcHook = FilterMessage;
_hwndSrc.AddHook(_hwndSrcHook);
}
private IntPtr FilterMessage(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
switch (msg)
{
case WM_MOVING:
OnLocationChange();
break;
}
return IntPtr.Zero;
}
private void OnLocationChange()
{
Rect windowRect = new Rect(this.Left, this.Top, this.Width, this.Height);
}
}
Need the location of window to update when it is dragged by mouse.

The animation on window closing does not appear after SetWindowRgn

I want to show a window with a custom region and a custom appearance. I hook a window proc and set the custom region on WM_SIZE message by SetWindowRgn function. Also i process a WM_NCCALCSIZE to remove standard window frame.
[SecuritySafeCritical]
protected virtual IntPtr HwndSourceHookHandler(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) {
if(msg == WinApi.WM_NCCALCSIZE) {
handled = true;
return IntPtr.Zero;
}
if(msg == WinApi.WM_SIZE) {
if(region != IntPtr.Zero)
WinApi.DeleteObject(region);
int width = lParam.ToInt32() & 0xFFFF;
int height = lParam.ToInt32() >> 16;
region = WinApi.CreateRoundRectRgn(0, 0, width, height, 40, 40);
WinApi.SetWindowRgn(new WindowInteropHelper(this).Handle, region, true);
}
return IntPtr.Zero;
}
The window is appeared on my Windows 8.1 OS with a round corners as expected and without border and window buttons. But i notice that in this case window is closed immediately without performing the close animation. If i comment SetWindowRgn, all works fine. Anyone knows what i am doing wrong?
The animation is being performed in the Window. Therefore, you need to stop the Window from actually closing when the close Button is pressed and start the animation then instead. When the animation is complete, then you should close the Window. This can be achieved as follows:
Handle the Window.Closing and Window.Close events:
private void AnimationWindow_Closing(object sender, CancelEventArgs e)
{
if (!isCloseAnimationComplete)
{
e.Cancel = true;
CloseWindow();
}
}
private void AnimationWindow_Close(object sender, RoutedEventArgs e)
{
CloseWindow();
}
public void CloseWindow()
{
// Set up animation
animation.Completed += CloseAnimation_Completed;
// Start animation
}
private void CloseAnimation_Completed(object sender, EventArgs e)
{
isCloseAnimationComplete = true;
// Actually close Window here
Close();
}

How to distinguish 'Window close button clicked (X)' vs. window.Close() in closing handler

Is there a clever way to detect whether a window was closed by
A user pressing the (X) button in the upper right corner of the window or
window.Close() has been called programatically.
I would like to detect this in the window.Closing handler.
I could set a flag whenever I call window.Close(), but this is not a very pretty solution.
I'm not sure I like this at all but it's a question that you obviously have a reason for asking. if you were to take a stack trace in the OnClosing event you could look up for the Window.Close event.
protected override void OnClosing(System.ComponentModel.CancelEventArgs e)
{
bool wasCodeClosed = new StackTrace().GetFrames().FirstOrDefault(x => x.GetMethod() == typeof(Window).GetMethod("Close")) != null;
if (wasCodeClosed)
{
// Closed with this.Close()
}
else
{
// Closed some other way.
}
base.OnClosing(e);
}
The difference is the following:
Window.Close() causes WM_CLOSE to be send to window.
Alt+F4 and X button causes WM_SYSCOMMAND message with SC_CLOSE type. You can decide if you wish to route this message further ( and cause WM_CLOSE in the end ).
Here's a piece of code to catch this message. Return "True" from delegate if you wish to cancel default behaviour:
class SystemMenu : IDisposable
{
const int WM_SYSCOMMAND = 0x0112;
const int SC_CLOSE = 0xF060;
public delegate bool HandleSystemCommand();
HwndSource _source;
HandleSystemCommand _handler;
public SystemMenu(Window window, HandleSystemCommand handler )
{
_handler = handler;
_source = HwndSource.FromHwnd(new WindowInteropHelper( window ).Handle);
_source.AddHook(WndProc);
}
public void Dispose() {
_source.RemoveHook(WndProc);
}
private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
switch (msg)
{
case WM_SYSCOMMAND:
int command = wParam.ToInt32() & 0xfff0;
if (command == SC_CLOSE)
handled = _handler();
break;
default:
break;
}
return IntPtr.Zero;
}
}

Capturing WndProc message of a certain button click

I have a cancel button on my form. I want to determine inside the WndProc method that this Cancel button is clicked and write some code for it. This is absolutely necessary because otherwise I'm not able to cancel all other control validation events that are yet to be performed.
Please help.
.NET - 2.0, WinForms
This is how you could parse the WndProc message for a left-click on a child control:
protected override void WndProc(ref Message m)
{
// http://msdn.microsoft.com/en-us/library/windows/desktop/hh454920(v=vs.85).aspx
// 0x210 is WM_PARENTNOTIFY
// 513 is WM_LBUTTONCLICK
if (m.Msg == 0x210 && m.WParam.ToInt32() == 513)
{
var x = (int)(m.LParam.ToInt32() & 0xFFFF);
var y = (int)(m.LParam.ToInt32() >> 16);
var childControl = this.GetChildAtPoint(new Point(x, y));
if (childControl == cancelButton)
{
// ...
}
}
base.WndProc(ref m);
}
BTW: this is 32-bit code.
And if there are controls which failed validation then CauseValidation does not help
Well, sure it does, that's what the property was designed to do. Here's an example form to show this at work. Drop a textbox and a button on the form. Note how you can click the button to clear the textbox, even though the box always fails its validation. And how you can close the form.
public partial class Form1 : Form {
public Form1() {
InitializeComponent();
textBox1.Validating += new CancelEventHandler(textBox1_Validating);
button1.Click += new EventHandler(button1_Click);
button1.CausesValidation = false;
this.FormClosing += new FormClosingEventHandler(Form1_FormClosing);
}
private void textBox1_Validating(object sender, CancelEventArgs e) {
// Always fail validation
e.Cancel = true;
}
void button1_Click(object sender, EventArgs e) {
// Your Cancel button
textBox1.Text = string.Empty;
}
void Form1_FormClosing(object sender, FormClosingEventArgs e) {
// Allow the form to close even though validation failed
e.Cancel = false;
}
}

Detecting input keystroke during WPF processing

Greetings,
I want to write code that executes within an event handler, inside a WPF Windows application, that can detect a keypress, specifically an "Escape" character keypress, within a processing loop. This will allow the user to escape from processing. I realize this may be accomplished with some kind of multi-threaded approach, but the problem seems so simple I wondered if it might be accomplished as follows:
// Attempt 1: See if Keyboard static IsKeyDown method detects key presses while executing.
// Note that this was not successful. The Keyboard states do not appear to be updated during processing.
bool iskeypressed = false;
while (!iskeypressed)
{
System.Threading.Thread.Sleep(1000);
if (Keyboard.IsKeyDown(Key.Enter))
iskeypressed = true;
}
So, on to attempt #2. I saw some articles and samples using the Pinvoke "GetKeyboardState" method. I'm not sure I used the method correctly, but here is my attempt. It is a bit clumsy to refer to a Windows.Forms enumeration in a WPF application, but it seems like it could work.
// Attempt 2: Use Pinvoke GetKeyboardState method.
// So far, I've been unsuccessful with this as well, but I'm not sure my usage is correct.
bool iskeypressed = false;
while (!iskeypressed)
{
System.Threading.Thread.Sleep(1000);
if (isEscapePressed())
iskeypressed = true;
}
}
[DllImport("user32.dll")] public static extern int GetKeyboardState(byte[] lpKeyState);
private bool isEscapePressed()
{
byte[] keyboardState = new byte[255];
int keystate = GetKeyboardState(keyboardState);
if (keyboardState[(int)System.Windows.Forms.Keys.Escape] == 128)
return true;
else
return false;
}
But unfortunately, I'm not seeing any change in the keyboard states as this executes. I also played around a little with calls to the Dispatcher to see if I could get the keyboard information to refresh during processing, but I have not been successful with any technique.
I'm out of ideas. Can someone propose something? Thank you in advance for your assistance.
David
Something like this:
private bool IsCancelled { get; set; }
private void OnButtonClick(object sender, EventArgs e)
{
Action doWorkDelegate = DoWork;
doWorkDelegate.BeginInvoke(null, null);
}
protected override void OnKeyDown(KeyEventArgs e) {
if (e.Key == Key.Escape) {
IsCancelled = true;
e.Handled = true;
} else {
base.OnKeyDown(e);
}
}
private void DoWork()
{
IsCancelled = false;
while (!IsCancelled)
{
System.Threading.Thread.Sleep(1000);
}
}
The important point is that the method that does the work is executed in a separate thread so the main thread can process user input (key strokes).
You can not detect a key event while you are blocking WPF by executing a very long loop. You must use a multithreaded approach or you have to split the loop.
Using a BackgroundWorker is an easy way to let WPF continue handling the frontend while executing the loop.
private BackgroundWorker bw;
private void Button_Click(object sender, RoutedEventArgs e)
{
if (bw != null)
return;
bw = new BackgroundWorker();
bw.WorkerSupportsCancellation = true;
bw.WorkerReportsProgress = true;
bw.DoWork += (senderBw, eBw) =>
{
for (int i = 0; i < 100; i++)
{
Thread.Sleep(1000);
bw.ReportProgress(i);
if (eBw.Cancel)
return;
}
};
bw.ProgressChanged += (senderBw, eBw) =>
{
//TODO set progressbar to eBw.ProgressPercentage
};
bw.RunWorkerCompleted += (senderBw, eBw) =>
{
this.bw = null;
//TODO frontend stuff (hide progressbar etc)
};
bw.RunWorkerAsync();
}
private void MainWindow_KeyDown(object sender, KeyEventArgs e)
{
if (this.bw != null && this.bw.IsBusy && e.Key == Key.Escape)
this.bw.CancelAsync();
}

Resources