Respuestas:
En realidad, hasta donde tengo entendido, tal cosa es posible en WPF usando HwndSource
y HwndSourceHook
. Vea este hilo en MSDN como ejemplo. (Código relevante incluido a continuación)
// 'this' is a Window
HwndSource source = HwndSource.FromHwnd(new WindowInteropHelper(this).Handle);
source.AddHook(new HwndSourceHook(WndProc));
private static IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
// do stuff
return IntPtr.Zero;
}
Ahora, no estoy muy seguro de por qué querría manejar los mensajes de Windows Messaging en una aplicación WPF (a menos que sea la forma más obvia de interoperabilidad para trabajar con otra aplicación WinForms). La ideología de diseño y la naturaleza de la API son muy diferentes en WPF de WinForms, por lo que le sugiero que se familiarice más con WPF para ver exactamente por qué no existe un equivalente de WndProc.
WM_MOUSEWHEEL
por ejemplo, la única forma de capturar esos mensajes de manera confiable era agregando el WndProc
a una ventana de WPF. Esto funcionó para mí, mientras que el funcionario MouseWheelEventHandler
simplemente no funcionó como se esperaba. No pude alinear los taquiones WPF correctos para obtener un comportamiento confiable MouseWheelEventHandler
, de ahí la necesidad de acceso directo al archivo WndProc
.
Puede hacer esto a través del System.Windows.Interop
espacio de nombres que contiene una clase llamada HwndSource
.
Ejemplo de uso de esto
using System;
using System.Windows;
using System.Windows.Interop;
namespace WpfApplication1
{
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
}
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)
{
// Handle messages...
return IntPtr.Zero;
}
}
}
Completamente tomado de la excelente publicación del blog: Uso de un WndProc personalizado en aplicaciones WPF de Steve Rands
HwndSource src = HwndSource.FromHwnd(new WindowInteropHelper(this).Handle);
src.AddHook(new HwndSourceHook(WndProc));
.......
public IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
if(msg == THEMESSAGEIMLOOKINGFOR)
{
//Do something here
}
return IntPtr.Zero;
}
Si no le importa hacer referencia a WinForms, puede usar una solución más orientada a MVVM que no combine el servicio con la vista. Necesita crear e inicializar un System.Windows.Forms.NativeWindow, que es una ventana liviana que puede recibir mensajes.
public abstract class WinApiServiceBase : IDisposable
{
/// <summary>
/// Sponge window absorbs messages and lets other services use them
/// </summary>
private sealed class SpongeWindow : NativeWindow
{
public event EventHandler<Message> WndProced;
public SpongeWindow()
{
CreateHandle(new CreateParams());
}
protected override void WndProc(ref Message m)
{
WndProced?.Invoke(this, m);
base.WndProc(ref m);
}
}
private static readonly SpongeWindow Sponge;
protected static readonly IntPtr SpongeHandle;
static WinApiServiceBase()
{
Sponge = new SpongeWindow();
SpongeHandle = Sponge.Handle;
}
protected WinApiServiceBase()
{
Sponge.WndProced += LocalWndProced;
}
private void LocalWndProced(object sender, Message message)
{
WndProc(message);
}
/// <summary>
/// Override to process windows messages
/// </summary>
protected virtual void WndProc(Message message)
{ }
public virtual void Dispose()
{
Sponge.WndProced -= LocalWndProced;
}
}
Use SpongeHandle para registrarse para los mensajes que le interesan y luego anule WndProc para procesarlos:
public class WindowsMessageListenerService : WinApiServiceBase
{
protected override void WndProc(Message message)
{
Debug.WriteLine(message.msg);
}
}
El único inconveniente es que debe incluir la referencia System.Windows.Forms, pero por lo demás, esta es una solución muy encapsulada.
Se puede leer más sobre esto aquí.
Aquí hay un enlace sobre cómo anular WindProc usando comportamientos: http://10rem.net/blog/2010/01/09/a-wpf-behavior-for-window-resize-events-in-net-35
[Editar: más vale tarde que nunca] A continuación se muestra mi implementación basada en el enlace anterior. Aunque revisando esto, me gustan más las implementaciones de AddHook. Podría cambiar a eso.
En mi caso, quería saber cuándo se estaba cambiando el tamaño de la ventana y un par de cosas más. Esta implementación se conecta al xaml de Windows y envía eventos.
using System;
using System.Windows.Interactivity;
using System.Windows; // For Window in behavior
using System.Windows.Interop; // For Hwnd
public class WindowResizeEvents : Behavior<Window>
{
public event EventHandler Resized;
public event EventHandler Resizing;
public event EventHandler Maximized;
public event EventHandler Minimized;
public event EventHandler Restored;
public static DependencyProperty IsAppAskCloseProperty = DependencyProperty.RegisterAttached("IsAppAskClose", typeof(bool), typeof(WindowResizeEvents));
public Boolean IsAppAskClose
{
get { return (Boolean)this.GetValue(IsAppAskCloseProperty); }
set { this.SetValue(IsAppAskCloseProperty, value); }
}
// called when the behavior is attached
// hook the wndproc
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.Loaded += (s, e) =>
{
WireUpWndProc();
};
}
// call when the behavior is detached
// clean up our winproc hook
protected override void OnDetaching()
{
RemoveWndProc();
base.OnDetaching();
}
private HwndSourceHook _hook;
private void WireUpWndProc()
{
HwndSource source = HwndSource.FromVisual(AssociatedObject) as HwndSource;
if (source != null)
{
_hook = new HwndSourceHook(WndProc);
source.AddHook(_hook);
}
}
private void RemoveWndProc()
{
HwndSource source = HwndSource.FromVisual(AssociatedObject) as HwndSource;
if (source != null)
{
source.RemoveHook(_hook);
}
}
private const Int32 WM_EXITSIZEMOVE = 0x0232;
private const Int32 WM_SIZING = 0x0214;
private const Int32 WM_SIZE = 0x0005;
private const Int32 SIZE_RESTORED = 0x0000;
private const Int32 SIZE_MINIMIZED = 0x0001;
private const Int32 SIZE_MAXIMIZED = 0x0002;
private const Int32 SIZE_MAXSHOW = 0x0003;
private const Int32 SIZE_MAXHIDE = 0x0004;
private const Int32 WM_QUERYENDSESSION = 0x0011;
private const Int32 ENDSESSION_CLOSEAPP = 0x1;
private const Int32 WM_ENDSESSION = 0x0016;
private IntPtr WndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, ref Boolean handled)
{
IntPtr result = IntPtr.Zero;
switch (msg)
{
case WM_SIZING: // sizing gets interactive resize
OnResizing();
break;
case WM_SIZE: // size gets minimize/maximize as well as final size
{
int param = wParam.ToInt32();
switch (param)
{
case SIZE_RESTORED:
OnRestored();
break;
case SIZE_MINIMIZED:
OnMinimized();
break;
case SIZE_MAXIMIZED:
OnMaximized();
break;
case SIZE_MAXSHOW:
break;
case SIZE_MAXHIDE:
break;
}
}
break;
case WM_EXITSIZEMOVE:
OnResized();
break;
// Windows is requesting app to close.
// See http://msdn.microsoft.com/en-us/library/windows/desktop/aa376890%28v=vs.85%29.aspx.
// Use the default response (yes).
case WM_QUERYENDSESSION:
IsAppAskClose = true;
break;
}
return result;
}
private void OnResizing()
{
if (Resizing != null)
Resizing(AssociatedObject, EventArgs.Empty);
}
private void OnResized()
{
if (Resized != null)
Resized(AssociatedObject, EventArgs.Empty);
}
private void OnRestored()
{
if (Restored != null)
Restored(AssociatedObject, EventArgs.Empty);
}
private void OnMinimized()
{
if (Minimized != null)
Minimized(AssociatedObject, EventArgs.Empty);
}
private void OnMaximized()
{
if (Maximized != null)
Maximized(AssociatedObject, EventArgs.Empty);
}
}
<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:behaviors="clr-namespace:RapidCoreConfigurator._Behaviors"
Title="name" Height="500" Width="750" BorderBrush="Transparent">
<i:Interaction.Behaviors>
<behaviors:WindowResizeEvents IsAppAskClose="{Binding IsRequestClose, Mode=OneWayToSource}"
Resized="Window_Resized"
Resizing="Window_Resizing" />
</i:Interaction.Behaviors>
...
</Window>
Here is a link...
respuestas exactas: como arriba.
Puede adjuntar a la clase 'SystemEvents' de la clase Win32 incorporada:
using Microsoft.Win32;
en una clase de ventana WPF:
SystemEvents.PowerModeChanged += SystemEvents_PowerModeChanged;
SystemEvents.SessionSwitch += SystemEvents_SessionSwitch;
SystemEvents.SessionEnding += SystemEvents_SessionEnding;
SystemEvents.SessionEnded += SystemEvents_SessionEnded;
private async void SystemEvents_PowerModeChanged(object sender, PowerModeChangedEventArgs e)
{
await vm.PowerModeChanged(e.Mode);
}
private async void SystemEvents_PowerModeChanged(object sender, PowerModeChangedEventArgs e)
{
await vm.PowerModeChanged(e.Mode);
}
private async void SystemEvents_SessionSwitch(object sender, SessionSwitchEventArgs e)
{
await vm.SessionSwitch(e.Reason);
}
private async void SystemEvents_SessionEnding(object sender, SessionEndingEventArgs e)
{
if (e.Reason == SessionEndReasons.Logoff)
{
await vm.UserLogoff();
}
}
private async void SystemEvents_SessionEnded(object sender, SessionEndedEventArgs e)
{
if (e.Reason == SessionEndReasons.Logoff)
{
await vm.UserLogoff();
}
}
Hay formas de manejar mensajes con un WndProc en WPF (por ejemplo, usando un HwndSource, etc.), pero generalmente esas técnicas están reservadas para la interoperabilidad con mensajes que no se pueden manejar directamente a través de WPF. La mayoría de los controles WPF ni siquiera son ventanas en el sentido de Win32 (y por extensión Windows.Forms), por lo que no tendrán WndProcs.
WndProc
a invalidarse, le System.Windows.Interop
permite obtener un HwndSource
objeto a través de HwndSource.FromHwnd
o al PresentationSource.FromVisual(someForm) as HwndSource
que puede vincular un delegado con un patrón especial. Este delegado tiene muchos de los mismos argumentos que un WndProc
objeto Mensaje.
WPF no funciona en WinForms tipo wndprocs
Puede alojar un HWndHost en un elemento WPF apropiado y luego anular el wndproc de Hwndhost, pero AFAIK eso es lo más cercano que va a conseguir.
http://msdn.microsoft.com/en-us/library/ms742522.aspx
http://blogs.msdn.com/nickkramer/archive/2006/03/18/554235.aspx
La respuesta corta es que no puedes. WndProc funciona pasando mensajes a un HWND en un nivel Win32. Las ventanas de WPF no tienen HWND y, por lo tanto, no pueden participar en los mensajes de WndProc. El bucle de mensajes de WPF base se encuentra encima de WndProc, pero los abstrae de la lógica central de WPF.
Puede usar un HWndHost y obtener un WndProc para ello. Sin embargo, es casi seguro que esto no es lo que desea hacer. Para la mayoría de los propósitos, WPF no opera en HWND y WndProc. Es casi seguro que su solución se basa en realizar un cambio en WPF, no en WndProc.