The animation on window closing does not appear after SetWindowRgn - wpf

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();
}

Related

How to prevent MouseUp event after DoubleClick on Window TitleBar in WPF?

If user double-clicks window title bar to maximize window, the window will maximize on MouseDown event of the second click, but then the MouseUp event on that same second click will be registered by one of the controls in the window that are now under the cursor and trigger something user didn't want.
How can it be prevented?
This should work just fine:
Solution:
//WNDPROC INTEROP
private const int WM_NCLBUTTONDBLCLK = 0x00A3;
protected override void OnSourceInitialized(EventArgs e)
{
base.OnSourceInitialized(e);
HwndSource source = PresentationSource.FromVisual(this) as HwndSource;
source.AddHook(WndProc);
}
private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
//TITLE BAR DOUBLE CLICK
if (msg == WM_NCLBUTTONDBLCLK)
{
Console.WriteLine("titlebar double click " + this.WindowState);
//WINDOW ABOUT TO GET MAXIMIZED
if (msg == WM_NCLBUTTONDBLCLK)
handleNextMouseup = true;
else if (msg == 0x202) // mouseup
{
if (handleNextMouseup)
handled = true;
handleNextMouseup = false;
}
}
return IntPtr.Zero;
}
another possible solution:
//WNDPROC INTEROP
private const int WM_NCLBUTTONDBLCLK = 0x00A3;
protected override void OnSourceInitialized(EventArgs e)
{
base.OnSourceInitialized(e);
HwndSource source = PresentationSource.FromVisual(this) as HwndSource;
source.AddHook(WndProc);
}
private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
//TITLE BAR DOUBLE CLICK
if (msg == WM_NCLBUTTONDBLCLK)
{
Console.WriteLine("titlebar double click " + this.WindowState);
//WINDOW ABOUT TO GET MAXIMIZED
if (this.WindowState == WindowState.Normal)
{
fakehittestvisible = true;
}
}
return IntPtr.Zero;
}
private bool fakehittestvisible = false;
//ON PREVIEW MOUSE UP ABORT IF MIXIMIZING HAPPENED
private void this_PreviewMouseUp(object sender, MouseButtonEventArgs e)
{
if (fakehittestvisible)
{
e.Handled = true;
fakehittestvisible = false;
return;
}
fakehittestvisible = false;
Console.WriteLine("this mouse up state is " + this.WindowState);
}

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.

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;
}
}

Resizing custom forms (with drop shadow effect) and controls on mouse drag event in c sharp?

In my application I have to resize forms and all its control on mouse drag effect and forms should have drop shadow effect the problem is that all my forms are custom one (with no boarder).
Thanks in advance
i think u have to implement by yourself
on mouse down start bind on mouse drag + change cursor to resize icon
on mouse drag, just simply reduce your form size
on mouse up unbind mouse drag event
the reason i suggest dynamic event binding so u can specified which control or area should have mouse down
I'm not sure about the drop shadow effect, but you should be able to resize a form by placing a button in the bottom right corner with some appropriate icon. When the user clicks and drags this button, it resizes the form. Here's some example code:
public partial class Form1 : Form
{
private int bottomBorder;
private int rightBorder;
private Point mouseStart;
private bool isResizing = false;
public Form1()
{
InitializeComponent();
}
private void button1_MouseMove(object sender, MouseEventArgs e)
{
if (isResizing)
{
var newLocation = button1.Location;
newLocation.Offset(
e.X - mouseStart.X,
e.Y - mouseStart.Y);
button1.Location = newLocation;
this.Height = button1.Bottom + bottomBorder;
this.Width = button1.Right + rightBorder;
button1.Refresh();
}
}
private void button1_MouseDown(object sender, MouseEventArgs e)
{
isResizing = true;
mouseStart = e.Location;
}
private void button1_MouseUp(object sender, MouseEventArgs e)
{
isResizing = false;
}
private void Form1_Load(object sender, EventArgs e)
{
bottomBorder = this.Height - button1.Bottom;
rightBorder = this.Width - button1.Right;
}
}
Without a border (or some control), how do you intend to resize? Figure that part out, then try this code in your form:
public class CustomForm : Form
{
private const int WmNcLButtonDown = 0xA1;
private const int HtBottomRight = 17;
[DllImport("user32.dll")]
private static extern int ReleaseCapture();
[DllImport("user32.dll")]
private static extern int SendMessage(IntPtr hwnd, int msg, int wparam, int lparam);
// elsewhere
void ResizeForm()
{
ReleaseCapture();
SendMessage(this.Handle, WmNcLButtonDown, HtBottomRight, 0);
}
}
This code will resize your form as though the bottom right corner was used. Look up HT_BOTTOMRIGHT and other HT_ constants for different locations for resizing.
I used the solutions by Don Kirkby and Matthew Ferreira and created my own solution combining the two. I added a StatusStrip named "resizeHandle", made it's size 20x20 pixels and listened to it's events.
public class CustomForm : Form
{
private const int WmNcLButtonDown = 0xA1;
private const int HtBottomRight = 17;
private const int wmNcLButtonUp = 0xA2;
private bool isResizing = false;
[DllImport("user32.dll")]
private static extern int ReleaseCapture();
[DllImport("user32.dll")]
private static extern int SendMessage(IntPtr hwnd, int msg, int wparam, int lparam);
private void resizeHandle_MouseDown(object sender, MouseEventArgs e)
{
isResizing = true;
}
private void resizeHandle_MouseMove(object sender, MouseEventArgs e)
{
if (isResizing)
{
// Check if we have released the Left mouse button
isResizing = (e.Button == MouseButtons.Left);
ReleaseCapture();
if (isResizing)
{
SendMessage(Handle, wmNcLButtonDown, HtBottomRight, 0);
}
else
{
// Left Mouse button was released, end resizing.
SendMessage(Handle, wmNcLButtonUp, HtBottomRight, 0);
}
}
}

Handling System Shutdown in 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

Resources