wpf центр дочернее окно не работает с sizetocontent - PullRequest
10 голосов
/ 16 ноября 2009

Если я установил SizeToContent на WidthAndHeight, то WindowStartupLocation="CenterOwner" не будет работать должным образом. Вместо того, чтобы центр нового окна находился в центре его родительского владельца, он больше похож на верхний левый угол дочернего окна, который находится в центре родительского. Если я удаляю SizeToContent, тогда все в порядке. Что не так?

Ответы [ 4 ]

9 голосов
/ 16 ноября 2009

Ну, Рэй это блестяще поднял. Проще говоря, он хочет сказать, что вы устанавливаете содержимое элементов управления в своем событии Loaded, которое сбрасывает Height & Width (а также ActualHeight & ActualWidth) после позиционирование окна выполнено.

Чтобы исправить это, у вас есть две альтернативы:

  1. Переместите код настройки значения контента в конструктор или
  2. Добавьте простой метод для пересчета позиции вашего Window в соответствии с Owner и вызовите этот метод в конце вашего Loaded события, например:

...

private void CenterOwner()
{
    if (Owner != null)
    {
        double top = Owner.Top + ((Owner.Height - this.ActualHeight) / 2);
        double left = Owner.Left + ((Owner.Width - this.ActualWidth) / 2);

        this.Top = top < 0 ? 0 : top;
        this.Left = left < 0 ? 0 : left;
    }
}
6 голосов
/ 16 ноября 2009

Когда отображается окно, оно измеряется, затем WindowStartupLocation обрабатывается с использованием ActualWidth и ActualHeight окна, вычисленного процессом измерения.

Поведение, которое вы описываете, говорит мне, что ActualWidth и ActualHeight измеряются либо равными нулю, либо относительно малыми во время вызова Show () или ShowDialog () и только позже устанавливаются в ненулевые значения.

Это может произойти, если, например, содержимое окна построено с использованием DataContext, который установлен только для события Loaded. Когда вызывается Show(), окно еще не было Loaded, поэтому в нем нет данных. Позже, когда происходит событие Loaded, он устанавливает DataContext, и окно обновляет его содержимое, но позиционирование уже произошло.

Существует много других сценариев, например, содержимое, заполненное с помощью вызова Dispatcher.BeginInvoke, или из отдельного потока, или привязки, которые задерживаются или асинхронны.

Обычно вам нужно искать все, что может привести к тому, что содержимое вашего окна будет меньше обычного в момент вызова Show(), и исправить это.

3 голосов
/ 13 мая 2014

Связанное динамическое содержимое обычно визуализируется непосредственно в графическом интерфейсе, но иногда отправляется в графическом интерфейсе. Таймер и другие потоки могут инициировать (MVVM) события изменения свойств. Несомненно, что рендеринг выполняется в ближайшее время, но не гарантируется, поскольку он является приоритетом очереди диспетчера WPF. Таким образом, вы не можете сказать, когда рендеринг закончен, и WPF не может сказать что-либо о порядке обработки, поэтому WPF не может сейчас найти идеальное время для вычисления StartPosition.

Хитрость в том, чтобы ждать, что очередь WPF будет emtpy. Тогда вы уверены, что у WPF есть время обработать ваш код. Это означает, что вы задерживаете вызов ShowDialog для Window.

Поэтому дайте основному потоку GUI все время, которое ему нужно , чтобы выполнять динамические изменения содержимого для MVVM или другие динамические изменения. Не пытайтесь рассчитать позицию вручную, это очень сложно, для поддержки нескольких дисплеев. Попробуйте этот код, чтобы открыть окно, оно открывает окно, только когда WPF завершил все операции .

        win.Dispatcher.Invoke(new Action(() => win.ShowDialog()), DispatcherPriority.ApplicationIdle);
1 голос
/ 16 ноября 2009

Ваш вопрос немного двусмысленный. В каком окне («родительский» или «дочерний») вы устанавливаете SizeToContent и WindowStartupLocation?

Если я создаю второе окно в своем проекте и устанавливаю его SizeToContent и WindowStartupLocation, как вы описываете, я получаю желаемые результаты.

Единственное, о чем я могу думать, что вы можете забыть, - это на самом деле сообщить дочернему окну, кто является его владельцем:

Window2 w = new Window2();
w.Owner = this; // "this" being the parent window
w.ShowDialog();

Или, более кратко:

new Window2 { Owner = this }.ShowDialog();
...