Держите окно по центру после SizeToContent плавно

У меня есть окно WPF, которое со временем меняет свой размер из-за SizeToContent="WidthAndHeight". Первоначально WindowStartupLocation="CenterScreen" показывает окно по центру правильно, и после этого я перецентрирую его с помощью:

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

Но он производит "прыжок", так как размер окна сначала изменяется, а затем центрируется.

Есть ли способ сделать это гладко?

5 ответов

Это сработало для меня:

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;
}

Вместо прямой установки Me.Top и Me.Left вы можете использовать TranslateTransform для анимации изменения позиции.

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);
}

Источник кода: WPF. Самый простой способ программно переместить изображение в (X,Y)?

Спасибо за публикацию этого. Мой сценарий состоит в том, что у меня есть диалоговое окно, размер которого изменяется, когда ViewModel загружен, который определяет его высоту и ширину. Я использовал ваш пример, но немного изменил его, чтобы захватить окно "Владелец", а затем перецентировал.

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);
}

Это решение основано на сочетании решений Имрана и Андраса.

Это было написано в .Net 6.0 с нулевыми ссылочными типами, поэтому для .Net Framework 4.8 (и старше) может потребоваться некоторая настройка.

Этот код в основном удерживает окно в центре его исходного центра, даже когда динамическое содержимое заставляет окно увеличиваться или уменьшаться. Кроме того, вместо того, чтобы прыгать в новое место, он анимируется. В настоящее время я установил его на 0,5 секунды. Что-нибудь медленнее, чем это, и это казалось неуклюжим.

Одна вещь, которую учитывает это решение (и ни одно из других), — это границы экрана. Таким образом, вы можете рассмотреть возможность включения логики, которая A) ограничивает максимальные границы в зависимости от текущего экрана, на котором находится окно, B) сохраняя его «по центру» в текущем центре при изменении размера, не позволяйте краям выходить за пределы экрана граница. Конечно, это гораздо более сложное решение, и вы всегда можете проигнорировать обе эти небольшие проблемы и просто обвинить конечного пользователя, если он позволит этому выйти за пределы своего экрана.

      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);

}
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;
    }
Другие вопросы по тегам