Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does minimizing and restoring a Window has an effect on already drawn graphics in WPF?

I have a very rudimentary test application only consisting of a Window and a CustomControl. The window contains the custom control and has the following additional attributes:

TextOptions.TextFormattingMode="Display"
SnapsToDevicePixels="True" 
UseLayoutRounding="True"
RenderOptions.EdgeMode="Aliased"

The custom control has these 5 lines added to the code-behind file:

protected override void OnRender(DrawingContext drawingContext)
{
    var pen = new Pen(Brushes.Black, 1);
    drawingContext.DrawLine(pen, new Point(0, 0), new Point(ActualWidth, ActualHeight)); 
}

Everything works great. A black 1px line is draw diagonally from the upper left to the bottom right corner. Perfect. Admittedly pixelated, but exactly what I need. However, when I minimize the window and restore it again the line gets "blurred". This happens without the OnRender method getting called again after the window has been restored. So why do drawn graphics fundamentally change after the window was minimized (loosing focus doesn't seem to be the problem)? Why is everything back to normal after the window is simply resized and how can I take care of this unwanted behavior? Forcing a complete resize/invalidate each time the WindowState changes seems a little excessive, no?

Update 1:

As @Backlash suggested I put the custom control inside a canvas

<Canvas Name="CanvasControl" SizeChanged="FrameworkElement_OnSizeChanged">
  <wpfTest:CustomControl x:Name="ChildControl" />
</Canvas>

and handled the SizeChanged event like this

private void FrameworkElement_OnSizeChanged(object sender, SizeChangedEventArgs e)
{
    ChildControl.Width = CanvasControl.ActualWidth;
    ChildControl.Height = CanvasControl.ActualHeight;
}

since elements inside a canvas are not automatically layouted. However, the results did not change.

Update 2:

One idea that crossed my mind was listening to changes to the window state and force a resize - and therefore a complete redraw - of the window and all its controls by slightly changes the Width or Height property of the window:

protected override void OnStateChanged(EventArgs e)
{
        if (WindowState != WindowState.Minimized)
        {               
            Width += 1;
        }
}

This works in some circumstances (although not when the Window is maximized for example), but generally speaking this is a blatantly bad approach for a number of reasons. I tried to understand what causes a (correct) redraw when the size is changed and it might have something to do with CoreFlags.AreTransformsClean, but since everything is either declared internal or privat I gave up on using those flags and any of the associated methods.

Update 3:

Given that WPF seems to be helplessly overburdened with drawing straight lines directly on the screen, I tried drawing into a in-memory bitmap first and than paint the bitmap onto the screen. And it works. Here is the method:

protected override void OnRender(DrawingContext drawingContext)
{
    var rtb = new RenderTargetBitmap((int)ActualWidth, (int)ActualHeight, 96, 96, PixelFormats.Pbgra32);
    var line = new Line();
    RenderOptions.SetEdgeMode(line, EdgeMode.Aliased);

    line.Stroke = Brushes.Black;
    line.StrokeThickness = 1;
    line.X1 = 0;
    line.X2 = ActualWidth;
    line.Y1 = 0;
    line.Y2 = ActualHeight;
    line.Arrange(new Rect(new Size((int)ActualWidth, (int)ActualHeight)));
    rtb.Render(line);

    drawingContext.DrawImage(rtb, new Rect(0, 0, (int)ActualWidth, (int)ActualHeight));
}

The "solution" is hacky to say the least. It's really bad, but compared to the rest I have seen so far from WPF this might be a legit approach. However, will leave the thread open for some further answers.

like image 964
UnclePaul Avatar asked Jan 04 '13 02:01

UnclePaul


People also ask

How do I close a window in WPF?

Pressing ALT + F4 . Pressing the Close button.

How do I know if a WPF window is open?

In WPF there is a collection of the open Windows in the Application class, you could make a helper method to check if the window is open. Here is an example that will check if any Window of a certain Type or if a Window with a certain name is open, or both. Show activity on this post. Show activity on this post.

What is a WPF page?

Windows Presentation Foundation (WPF) supports browser-style navigation that can be used in two types of applications: standalone applications and XAML browser applications (XBAPs). To package content for navigation, WPF provides the Page class.


1 Answers

I think this is because you are using

SnapsToDevicePixels="True" 

Pixel snapping is not always exactly accurate according to the Microsoft msdn

Unfortunately, simply adjusting the container object's size does not guarantee device pixel alignment. The entire layout of an application can have an affect on the alignment of the image. The display dots per inch (dpi) also affects image alignment. In the example above, the image alignment will only work if the display is set to 96 dots per inch (dpi). At any other setting, the layout needs to be adjusted to accommodate the display settings. High frequency images should be avoided whenever possible in Windows Presentation Foundation (WPF) applications.

So try to change that flag to false and see what it does

like image 173
Pic Mickael Avatar answered Oct 04 '22 19:10

Pic Mickael