When to call HwndSource.RemoveHook() - c#

I am calling HwndSource.AddHook() to get the messages to be handled in my WPF window. I wonder whether I need to call HwndSource.RemoveHook() when the window is destroyed - this window is not the MainWindow. If so, what is the right place for calling it (Closing())?. It seems the Hooks are removed when the window is destroyed.
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)
{
if (NativeMethods.UWM_SHOWMYAPP == msg)
{
if (this.WindowState == WindowState.Minimized)
this.WindowState = WindowState.Normal;
this.Activate();
handled = true;
}
return IntPtr.Zero;
}
void StatusWindow_Closing(object sender, CancelEventArgs e)
{
HwndSource source = PresentationSource.FromVisual(this) as HwndSource;
source.RemoveHook(WndProc);
}
Here while removing I am getting the HwndSource again. Is this okay? or should I keep the source object used for Add() and use it for Remove()?

If so, what is the right place for calling it (Closing())?
You could override the OnClosed method.
Is this okay? or should I keep the source object used for Add() and use it for Remove()?
It's "okay" but it's unnecessary to call PresentationSource.FromVisual and cast the result more than once. This is probably how I would do it:
private HwndSource _source;
protected override void OnSourceInitialized(EventArgs e)
{
base.OnSourceInitialized(e);
_source = PresentationSource.FromVisual(this) as HwndSource;
_source.AddHook(WndProc);
}
protected override void OnClosed(EventArgs e)
{
_source.RemoveHook(WndProc);
base.OnClosed(e);
}

Related

After Disposing HwndSource calling close() on window leads to System.NullReferenceException

I am calling new Window1().ShowDialog(); from MainWindow when Dispose() is called on HwndSource calling this.close() on Window arise An unhandled exception of type 'System.NullReferenceException' occurred in PresentationFramework.dll Object reference not set to an instance of an object.
What if i don't call Dispose, is there any problem may occurs in the future.
public partial class Window1 : Window
{
private const int MESSAGE_CAPTURED_OK = 0x0400 + 6;
private HwndSource source;
public Window1()
{
InitializeComponent();
Closing += Window_Closing;
}
private void Close_Click(object sender, RoutedEventArgs e) => this.Close();
protected override void OnSourceInitialized(EventArgs e)
{
base.OnSourceInitialized(e);
var FormHandle = new WindowInteropHelper(this).Handle;
source = HwndSource.FromHwnd(FormHandle);
source.AddHook(WndProc);
}
IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
// Handle messages...
if (msg == MESSAGE_CAPTURED_OK)
{
// operations
}
return IntPtr.Zero;
}
private void Window_Closing(object sender, CancelEventArgs e)
{
source.RemoveHook(WndProc);
//source.Dispose(); This line rises error
}
}
DoDialogHide() seems to get upset with the dispose.
Doing this will correct the problem
private void Window_Closing(object sender, CancelEventArgs e)
{
Hide();
source.RemoveHook(WndProc);
source.Dispose();
}

how do I bind the repeating codes shared by multiple forms in c#

I have around 10+ child forms and all the child forms have the following features
Close button: on hover displays "close" text and when mouse clicked, shows a message box to confirm the action
Minimize button: On hover, it displays "minimize" text, when the mouse is clicked, the form gets minimized
Drag anyway inside the parent form
I have to avoid using Visual Studio's default style to get a desirable customized form.
-The code below is shared by all the child forms, since the code is the same, is there a way to bind to one place and reference?
code
public const int WM_NCLBUTTONDOWN = 0xA1;
public const int HTCAPTION = 0x2;
[DllImport("User32.dll")]
public static extern bool ReleaseCapture();
[DllImport("User32.dll")]
public static extern int SendMessage(IntPtr hWnd, int Msg, int wParam, int lParam);
//Dtrag the form
private void DragPanel(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
ReleaseCapture();
SendMessage(Handle, WM_NCLBUTTONDOWN, HTCAPTION, 0);
}
}
#region controlbox
private void minimizebtn_Click(object sender, EventArgs e)
{
WindowState = FormWindowState.Minimized;
}
private void closebtn_Click(object sender, EventArgs e)
{
if (MessageBox.Show("Are you sure you want to Close the window?", "Close", MessageBoxButtons.OKCancel) == DialogResult.OK)
{
Close();
//Application.Exit();
}
}
private void closebtn_MouseHover(object sender, EventArgs e)
{
System.Windows.Forms.ToolTip ToolTip1 = new System.Windows.Forms.ToolTip();
ToolTip1.SetToolTip(this.closebtn, "Close");
}
private void minimizebtn_MouseHover(object sender, EventArgs e)
{
System.Windows.Forms.ToolTip ToolTip1 = new System.Windows.Forms.ToolTip();
ToolTip1.SetToolTip(this.minimizebtn, "Minimize");
}
#endregion
You could create a base class which itself derives from Form. Your actual forms should then derive from that base class.

WPF Intercept global mouse movements (like using IMessageFilter in Windows Form)

I'm converting a UserControl from Windows Form to WPF, Here is my Windows Form working code:
GlobalMouseHandler mouseHandler = new GlobalMouseHandler();
mouseHandler.MouseMove += OnGlobalMouseMove;
mouseHandler.MouseButtonUp += OnGlobalMouseButtonUp;
public class GlobalMouseHandler : IMessageFilter, IDisposable
{
public event MouseMovedEvent MouseMove;
public event MouseButtonUpEvent MouseButtonUp;
public GlobalMouseHandler(){
Application.AddMessageFilter(this);
}
public bool PreFilterMessage(ref Message m)
{
switch (m.Msg)
{
case WM_MOUSEMOVE:
MouseMove?.Invoke();
break;
case WM_LBUTTONUP:
MouseButtonUp?.Invoke();
break;
}
return false;
}
}
I used IMessageFilter to get the mouse move and mouse up events in my application and attach to them.
Now, I know that IMessageFilter is not available in WPF, but is there a simple way to record these simple mouse events in WPF?
You can handle messages similar way (mouse move for example):
Xaml:
<Window ... Loaded="Window_OnLoaded">
...
</Window>
Code-behind:
using System.Windows.Interop;
...
private const int WM_MOUSEMOVE = 0x0200;
private void Window_OnLoaded(object sender, RoutedEventArgs e)
{
HwndSource.FromHwnd(new WindowInteropHelper(this).Handle)?.AddHook(this.WndProc);
}
private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
switch (msg)
{
case WM_MOUSEMOVE:
// MouseMove?.Invoke();
break;
}
return IntPtr.Zero;
}
Of course, if you don't want to do it in native WPF manner (left button up for example):
<Window ... PreviewMouseLeftButtonUp="Window_OnPreviewMouseLeftButtonUp">
...
</Window>
private void Window_OnPreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
// e.Handled = true if you want to prevent following MouseLeftButtonUp event processing
}

Form doesn't have DragMove() method?

So I need to move my form no matter what element is clicked (I need to drag form by pressing and holding button, form is 100% transparent) , I tried to do this:
private void MessageForm_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
this.DragMove();
}
but I was quite surprised that there in no DragMove() method, it was renamed or what I am missing?
And if this is not possible, is there any other way to do that?
You will need something like this:
public const int WM_NCLBUTTONDOWN = 0xA1;
public const int HT_CAPTION = 0x2;
[DllImportAttribute("user32.dll")]
public static extern int SendMessage(IntPtr hWnd, int Msg, int wParam, int lParam);
[DllImportAttribute("user32.dll")]
public static extern bool ReleaseCapture();
private void MessageForm_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
ReleaseCapture();
SendMessage(Handle, WM_NCLBUTTONDOWN, HT_CAPTION, 0);
}
}
private void button1_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
ReleaseCapture();
SendMessage(Handle, WM_NCLBUTTONDOWN, HT_CAPTION, 0);
}
}
Basically, it acts like dragging the title bar/window caption when you drag anywhere in the window. This is great for borderless windows.
EDIT:
If you use a button as the control for moving the form, you will need to be careful when attaching your click event handler as you are overriding the Windows Forms event loop for that control.
By moving/adding the ReleaseCapture and SendMessage calls to the MouseDown event of a control, you can use it to drag the window. Any control can be used to drag the window as long as you update the MouseDown event to be similar to the code above.
Tested and working: this.DragMove(), alternative
private void Form1_Load(object sender, EventArgs e)
{
FormCommonSetting(this);
}
public void FromCommonSetting(Form _Form)
{
_Form.StartPosition = FormStartPosition.CenterScreen;
_Form.FormBorderStyle = FormBorderStyle.None;
_Form.MaximizeBox = false;
_Form.ShowInTaskbar = true;
_Form.AutoSize = false;
}
protected override void WndProc(ref Message _Message)
{
switch (_Message.Msg)
{
case 0x84:
base.WndProc(ref _Message);
if ((int)_Message.Result == 0x1)
_Message.Result = (IntPtr)0x2;
return;
}
base.WndProc(ref _Message);
}

LocationChanged is not reliably called when Window is being dragged

To implement docking I was relying on listening to the Window.LocationChanged event to detect the changing position of a window being dragged around the screen. But a user reported that docking was not working on their machine.
Turns out they had disabled "Show window contents while dragging" in Windows performance options and as a result the LocationChanged event is only fired once the window is moved to it's final position, not while the window is in mid-flight.
I was wondering if there was an alternative way to detect window moves, a nice way. I know I could pinvoke, or wire up some horrific timer, but I was hoping for a better way, perhaps there is a reliable event to listen on?
Here's a method to forestall any "you didn't post any code"/"what have you tried" complaints.
protected override void OnLocationChanged(EventArgs e)
{
}
Here's my solution,
Excellent work.
class MyWindow : Window
{
private const int WM_MOVING = 0x0216;
private HwndSource _hwndSrc;
private HwndSourceHook _hwndSrcHook;
public MyWindow()
{
Loaded += OnLoaded;
Unloaded += OnUnloaded;
}
void OnUnloaded(object sender, RoutedEventArgs e)
{
_hwndSrc.RemoveHook(_hwndSrcHook);
_hwndSrc.Dispose();
_hwndSrc = null;
}
void OnLoaded(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()
{
//Do something
}
}

Categories

Resources