Приложение WPF полностью теряет фокус при закрытии окна

Описание проблемы

Если я сделаю немодальное окно в качестве дочернего окна, установив для владельца окна родительское окно, а затем покажу MessageBox из этого дочернего окна, родительское окно потеряет фокус, если я закрою дочернее окно. Если Windows Explorer или другое приложение открыто, это приложение получит фокус, и мое главное окно будет скрыто.

Кажется, это известная проблема, как я видел в других группах новостей, но я не нашел хорошего решения. Установка владельца на ноль в OnDeactivate не вариант. Установка владельца перед показом MessageBox на ноль и сброс после этого не помогает. Установка нулевого значения для владельца в событии OnClosed также не помогает.

Найдено простое решение

Если у вас возникла та же проблема, что я описал, поместите следующий код в закрытие всех дочерних окон.

void  OnClosing(System.ComponentModel.CancelEventArgs e)
  base.OnClosing(e);
  if (null != Owner) {
         Owner.Activate();
  }
  // ....

За ним может следовать любая дальнейшая логика обработки, допускается даже открытие MessageBoxes.

Пример-код

Проблема, кажется, намного больше, чем я думал. В следующем примере будет удален фокус родительского окна, если будет открыто окно сообщения, а дочернее окно будет закрыто (скопируйте код в загруженный обработчик события окна).

Window firstChildWindow = new Window() {
    Title = "Floating Window", Width = 100, Height = 70
};
firstChildWindow.Owner = Window.GetWindow(this);

Button button = new Button() { Content="MessageBox"};
button.Click += delegate { 
    MessageBox.Show("Klicking her breaks the focus-chain."); };
firstChildWindow.Content = button;
firstChildWindow.Show();

Также этот пример разрывает фокус-цепочку:

Window firstChildWindow = new Window() {
    Title = "Floating Window", Width = 100, Height = 70
};
firstChildWindow.Owner = Window.GetWindow(this);
firstChildWindow.Show();

Window secondChildWindow = new Window() { 
    Title="Second Window",Width=100,Height=70};
secondChildWindow.Content = new TextBlock() { 
    Text="SecondWindow"};
secondChildWindow.Owner = firstChildWindow;
secondChildWindow.Show();

Есть кто-то решение этой проблемы. Я думаю о хаке, чтобы инициировать предоставление фокуса родителю после закрытия, с помощью Dispatcher или DispachterTimer, или, возможно, это будет работа по ручному принудительному фокусированию на родителе при закрытии, но это все мне кажется очень нечистым (и также немного сложным, если есть более активные окна, принадлежащие тому же родителю, что и в моем текущем приложении).

Никто не знает аккуратного решения этого?

Ресурсы

Описание MSDN (см. Примечания для немодальных окон, открытых с вызовом Show())

Та же проблема на форумах msdn без соответствующего решения

Пожалуйста, смотрите также: Нестабильный фокус приложений WPF

7 ответов

Решение

Скорее всего, здесь у вас есть два независимых окна верхнего уровня и одно из них закрыто. Это иногда заставит фокус перейти к другому приложению.

Это также происходит, если у одного окна есть второе, а у третьего - третье. Вероятная ошибка в Win32 API, но она была с нами навсегда, так что удачи в ее исправлении.

Обходной путь - вручную передать фокус обратно ребенку в событии закрытия внука. Но в этом случае внук - это окно сообщений, поэтому вы не можете этого сделать. Самое простое, что нужно сделать - это привязать ящик сообщений к родителю. Следующий простой способ - создать свой собственный элемент управления почтовым ящиком.

Это очень раздражающая ошибка, наиболее распространенное решение:

вызов Me.Owner.Focus в Window_Unloaded или же Window_Closing событие.

Но это все равно не работает на 100%, вы все еще видите, как фоновое окно мигает на передний план (очень кратко), прежде чем фокус будет восстановлен правильно.

Я нашел метод, который работает лучше:

вызов Me.Owner.Activate до Me.Close (или в _Closing событие).

2020 год и все еще в беде...

Обходной путь для Wpf:

    public ContactsWindow()
    {
        InitializeComponent();

        Closing += ContactsWindow_Closing;
    }

    private void ContactsWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e)
    {
        Owner?.Activate();
    }

Вроде работает у меня, интересуюсь любыми новостями

Может быть, я опоздал по этому вопросу.
@HCL эту проблему можно решить проще, если для параметра options MessageBox.show() установить значение MessageBoxOptions.None.

Приветствия...

У меня была похожая проблема. У меня было одно главное окно, где остальные дочерние окна были назначены этому окну через свойство Owner. Если в главном окне было два или более дочерних элемента, после закрытия второго дочернего элемента приложение теряет фокус. Я использовал трюк с child.Owner = null перед закрытием, но затем мое следующее дочернее окно теряет фокус. Я справился с этим, собрав всех детей и установив фокус на последний, прежде чем закрывать ребенка. Вот пример кода:

private void OnClosing(object sender, CancelEventArgs cancelEventArgs)
        {
            var childWindows = (sender as Window).Owner.OwnedWindows;
            if (childWindows.Count - 2 >= 0)
                childWindows[childWindows.Count - 2].Focus();
            else
            {
                (sender as Window).Owner.Focus();
            }
            (sender as Window).Owner = null;
        }

Решение Johsua.Activate (см. Выше) прекрасно работает со всеми типами окон. По крайней мере, это помогло мне.

Право собственности не имеет никакого отношения к тому, какое окно активации. Чтобы увидеть это, измените владельца второго окна на верхнее окно и запустите программу. Обратите внимание, что ничего не меняется. Право собственности связано с "Если окно свернуто, то что нужно свернуть другим окнам". Таким образом, вы застряли с использованием пользовательского кода активации.

Вы можете написать свой собственный ящик сообщений, вы можете реализовать его как всплывающее окно, чтобы отключить весь экран, когда отображается.

Вы проверяете образец caliburn mvvm frameork, есть хорошая реализация messagebox как сервиса.

Другие вопросы по тегам