Ashish Sapkale
Ashish Sapkale

Reputation: 550

WPF Window move flickers on Restriction

I have WPF borderless Transparent window.

By using this.DragMove(); I can successfully move the window.

I wanted to restrict window within screen area. It's also working using below snippet.

    private void Window_LocationChanged(object sender, EventArgs e)
            {
                CheckBounds();
    }

private void CheckBounds()
        {
            var height = System.Windows.SystemParameters.PrimaryScreenHeight;
            var width = System.Windows.SystemParameters.PrimaryScreenWidth;

            if (this.Left < 0)
                this.Left = 0;
            if (this.Top < 0)
                this.Top = 0;
            if (this.Top + this.Height > height)
                this.Top = height - this.Height;
            if (this.Left + this.Width > width)
                this.Left = width - this.Width;
        }

But using above code, whenever window reach its max bounds using mouse drag, It starts flickering.

Could anybody suggest how to avoid this flickering?

Upvotes: 1

Views: 776

Answers (1)

Xavier
Xavier

Reputation: 3424

The best way I know to deal with this issue is to handle the WM_MOVING windows message in your window and adjust the position there. Since the WM_MOVING message is received before the window actually moves and allows the position to be modified, you never see any jitter. Here is an example code-behind for a Window.

using System;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Interop;
public partial class MainWindow : Window
{
    private HwndSource mSource;

    public MainWindow()
    {
        InitializeComponent();
    }

    protected override void OnSourceInitialized(EventArgs e)
    {
        base.OnSourceInitialized(e);

        mSource = (HwndSource)PresentationSource.FromVisual(this);
        mSource.AddHook(WndProc);
    }

    protected override void OnClosed(EventArgs e)
    {
        mSource.RemoveHook(WndProc);
        mSource.Dispose();
        mSource = null;

        base.OnClosed(e);
    }

    private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
    {
        if (msg == (int)WindowsMessage.WM_MOVING)
        {
            // TODO: Substitute realistic bounds
            RECT bounds = new RECT() { Left = 0, Top = 0, Right = 1000, Bottom = 800 };

            RECT window = (RECT)Marshal.PtrToStructure(lParam, typeof(RECT));
            if (window.Left < bounds.Left)
            {
                window.Right = window.Right + bounds.Left - window.Left;
                window.Left = bounds.Left;
            }
            if (window.Top < bounds.Top)
            {
                window.Bottom = window.Bottom + bounds.Top - window.Top;
                window.Top = bounds.Top;
            }
            if (window.Right >= bounds.Right)
            {
                window.Left = bounds.Right - window.Right + window.Left - 1;
                window.Right = bounds.Right - 1;
            }
            if (window.Bottom >= bounds.Bottom)
            {
                window.Top = bounds.Bottom - window.Bottom + window.Top - 1;
                window.Bottom = bounds.Bottom - 1;
            }
            Marshal.StructureToPtr(window, lParam, true);

            handled = true;
            return new IntPtr(1);
        }

        handled = false;
        return IntPtr.Zero;
    }
}

Here are the helper objects that are used in the code:

[StructLayout(LayoutKind.Sequential)]
struct RECT
{
    public int Left, Top, Right, Bottom;
}

enum WindowsMessage
{
    WM_MOVING = 0x0216
}

P.S. The LocationChanged event (and associated OnLocationChanged override) is called in response to WM_MOVE, which is not called until the window has already moved. There does not seem to be a corresponding OnLocationChanging event.

Upvotes: 1

Related Questions