-->

Fix display garbage left by WPF dialog window?

2020-07-27 04:12发布

问题:

In my WPF app, I have WPF windows that can open other WPF dialog windows, which I do as follows:

PickForEveryone PickForEveryoneWindow = new PickForEveryone(sSelRecipe, selMRM.sDay, selMRM.MealTypeID);
PickForEveryoneWindow.Owner = this;
PickForEveryoneWindow.ShowDialog();

Where PickForEveryone is defined as:

public partial class PickForEveryone : Window

and

<Window x:Class="PFWb0.PickForEveryone"
 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 xmlns:dg="http://schemas.microsoft.com/wpf/2008/toolkit"
 ShowInTaskbar="False"
 Title="Pick Recipe For All" Height="536" Width="441" 
 WindowStartupLocation="CenterOwner">

And contains a Grid with a DataGrid and a few buttons and check boxes.

Which works fine on my development computers. However, my client keeps seeing the dialog windows leave part of their display as visual litter on top of the parent window when the dialog window closes. That is, only some of the window undraws when ShowDialog() returns.

I tried adding this.InvalidateVisual(); below the above code, but it didn't solve the problem.

I also saw a suggestion here (for another kind of WPF display problem) to call OnRender() to force a redraw, but OnRender requires a parameter of type DrawingContext, which I don't know how to get.

So, I am asking if anyone knows how to either fix the display problem in the first place, or how to recover from it by getting WPF to redraw a window.

Update: As seen in comments to suggested answers below, I still have no solution that works on my client's computers, and my workaround (of having the windows dodge each other) is no longer enough. The only thing that works is to minimize and maximize the polluted underlying window.

回答1:

I had a similar problem on a specific computer with an ATOM N270 processor. The problem seamed to be linked to the graphic hardware acceleration.

To deactivate the accelaration, just add this to the registery (this will deactivate hardware acceleration for all WPF applications) :

HKEY_CURRENT_USER\SOFTWARE\Microsoft\Avalon.Graphics\DisableHWAcceleration

I had to create the Avalon.Graphics folder.

DisableHWAcceleration is a DWORD that has to be set to 1.

This had solve my problem, if I reactivate the acceleration, the problem come back.

Hope this helps.

References :

  • Guidelines for troubleshooting graphic issues in WPF applications : http://msdn.microsoft.com/en-us/library/aa970912(v=vs.90).aspx
  • Graphics Rendering Registry Settings : http://support.microsoft.com/kb/963021


回答2:

This ugly code works for me:

        void RefreshWindow()
    {
        switch (WindowState)
        {
            case WindowState.Maximized:
                {
                    double oldWidth = Width;
                    Width = System.Windows.SystemParameters.PrimaryScreenWidth - 1;
                    WindowState = System.Windows.WindowState.Normal;
                    WindowState = System.Windows.WindowState.Maximized;
                    Width = oldWidth;
                }
                break;
            case WindowState.Normal:
                if (Width > 1)
                {
                    Width -= 1;
                    Width += 1;
                }
                else
                {
                    Width += 1;
                    Width -= 1;
                }
                break;
            case WindowState.Minimized:
            default:
                // no action necessary
                break;
        }
    }


回答3:

So I have been looking for an answer to this on the MS forums, and apparently, variations of this question have been a asked for a few years now.

Sometimes, they say, the problem has to do with video drivers, of all things, although in my case, my client has recently updated his video drivers.

My impression is that Microsoft thought they designed WPF so that a developer should never need to do such a thing as force a redraw of the display, so they make no way to do so by design. Of course, when things go wrong for whatever reason, this means there is no straightforward way to do so. And the ways that seem like they might do so (such as InvalidateVisual()), don't.

But I did find one hack that does work. Well, two. The ugly one is to tell the window to minimize and return to normal. But that results in a visual animation of it doing so, which is not ideal. In my case, it also made it hide behind other open windows, requiring me to make it go topmost. But is does solve the problem, in a jarring way.

Code after ShowDialog:

    this.WindowState = WindowState.Minimized;
    this.WindowState = WindowState.Normal;
    this.Topmost = true;

The better hack, looks a bit like this:

Code outside:

public delegate void NoArgDelegate();

Code after ShowDialog:

    this.Dispatcher.Invoke(
    System.Windows.Threading.DispatcherPriority.Loaded,
        (NoArgDelegate)delegate {}
    );

Presto ala kazzam!



回答4:

This solution works, but it is not very pretty (easy to see that dialog is minimized and then set to normal).

this.WindowState = WindowState.Minimized;
this.WindowState = WindowState.Normal;
this.Topmost = true;


回答5:

So far nothing I have tried actually works on my client's computer. I have a new fix (workaround hack) attempt in for client testing, which involves moving the window away, and trying to make it actually take effect by launching an empty window just before I close the dialog window. Sigh...