Держите окно по центру после 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;
}