Keep window centered after SizeToContent smoothly
Asked Answered
N

5

13

I have a WPF window that changes it's size over time due to SizeToContent="WidthAndHeight". Initially the WindowStartupLocation="CenterScreen" shows the window centered correctly, and after that I recenter it with:

Private Sub Window_SizeChanged(ByVal sender As Object, ByVal e As System.Windows.SizeChangedEventArgs) Handles Me.SizeChanged
  Me.Top = (SystemParameters.WorkArea.Height - e.NewSize.Height) / 2
  Me.Left = (SystemParameters.WorkArea.Width - e.NewSize.Width) / 2
End Sub

But it produces a "jump" as the window is resized first and centered after.

Is there any way of doing it smoothly?

Nadene answered 9/5, 2013 at 6:40 Comment(0)
I
8

This worked for me:

protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo)
{
    base.OnRenderSizeChanged(sizeInfo);

    //Calculate half of the offset to move the form

    if (sizeInfo.HeightChanged)
        this.Top += (sizeInfo.PreviousSize.Height - sizeInfo.NewSize.Height) / 2;

    if (sizeInfo.WidthChanged)
        this.Left += (sizeInfo.PreviousSize.Width - sizeInfo.NewSize.Width) / 2;
}
Inseminate answered 23/4, 2015 at 11:55 Comment(0)
E
0

Instead of setting Me.Top and Me.Left directly you can use a TranslateTransform to animate position change.

public static void MoveTo(this UIElement target, double newX, double newY)
{
    var top = Canvas.GetTop(target);
    var left = Canvas.GetLeft(target);
    TranslateTransform trans = new TranslateTransform();
    target.RenderTransform = trans;
    DoubleAnimation anim1 = new DoubleAnimation(top, newY - top, TimeSpan.FromSeconds(10));
    DoubleAnimation anim2 = new DoubleAnimation(left, newX - left, TimeSpan.FromSeconds(10));
    trans.BeginAnimation(TranslateTransform.XProperty,anim1);
    trans.BeginAnimation(TranslateTransform.YProperty,anim2);
}

Code source: WPF. Easiest way to move Image to (X,Y) programmatically?

Euphemiah answered 11/11, 2013 at 14:34 Comment(1)
I didn't vote this down because technically it works. However, I'm not sure anyone would want to animate a newly resized window re-centering itself. A much cooler thing to animate would be resizing with an origin point of 0.5, 0.5. The window would keep being centered at all times and just span accross the desired size.Deferral
L
0

Thanks for posting this. My scenario is that I have a Dialog Window that resized when the ViewModel loaded which specifies its Height and Width. I used your example but modified it a bit to capture the Owner Window then I recenter.

protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo)
{
    base.OnRenderSizeChanged(sizeInfo);

    //Calculate half of the owner to move the form
    Window owner = Owner as Window;

    this.Top = (owner.Height / 2) - (this.Height / 2);
    this.Left = (owner.Width / 2) - (this.Width / 2);
}
Lafond answered 28/8, 2018 at 18:57 Comment(0)
T
0

This solution is based on a combination of Imran's solution and Andras's solution.

This was written in .Net 6.0 with nullable reference types - so some tweaking might be necessary for .Net Framework 4.8 (and older).

This code basically keeps the window centered on it's original center even when the dynamic content causes the window to grow or shrink. Additionally, instead of jumping to the new location, it animates to it. Currently I have it set to 0.5 seconds. Anything slower than that and it felt clunky.

One thing this solution (and none of the others) account for is screen bounds. So you might want to consider putting in logic that A) caps max bounds based on the current screen the window is on, B) while keeping it 'centered' on it's current center on resize, don't let it edges go off a screen bound. Course that's a much more complex solution, and you could always ignore both of those slight problems and just blame the end user if they let it go outside their screen bounds.

protected override void OnRenderSizeChanged(SizeChangedInfo info)
{
    base.OnRenderSizeChanged(info);

    // This starts as false, and gets set to true in the Window.Loaded event.
    // It gets set back to false during the window closing event.
    // The window I am using this on has the min/max buttons disabled.
    // If you allow min/max, you might want to override the window state changed event and 
    // set this to false there as well.
    if (!this.canAnimate) return;

    DoubleAnimation? animT = null;
    DoubleAnimation? animL = null;

    if (info.HeightChanged)
        animT = new
                (
                    this.Top,
                    this.Top + ((info.PreviousSize.Height - info.NewSize.Height) / 2),
                    TimeSpan.FromSeconds(0.5)
                )
        {
            // !Important: Animation coerces the dependency property values. If you don't
            // specify Stop as the fill behavior, the coerced property will always report
            // the wrong value if you access it directly. IE, let's say the window is at
            // Y = 100, and the growth animation is going to cause it to be at 90.
            // The user then moves the window and now the true value is 150. When 
            // accessing this.Top the value will constantly be stuck at 90 - that is if you
            // don't specify FillBehavior = Stop.
            FillBehavior = FillBehavior.Stop
        };

    if (info.WidthChanged)
        animL = new
                (
                    this.Left,
                    this.Left + ((info.PreviousSize.Width - info.NewSize.Width) / 2),
                    TimeSpan.FromSeconds(0.5)
                )
        {
            FillBehavior = FillBehavior.Stop
        };

    if (animT is not null)
        this.BeginAnimation(TopProperty, animT);

    if (animL is not null)
        this.BeginAnimation(LeftProperty, animL);

}
Terminator answered 23/4, 2022 at 22:48 Comment(0)
M
-2
private void Window_SizeChanged(object sender, SizeChangedEventArgs e)
    {
        if (e.HeightChanged)
            Top += (e.PreviousSize.Height - e.NewSize.Height) / 2;
        if (e.WidthChanged)
            Left += (e.PreviousSize.Width - e.NewSize.Width) / 2;
    }
Moxa answered 12/6, 2019 at 14:57 Comment(2)
Can you explain that further? What does that code do? What makes you think that it solves the original question?Catling
This code is used to recenter your window each time it is resized.Moxa

© 2022 - 2024 — McMap. All rights reserved.