导航

WPF实现窗口比例恒定不变小结(2)

Posted on 2012-10-26 17:38  吴豪  阅读(1821)  评论(2编辑  收藏  举报

WPF实现窗口比例恒定不2

在上一篇中讲到可以通过捕获并处理WM_EXITSIZEMOVE消息来达到保持窗口比例不变,但是必须在释放鼠标的情况下才能调整窗口比例,算不上实时调整。在上一遍还提到在窗口的SizeChanged事件中调整窗口比例,但是因为事件触发的太频繁,导致在释放鼠标后窗口又还原为拖拉之前的大小。

那能不能实现窗口比例的实时调整呢?让我们来看看下面一种方法。

其实这种方法就是结合SizeChanged事件和WM_EXITSIZEMOVE消息。让我们先来分析下这2个方式:

SizeChanged事件:

    优点:能够在鼠标按下(拖拉住窗口不放)的情况下实时调整窗口;

    缺点:在释放鼠标后,窗口可能会还原为拖拉之前的大小;

WM_EXITSIZEMOVE消息:

    优点:在释放鼠标后,窗口能够保持拖拉后的大小;

    缺点:不能实时调整窗口,只能在释放鼠标后调整窗口;

我们把这2种方式的结合起来,就能够弥补2者的缺点,达到实时保持窗口比例的效果。下面我分别列出2者的代码:

处理WM_EXITSIZEMOVE消息代码:

        /// <summary>

        /// 重载窗口资源加载函数

        /// </summary>

        protected override void OnSourceInitialized(EventArgs e)

        {

            base.OnSourceInitialized(e);

            HwndSource source = HwndSource.FromVisual(this) as HwndSource;

            if (source != null)

            {

                // 注册窗口消息处理函数

                source.AddHook(new HwndSourceHook(WinProc));

            }

        }

 

        public const Int32 WM_EXITSIZEMOVE = 0x0232;

        /// <summary>

        ///窗口消息处理函数

        /// </summary>

        private IntPtr WinProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, ref Boolean handled)

        {

            IntPtr result = IntPtr.Zero;

            switch (msg)

            {

                // 窗口消息处理

                case WM_EXITSIZEMOVE:

                    {

                        // 上下拖拉窗口

                        if (this.Height != LastHeight)

                        {

                            this.Width = this.Height * dAspectRatio;

                        }

                        else // 左右拖拉窗口

                        {

                            this.Height = this.Width / dAspectRatio;

                        }

 

                        // 把修改后的窗口大小记录下来

                        LastWidth = (int)this.Width;

                        LastHeight = (int)this.Height;

                        break;

                    }

            }

 

            return result;

        }

    注意:代码和上一篇一致,未修改。

    处理SizeChanged事件代码

        // 声明一个定时器

        private Timer timer;

 

        /// <summary>

        /// SizeChanged事件处理函数

        /// </summary>

        private void Window_SizeChanged(object sender, SizeChangedEventArgs e)

        {

            if (timer != null)

                timer.Dispose();

 

            // 注册定时器处理函数和定时器触发时间

            timer = new Timer(setSize, e, 80, 100);

        }

 

        /// <summary>

        /// 定时器处理函数

        /// </summary>

        private void setSize(object eo)

        {

            SizeChangedEventArgs e = eo as SizeChangedEventArgs;

 

            if (e.WidthChanged) // 左右拖拉窗口

            {

                if (e.NewSize.Height != e.NewSize.Width / dAspectRatio)

                {

                    this.Dispatcher.BeginInvoke(new Action(() => { this.Height = e.NewSize.Width / dAspectRatio; }));

                }

            }

            else if (e.HeightChanged) // 上下拖拉窗口

            {

                if (e.NewSize.Width != e.NewSize.Height * dAspectRatio)

                {

                    this.Dispatcher.BeginInvoke(new Action(() => { this.Width = e.NewSize.Height * dAspectRatio; }));

                }

            }

 

            // 解除定时器

            timer.Dispose();

        }

    注意:这里采用定时器来延迟每一次调整窗口,这是因为直接调整窗口会导致SizeChanged事件触发的太频繁了!!!会导致窗口混乱的,所以我这里采用的定时器,为每一次SizeChanged事件触发延迟了80ms,当然这个事件可以调,不过最好不要小于50ms。

    好了,代码贴完了,让我们来总结一下。这篇主要阐述的就是如何通过SizeChanged事件和WM_EXITSIZEMOVE消息来达到实时保持窗口比例的效果。用SizeChanged事件实时调整窗口比例(在鼠标按下不放的情况),用WM_EXITSIZEMOVE消息来最后定下窗口大小。两者结合使用,从而达到保持窗口比例的效果。不过屏幕可能还有闪烁。

    好了,今天就写到这里了。

nbsp;