WPF MVVM Как повторно центрировать окно приложения после изменения вида?

#c# #wpf #xaml

#wpf #mvvm #центрирование

Вопрос:

Я работаю над очень простым приложением, использующим собственные WPF и MVVM. Основное представление «оболочки» использует, как я считаю, общий базовый шаблон, в котором он содержит ContentControl, привязанный к данным активной viewmodel, которая вводит представление через шаблоны данных. Это сокращенная версия того, как это выглядит:

 <Window.DataContext>
    <local:ShellViewModel/>
</Window.DataContext>

<Window.Resources>
    <DataTemplate DataType="{x:Type local:DbConfigViewModel}">
        <local:DbConfigView/>
    </DataTemplate>
    <DataTemplate DataType="{x:Type local:AuthenticationViewModel}">
        <local:AuthenticationView/>
    </DataTemplate>
</Window.Resources>

<DockPanel>
    <ContentControl Content="{Binding CurrentViewModel}"/>
</DockPanel>
  

Для этого окна установлен автоматический размер в зависимости от вида, и оно настроено на запуск по центру. Это отлично работает для первоначального представления. Однако некоторые представления намного больше, и это становится проблемой пользовательского интерфейса, когда они становятся активными. Что мне нужно, так это чтобы приложение перецентрировалось при каждом изменении представления.

До сих пор я пытался привязать к данным свойства Left и Top главного окна, например:

 <Window (....)
    Width="auto" Height="auto"
    SizeToContent="WidthAndHeight"
    WindowStartupLocation="CenterScreen"
    Left="{Binding WindowLeft}"
    Top="{Binding WindowTop}">
  

Моя навигация привязана к методу в viewmodel основного окна Windows, поэтому в этом методе после того, как для нового viewmodel установлено свойство CurrentViewModel, я затем вызываю этот метод:

     private void CenterWindow()
    {
        Rect workArea = System.Windows.SystemParameters.WorkArea;
        WindowLeft = (workArea.Width - Application.Current.MainWindow.Width) / 2   workArea.Left;
        WindowTop = (workArea.Height - Application.Current.MainWindow.Height) / 2   workArea.Top;
    }
  

Кажется, что это должно сработать, но, похоже, происходит то, что MainWindow .Ширина и высота еще не были скорректированы, поэтому центрирование выполняется на основе предыдущего представления, а не того, которое я только что создал.

Итак, есть ли какое-то событие или другое место для вызова этого кода, чтобы оно происходило после отображения нового представления? Это вообще правильный подход?

Комментарии:

1. если вы уверены, что размер окна не может быть изменен пользователем, я думаю, вы сможете сделать это, создав переопределение onSizeChanged для главного окна и вызвав там CenterWindow ? Примечание: MSDN описывает WorkArea , как получает размер рабочей области на основном мониторе отображения. таким образом, ваш метод не всегда будет делать то, что вы ожидаете от него в системах с несколькими мониторами

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

3. Можете ли вы показать мне какое- либо профессиональное приложение с таким поведением? Я предполагаю, что вы не можете, и это потому, что это плохая идея. Изменение размера приложения Window (если оно не является дочерним Window ) не должно быть проблемой команды разработчиков. Создайте гибкий пользовательский интерфейс, который максимально подходит для всех видов, и оставьте размер Window на усмотрение пользователя… как и должно было быть . Я бы сказал, что тот факт, что вы задаете этот вопрос, указывает на то, что у вас что-то не так с дизайном вашего пользовательского интерфейса.

4. @Sheridan Хотя я обычно соглашаюсь с вами, это конкретное приложение представляет собой очень простую утилиту, которую я разрабатываю для нашей команды инженерных служб. Основная часть приложения — это единый вид, однако мне нужно, чтобы они прошли через 2 коротких диалоговых окна, прежде чем попасть туда. Вместо того, чтобы использовать традиционные диалоговые службы, я решил, что было бы достаточно просто просто поменять вид, как я показал. Это был быстрый и простой способ завершить задачу, но мне просто нужно решить этот маленький нюанс.

5. Это то, что я не могу понять … гибкий пользовательский интерфейс WPF поместится в любое пространство, предоставленное содержащим Window … кажется, что это то, чего вам на самом деле не хватает, и как вы должны исправить этот маленький нюанс , как вы его называете.

Ответ №1:

Затем вам нужно подписаться на SizeChanged в вашем окне:

 private void Window_SizeChanged(object sender, SizeChangedEventArgs e)
{
    if (e.PreviousSize == e.NewSize)
        return;

    var w = SystemParameters.PrimaryScreenWidth;
    var h = SystemParameters.PrimaryScreenHeight;

    this.Left = (w - e.NewSize.Width) / 2;
    this.Top = (h - e.NewSize.Height) / 2;
}
  

Вы можете использовать события взаимодействия, если хотите, в своей ViewModel

Комментарии:

1. Что делать, если окна нет на основном экране?