C# 如何捕捉结束调整窗口大小?

C# 如何捕捉结束调整窗口大小?,c#,wpf,xaml,resize,window,C#,Wpf,Xaml,Resize,Window,我需要在WPF中捕获事件endresize。WPF不提供仅在调整大小过程结束时触发的事件SizeChanged是唯一与窗口大小调整相关联的事件,它将在调整大小过程中触发多次 当SizeChanged事件触发时,一个总的技巧就是不断地设置计时器滴答作响。然后,计时器将不会有机会勾选,直到调整大小结束,并在这一点上做您的一次性处理 public MyUserControl() { _resizeTimer.Tick += _resizeTimer_Tick; } DispatcherTim

我需要在WPF中捕获事件endresize。

WPF不提供仅在调整大小过程结束时触发的事件SizeChanged是唯一与窗口大小调整相关联的事件,它将在调整大小过程中触发多次

当SizeChanged事件触发时,一个总的技巧就是不断地设置计时器滴答作响。然后,计时器将不会有机会勾选,直到调整大小结束,并在这一点上做您的一次性处理

public MyUserControl()
{
    _resizeTimer.Tick += _resizeTimer_Tick;
}

DispatcherTimer _resizeTimer = new DispatcherTimer { Interval = new TimeSpan(0, 0, 0, 0, 1500), IsEnabled = false };

private void UserControl_SizeChanged(object sender, SizeChangedEventArgs e)
{
    _resizeTimer.IsEnabled = true;
    _resizeTimer.Stop();
    _resizeTimer.Start();
}

void _resizeTimer_Tick(object sender, EventArgs e)
{
    _resizeTimer.IsEnabled = false;    

    //Do end of resize processing
}

NET的反应式扩展为处理标准事件模式提供了一些非常酷的功能,包括能够限制事件。我在处理大小更改事件时也遇到了类似的问题,虽然解决方案仍然有些“粗糙”,但我认为反应式扩展提供了一种更优雅的实现方式。以下是我的实现:

IObservable<SizeChangedEventArgs> ObservableSizeChanges = Observable
    .FromEventPattern<SizeChangedEventArgs>(this, "SizeChanged")
    .Select(x => x.EventArgs)
    .Throttle(TimeSpan.FromMilliseconds(200));

IDisposable SizeChangedSubscription = ObservableSizeChanges
    .ObserveOn(SynchronizationContext.Current)
    .Subscribe(x => {
        Size_Changed(x);
    });

您可以准确地检测WPF窗口大小调整何时结束,并且不需要计时器。当用户在窗口调整大小或移动操作结束时释放鼠标左键时,本机窗口将收到
WM_EXITSIZEMOVE
消息。WPF窗口没有收到此消息,因此我们需要连接一个将接收此消息的
WndProc
函数。我们可以使用
HwndSource
WindowInteropHelper
来获取窗口句柄。然后我们将把钩子添加到我们的
WndProc
函数中。我们将在窗口
Loaded
事件(vb.net代码)中执行所有这些操作:

Dim WinSource作为HwndSource
私有子窗口加载(发送方作为对象,e作为路由EventTargets)
WinSource=HwndSource.FromHwnd(新的WindowInteropHelper(Me).Handle)
AddHook(新的HwndSourceHook(WndProc的地址))
端接头
现在,在我们的
WndProc
中,我们将收听
WM_EXITSIZEMOVE
消息:

Const WM_EXITSIZEMOVE As Integer=&H232
私有函数WndProc(hwnd作为IntPtr,msg作为Integer,wParam作为IntPtr,lParam作为IntPtr,ByRef作为Boolean处理)作为IntPtr
如果msg=WM_存在IzeMove,则
你需要什么
如果结束
返回IntPtr.Zero
端函数
对这一技术和类似技术进行了解释和说明

请注意,函数应该返回IntPtr.Zero。此外,除了处理您感兴趣的特定消息外,不要在此函数中执行任何操作

现在,
WM_EXITSIZEMOVE
也会在移动操作结束时发送,我们只对调整大小感兴趣。有几种方法可以确定这是调整大小操作的结束。我通过收听
WM_size
消息(在调整大小过程中多次发送)并结合一个标志来完成。整个解决方案如下所示:

(注意:不要与此处突出显示的代码混淆,因为它对于vb.net是错误的)

Dim WinSource作为HwndSource
常量WM_大小调整为整数=&H214
常量WM_EXITSIZEMOVE为整数=&H232
Dim窗口的大小调整为布尔值=False
私有子窗口加载(发送方作为对象,e作为路由EventTargets)
WinSource=HwndSource.FromHwnd(新的WindowInteropHelper(Me).Handle)
AddHook(新的HwndSourceHook(WndProc的地址))
端接头
私有函数WndProc(hwnd作为IntPtr,msg作为Integer,wParam作为IntPtr,lParam作为IntPtr,ByRef作为Boolean处理)作为IntPtr
如果msg=WM\u,则
如果WindowWasResized=False,则
'指示用户正在调整窗口大小而不移动窗口
WindowWasResized=True
如果结束
如果结束
如果msg=WM_存在IzeMove,则
'检查这是否是调整大小和不移动操作的结束
如果WindowWasResized=True,则
你需要什么
'下次调整/移动时将其设置回false
WindowWasResized=False
如果结束
如果结束
返回IntPtr.Zero
端函数

就这样。

不需要计时器,也不需要@Bohoo up提供的非常干净的解决方案,我只是将他的代码从vb.net改编成c#

对于带有Rx的UWP(系统无功)

//停止窗口更新
rootFrame=新帧
{
水平对齐=水平对齐。左,
垂直对齐=垂直对齐。顶部,
宽度=Window.Current.Bounds.Width,
高度=Window.Current.Bounds.Height
};
//节流后的更新
var sizeChangedObservable=Observable.FromEventPattern(
handler=>Window.Current.SizeChanged+=handler,
handler=>Window.Current.SizeChanged-=handler);
SizeChangedServable.Throttle(时间跨度从秒(0.35)).ObserveOnDispatcher(CoreDispatcherPriority.Normal).订阅(x=>
{
rootFrame.Width=x.EventArgs.Size.Width;
rootFrame.Height=x.EventArgs.Size.Height;
});

Form.ResizeBegin/End在Winforms中。通知仍然存在,但在WPF中被忽略。“向前走两步,向后退一步。@马丁,请解释你为什么提出_resizeTimer.IsEnabled=true;在停止开始之前?这对我来说似乎毫无意义。我喜欢这种机制,因为它允许在用户暂停调整大小时进行一些处理。当用户调整大小时需要重新布局画布的情况。当用户停止移动鼠标(但未释放鼠标)时,使用此计时器方法可以执行重新布局,并可以看到新大小的影响。我的测试团队喜欢它,而不是以前的-只有当鼠标发布ie WM_EXITSIZEMOVE方法时才重新布局。我确实将计时器间隔设置为200ms,而不是本示例代码中使用的1500值。我认为,我们应该处理
WM\u ENTERSIZEMOVE
而不是
WM\u size
private void Size_Changed(SizeChangedEventArgs e) {
    // custom code for dealing with end of size changed here
}
     public partial class MainWindow : Window
        {
            public MainWindow()
            {
                InitializeComponent();
                this.Loaded += MainWindow_Loaded;
            }

            private void MainWindow_Loaded(object sender, RoutedEventArgs e)
            {
                // this two line have to be exactly onload
                HwndSource source = HwndSource.FromHwnd(new WindowInteropHelper(this).Handle);
                source.AddHook(new HwndSourceHook(WndProc));
            }


            const int WM_SIZING = 0x214;
            const int WM_EXITSIZEMOVE = 0x232;
            private static bool WindowWasResized = false;


            private static IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
            {
                if (msg == WM_SIZING)
                {

                    if (WindowWasResized == false)
                    {

                        //    'indicate the the user is resizing and not moving the window
                        WindowWasResized = true;
                    }
                }

                if (msg == WM_EXITSIZEMOVE)
                {

                    // 'check that this is the end of resize and not move operation          
                    if (WindowWasResized == true)
                    {

                        // your stuff to do 
                        Console.WriteLine("End");

                        // 'set it back to false for the next resize/move
                        WindowWasResized = false;
                    }
                }

                return IntPtr.Zero;
            }

    }
            //Stop window updates
            rootFrame = new Frame
            {
                HorizontalAlignment = HorizontalAlignment.Left,
                VerticalAlignment = VerticalAlignment.Top,
                Width = Window.Current.Bounds.Width,
                Height = Window.Current.Bounds.Height
            };

            //updates after throttling
            var sizeChangedObservable = Observable.FromEventPattern<WindowSizeChangedEventHandler, WindowSizeChangedEventArgs>(
                      handler => Window.Current.SizeChanged += handler,
                      handler => Window.Current.SizeChanged -= handler);

            sizeChangedObservable.Throttle(TimeSpan.FromSeconds(0.35)).ObserveOnDispatcher(CoreDispatcherPriority.Normal).Subscribe(x =>
            {
                rootFrame.Width = x.EventArgs.Size.Width;
                rootFrame.Height = x.EventArgs.Size.Height;
            });