c# wpf wndproc

c# - ¿Cómo manejar los mensajes de WndProc en WPF?



(9)

En realidad, hasta donde yo entiendo, tal cosa es posible en WPF usando HwndSource y HwndSourceHook . Vea este hilo en MSDN como un 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 mensajería de Windows 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 con WPF más para ver exactamente por qué no hay un equivalente de WndProc.

Encontrar WPF una curva de aprendizaje empinada.

En las buenas formas de Windows, simplemente anularía WndProc y comenzaría a manejar los mensajes a medida que llegaran.

¿Puede alguien mostrarme un ejemplo de cómo lograr lo mismo en WPF?


Hay formas de manejar mensajes con WndProc en WPF (por ejemplo, usando un HwndSource, etc.), pero generalmente esas técnicas están reservadas para la interoperabilidad con mensajes que no pueden manejarse directamente a través de WPF. La mayoría de los controles WPF ni siquiera tienen ventanas en el sentido Win32 (y por extensión Windows.Forms), por lo que no tendrán WndProcs.


La respuesta corta es que no puedes. WndProc funciona pasando mensajes a un HWND en un nivel de Win32. Las ventanas de WPF no tienen HWND y, por lo tanto, no pueden participar en los mensajes de WndProc. El bucle de mensaje WPF base se sienta encima de WndProc pero los abstrae de la lógica WPF principal.

Puede usar HWndHost y obtener un WndProc para ello. Sin embargo, esto es casi seguro que no es lo que quieres hacer. Para la mayoría de los propósitos, WPF no opera en HWND y WndProc. Su solución casi con seguridad depende de realizar un cambio en WPF no en WndProc.


Puede adjuntar a la clase ''SystemEvents'' de la clase Win32 incorporada:

using Microsoft.Win32;

en una clase de ventana de 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(); } }


Puede encontrar otra explicación para adjuntar a WndProc here .


Puede hacerlo a través del espacio de nombres System.Windows.Interop que contiene una clase llamada HwndSource .

Ejemplo de usar 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 de blog: Usar un WndProc personalizado en aplicaciones WPF de Steve Rands (nota, el enlace ya no es válido)

Este sitio ya no funciona, pero puede verlo desde el motor de Wayback: http://web.archive.org/web/20091019124817/http://www.steverands.com/2009/03/19/custom-wndproc-wpf-apps/


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. Debe crear e inicializar 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 en los mensajes que le interesen 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 de lo contrario, esta es una solución muy encapsulada.

Se puede leer más sobre esto here



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