遅い→起動時

http://d.hatena.ne.jp/pmint/

WPFでWindowsXPでも使える影付きウィンドウを作ってみた (2)

d:id:pmint:20130808:p1の続き。


設定と取得を完全に分けると震えなくなった。

プロジェクトファイル(VS2010)

WindowMoving3.zip

ClickOnceで実行してみる(InternetExplorer向け)

コード(C#)
using System;
using System.Diagnostics;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Effects;

namespace WindowMoving3
{
    /// <summary>
    /// MainWindow.xaml の相互作用ロジック
    /// </summary>
    public partial class MainWindow : Window
    {
        Thickness _initialBorderThickness = new Thickness(30);
        private DropShadowEffect _effect = new DropShadowEffect();

        // 動きをわかりやすくするための画面端の余白
        Thickness _margin = new Thickness(50);

        public MainWindow()
        {
            InitializeComponent();
            this.WindowStyle = WindowStyle.None;
            this.BorderThickness = new Thickness(30);
            this.AllowsTransparency = true;

            this.MouseDown += new MouseButtonEventHandler(Window_MouseDown);
            this.MouseUp += new MouseButtonEventHandler(Window_MouseUp);
            this.MouseMove += new MouseEventHandler(Window_MouseMove);
            this.MouseDoubleClick += new MouseButtonEventHandler(Window_MouseDoubleClick);
            this.StateChanged += new EventHandler(Window_StateChanged);
            this.Activated += new EventHandler(Window_Activated);
            this.Deactivated += new EventHandler(Window_Deactivated);

            {
                _effect.Color = Colors.Black;
                _effect.BlurRadius = 20;
                _effect.ShadowDepth = 0;
                _effect.Opacity = 0.25;
                _effect.RenderingBias = RenderingBias.Performance;
            }
            {
                // 動きがわかりやすくなる枠線
                //this.BorderBrush = new SolidColorBrush(Color.FromArgb(128, 255, 255, 255));
            }
        }

        void Window_Activated(object sender, EventArgs e)
        {
            this.Effect = _effect;
        }

        void Window_Deactivated(object sender, EventArgs e)
        {
            this.Effect = null;
        }

        void Window_MouseDoubleClick(object sender, MouseButtonEventArgs e)
        {
            if (e.ChangedButton == MouseButton.Left)
                this.WindowState = (this.WindowState != WindowState.Maximized) ? WindowState.Maximized : WindowState.Normal;
        }

        void Window_StateChanged(object sender, EventArgs e)
        {
            this.BorderThickness = (this.WindowState == WindowState.Maximized) ? new Thickness(0) : this._borderThickness;
        }

        private Thickness _borderThickness;
        private Size _windowSize;
        private Point _windowOrigin;
        private Point _dragStart;
        void Window_MouseDown(object sender, MouseButtonEventArgs e)
        {
            this._borderThickness = this.BorderThickness;

            var decrease = new Thickness(
                this._borderThickness.Left - this._initialBorderThickness.Left,
                this._borderThickness.Top - this._initialBorderThickness.Top,
                this._borderThickness.Right - this._initialBorderThickness.Right,
                this._borderThickness.Bottom - this._initialBorderThickness.Bottom);
            
            this._windowSize = this.RenderSize;
            {
                this._windowSize.Width -= decrease.Left + decrease.Right;
                this._windowSize.Height -= decrease.Top + decrease.Bottom;
            }
            
            this._windowOrigin = this.RestoreBounds.TopLeft;
            {
                this._windowOrigin.X += decrease.Left;
                this._windowOrigin.Y += decrease.Top;
            }
            
            this._dragStart = this.PointToScreen(e.GetPosition(this));
            
            e.MouseDevice.Capture(this);
        }

        void Window_MouseUp(object sender, MouseButtonEventArgs e)
        {
            this._windowSize = default(Size);
            this._windowOrigin = default(Point);
            this._borderThickness = default(Thickness);
            this._dragStart = default(Point);
            
            e.MouseDevice.Capture(this, CaptureMode.None);
        }

        private void Window_MouseMove(object sender, MouseEventArgs e)
        {
            if (e.LeftButton == MouseButtonState.Pressed && this.WindowState != WindowState.Maximized)
            {
                // この中でWindow.RestoreBoundsなど(例えばWindow.Left)を参照・変更すると、変更が累積してしまうので不可。変更のみにする。

                Debug.Assert(_dragStart != default(Point));

                var originDelta = new Vector();
                {
                    var pos = this.PointToScreen(e.GetPosition(this));  // Window上の座標のままではthis.Left, this.Topを参照することになるので、スクリーン上の座標に変換して使用する。
                    originDelta.X = pos.X - _dragStart.X;
                    originDelta.Y = pos.Y - _dragStart.Y;
                    Debug.WriteLine(new { originDelta });
                }

                var bounds = new Rect();
                {
                    bounds.X = this._windowOrigin.X + originDelta.X;
                    bounds.Y = this._windowOrigin.Y + originDelta.Y;
                    bounds.Width = this._windowSize.Width - (this._borderThickness.Left - this._initialBorderThickness.Left) + (this._borderThickness.Right - this._initialBorderThickness.Right);
                    bounds.Height = this._windowSize.Height;
                    Debug.WriteLine(new { bounds.Left, bounds.Top, bounds.Right, bounds.Bottom, bounds.Width, bounds.Height });
                }

                var over = new Thickness();
                {
                    over.Left = Math.Min(this._initialBorderThickness.Left, -Math.Min(0, bounds.Left - (SystemParameters.WorkArea.Left + _margin.Left)));
                    over.Top = Math.Min(this._initialBorderThickness.Top, -Math.Min(0, bounds.Top - (SystemParameters.WorkArea.Top + _margin.Top)));
                    over.Right = Math.Min(this._initialBorderThickness.Right, Math.Max(0, bounds.Right - (SystemParameters.WorkArea.Right - _margin.Right)));
                    over.Bottom = Math.Min(this._initialBorderThickness.Bottom, Math.Max(0, bounds.Bottom - (SystemParameters.WorkArea.Bottom - _margin.Bottom)));
                    Debug.WriteLine(new { over });
                }

                var sizeDelta = new Vector();
                {
                    sizeDelta.X = -(over.Left + over.Right);
                    sizeDelta.Y = -(over.Top + over.Bottom);
                }

                var thickness = new Thickness(this._initialBorderThickness.Left, this._initialBorderThickness.Top, this._initialBorderThickness.Right, this._initialBorderThickness.Bottom);
                {
                    thickness.Left -= over.Left;
                    thickness.Right -= over.Right;
                    thickness.Top -= over.Top;
                    thickness.Bottom -= over.Bottom;
                    Debug.WriteLine(new { thickness });
                }

                this.BorderThickness = this._borderThickness = thickness;
                this.Left = bounds.X + over.Left;
                this.Top = bounds.Y + over.Top;
                this.Width = this._windowSize.Width + sizeDelta.X;
                this.Height = this._windowSize.Height + sizeDelta.Y;

                Debug.WriteLine(new { this.Left, this.Top, this.Width, this.Height, this.BorderThickness });
            }
        }
    }
}

ウィンドウリサイズも独自にやるしかないみたい。ウィンドウ枠や右下のリサイズグリップを使うとWindowsがサイズ調整前のウィンドウを描画してくれるのでまた震えてしまう。描画するのはサイズ調整後だけでいい。