Paul Mrozowski
Paul Mrozowski

Reputation: 6734

Shrinking window height using WPF animation stops at 16 pixels before finishing

I have a simple form that I want to shrink vertically using an animation. It works OK, except when the height of the form is approx. 16 pixels in height it "stops", then the animation completes (350 to 16, stops, then animation completes a brief moment later).

I see the same "jump" if I try the animation in the opposite direction (0 to 350, for example, it seems to wait, then immediately displays at 16 pixels and the animation continues to 350).

Here's a simple example which shows the issue:

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        WindowStyle="None" Background="Transparent" AllowsTransparency="True" Padding="0"
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <Storyboard x:Key="HideWindow">
            <DoubleAnimation Duration="0:0:3.8" 
                             Storyboard.TargetProperty="Height" 
                             From="350" 
                             To="0" 
                             AccelerationRatio=".1">
            </DoubleAnimation>
        </Storyboard>
    </Window.Resources>
    <Grid Background="Red">
        <Button Click="Button_Click" Width="50" Height="20">Hide</Button>                
    </Grid>
</Window>

Then in the .cs file for the window:

private void Button_Click(object sender, RoutedEventArgs e)
{
    var hide = FindResource("HideWindow") as Storyboard;
    if (hide != null)
    {
        hide.Completed += (s, ea) => Close();
        hide.Begin(this, true);
    }
}

It seems as if there is some internal border it's still trying to respect, even though I have it shut off.

I've tried a number of different combinations of borders, margins, etc. and can't seem to get it working nicely.

Upvotes: 1

Views: 242

Answers (1)

Kcvin
Kcvin

Reputation: 5163

I believe this is an "as-design" feature, as the OS with dictate the actual minimum and maximum sizes, even if you try to change it to be smaller. I believe it comes down to the "minimum tracking size," which is a private field in the Window class and is device dependent. Take a look at Window.GetWindowMinMax.

I reproduced your situation on my laptop, however my window stopped shrinking at 14 pixels. I also noticed that all the content will shrink if you set the WindowStyle to normal. I think you might have to do something with WM_GETIMINMAXINFO and some PInvoke in order to change the tracking size; or make your own window control.

My definition/thoughts on tracking size is that a Window needs to have some size/content to it in order for a user to actually re-position, re-size, etc., in other words track, the window. Microsoft probably doesn't want that, its not user-friendly. Set the window style to normal, and do the same thing, and you'll see the heights of the content in the window will all be set to 0; and that's possible because there is still a way to track the window.

Another interesting thing I've found is, execute your application from the bin folder (not Visual Studio), fire the animation (with the Close() event commented out), and when the animation is done, go to the task bar, right click on the window, and click Maximize. On my machine, the application crashes--I'm not sure why.

Another thing to observe is that when you start the application and use Snoop to inspect the controls, you can change the Window.Height to anything you want and it will adjust. Once you fire your animation, the Height changes to something like 5.xxxxxxxxE-14, and ActualHeight does not match. After the animation, if you try to change Window.Height in Snoop, it will not take affect for whatever reason.

Since setting Height seems to be restricted my .NET and the device, I'm not sure that's the easiest solution. Another approach you try is a ScaleTransform and optionally mix the Height animation. Here is the XAML I used:

<Window.Resources>
    <Storyboard x:Key="HideWindow">
        <DoubleAnimation Duration="0:0:3.5" 
                         BeginTime="0:0:0"
                         Storyboard.TargetProperty="Height"
                         From="350"
                         To="0">
        </DoubleAnimation>
        <DoubleAnimation Storyboard.TargetProperty="(Window.RenderTransform).(ScaleTransform.ScaleY)"  
                         To="0"
                         BeginTime="0:0:3.3"
                         Duration="0:0:0.3">
        </DoubleAnimation>
    </Storyboard>
</Window.Resources>
<Window.RenderTransform>
    <ScaleTransform ScaleY="1"/>
</Window.RenderTransform>

This will first change the Height to go to minimum, then you can continue to "close" the window by scaling the window down to 0. You could also scale in both X and Y directions, or just Y, and animate the window closing.

Upvotes: 2

Related Questions