Search code examples
wpfdrivermultiple-monitors

Secondary monitor bug: a problem in WPF or in the graphics driver?


I have discovered a strange bug with my WPF application and I am trying to determine whether it is a problem with WPF or my graphics driver so that I can report it to the appropriate company. I have a Quadro FX 1700 with the latest drivers (197.54) on a Windows XP system, running a .NET 3.5 SP1 application.

I have dual monitors, my primary on the left and secondary on the right. The problem occurs when I maximize then restore a child window of the main window on my primary monitor. The child window is sized correctly on the primary monitor, but it is drawn on my secondary monitor as if it were still maximized. Moving the child window around on the primary monitor moves it on the secondary one.

Restored child window is drawn on secondary monitor as if it were still maximized

I made a sample application (code is below) which induces this behavior.

  1. Start the application and ensure the main window is on your primary monitor.
  2. Double-click the main window. A green child window should appear.
  3. Click the green child window to maximize.
  4. Click the green child window to restore.

Can anyone else reproduce this problem? On my system the green child restores, but then it's drawn on both my primary and secondary monitors, rather than just the primary monitor.

App.xaml

<Application
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    x:Class="DualMonitorBug.App"
    StartupUri="Shell.xaml" />

App.xaml.cs

using System.Windows;
namespace DualMonitorBug { public partial class App : Application { } }

Shell.xaml

<Window
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    x:Class="DualMonitorBug.Shell"
    Title="Shell" Height="480" Width="640"
    MouseDoubleClick="ShowDialog" />

Shell.xaml.cs

using System.Windows;
using System.Windows.Input;

namespace DualMonitorBug
{
    public partial class Shell : Window
    {
        public Shell()
        {
            InitializeComponent();
        }

        private void ShowDialog(object sender, MouseButtonEventArgs e)
        {
            DialogWindow dialog = new DialogWindow();
            dialog.Owner = this;
            dialog.Show();
        }
    }
}

DialogWindow.xaml

<Window
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    x:Class="DualMonitorBug.DialogWindow"
    Title="Dialog Window" Height="240" Width="320"
    AllowsTransparency="True"
    Background="Green"
    MouseLeftButtonDown="ShowHideDialog"
    WindowStyle="None" />

DialogWindow.xaml.cs

using System.Windows;
using System.Windows.Input;

namespace DualMonitorBug
{
    public partial class DialogWindow : Window
    {
        public DialogWindow() { InitializeComponent(); }

        private void ShowHideDialog(object sender, MouseButtonEventArgs e)
        {
           if (e.ClickCount == 1)
           {
               if (this.WindowState == WindowState.Normal)
               {
                   this.DragMove();
               }
           }
           else
           {
               this.WindowState
                   = (this.WindowState == WindowState.Normal)
                   ? WindowState.Maximized
                   : WindowState.Normal;
           }
        }
    }
}

Solution

  • OK, I try something today and it seems the problem happens because the maximizing activity extends the width to the second monitor before moving the window to top-left corner. Therefore, when you normalize the window, the extended part is still in the second monitor and will not be cleared until you drag the window to the second monitor.

    To work around this problem, before setting WindowState to Maximized, set Width and Height to zeros first. That will solve the problem. You may want to cache the current Top and Left so that when the window is normalized, it comes back to previous position:

    Maximized code:

     _oldTop = Top;
    _oldHeight = Height;
     Top = Height = 0;
     WindowState = WindowState.Maximized;
    

    Normalized code:

    Top = _oldTop;
    Height = _oldHeight;
    WindowState = WindowState.Normal;