Какое последнее событие сработало при загрузке нового окна WPF/C#?

Я пытаюсь загрузить окно настроек для своего приложения, и я хотел бы, чтобы кнопка "Применить" изначально была отключена, а затем при обновлении настроек кнопка "Применить" снова включается. У меня есть некоторые управляющие данные, связанные с объектом предпочтений, и что происходит, когда после загрузки окна запускаются события выпадающего списка. Есть ли какое-нибудь событие, которое гарантированно произойдет в последний раз после того, как все станет стабильным?

Вот как выглядит мой код (кнопка "Применить" всегда активируется после загрузки окна):

private void Window_Loaded(object sender, RoutedEventArgs e)
{
    _preferencesData = new PreferencesDataContext();
    LayoutRoot.DataContext = _preferencesData;
    ButtonApply.IsEnabled = false;
}

private void ComboBox_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
{
    ButtonApply.IsEnabled = true;
}

Интересно также отметить, что это происходит только с текстовыми полями и комбинированными списками, а не с флажками или радиокнопками.

7 ответов

Решение

Лучшее решение для простой необходимости

Ответ Джозефа - лучшее решение для вашей простой задачи: просто используйте привязку данных и позвольте модели данных справиться с этим.

Ответ на вопрос в изложенном виде

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

Вот как это сделать:

Dispatcher.BeginInvoke(DispatcherPriority.ContextIdle, new Action(() =>
{
  var x = ComputeSomething(1, 2, 3);
  DoSomething(x, "Test");
}));

Все внутри { } будет выполнено, когда WPF завершит все с более высоким приоритетом, чем ContextIdle, который включает все обработчики событий, загруженные события, события ввода, рендеринг и т. Д.

Последовательность событий, когда окно создано и показано

Как и было запрошено, вот последовательность основных событий в WPF при создании и отображении окна:

  1. Конструкторы и геттеры / сеттеры вызываются при создании объектов, включая PropertyChangedCallback, ValidationCallback и т. Д. Для обновляемых объектов и любых объектов, которые наследуются от них.

  2. Когда каждый элемент добавляется в визуальное или логическое дерево, запускается его инициализированное событие, что приводит к тому, что стили и триггеры применяются в дополнение к любой специфичной для элемента инициализации, которую вы можете определить [примечание: инициализированное событие не запускается для листьев в логическом дереве если в его корне нет PresentationSource (например, Window)

  3. Окно и все неразвернутые визуальные элементы в нем измеряются, что вызывает ApplyTemplate для каждого элемента управления, что вызывает дополнительную конструкцию дерева объектов, включая дополнительные конструкторы и геттеры / сеттеры.

  4. Окно и все не свернутые визуальные эффекты на нем расположены

  5. Окно и его потомки (логические и визуальные) получают событие Loaded.

  6. Все привязки данных, которые потерпели неудачу при первой установке, повторяются

  7. Окну и его потомкам предоставляется возможность визуально визуализировать свой контент.

Шаги 1-2 выполняются при создании окна, независимо от того, отображается оно или нет. Другие шаги, как правило, не выполняются до тех пор, пока не появится окно, но они могут произойти раньше, если их запустить вручную.

Событие Window.ContentRendered удовлетворяет моим требованиям.

Я просто сделал то же самое с поведением в приложении Systray WPF.

Однако я не делал это с помощью обработки событий. Я просто связал свойство Enabled моей кнопки со свойством в моей ViewModel и обновлял свойство всякий раз, когда мне нужно было поведение.

Вы можете использовать ManagedSpy, чтобы понять это самостоятельно.

http://msdn.microsoft.com/en-us/magazine/cc163617.aspx

Установка DataContext, скорее всего, вызовет событие SelectionChanged, и вы не можете рассчитывать, когда именно он будет запущен. Некоторая логическая проверка того, что именно выбрано, будет более надежной:

private void ComboBox_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
{
    if (myComboBox.SelectedItem == null) 
    { 
      buttonApply.IsEnabled = false;
    }
    else 
    {
      buttonApply.IsEnabled = true;
    }
}

Причина, по которой это происходит потом с вашим кодом "как есть", заключается в том, что событие помещается в очередь в потоке пользовательского интерфейса, поэтому дело за Windows, если оно выполнит следующую строку кода в Loadили для обработки других событий в очереди.

Не бросать в вас много вещей, с которыми вы можете или не можете быть знакомы, но если это относительно новая кодовая база, вы можете рассмотреть возможность использования шаблона MVVM и использования команд вместо архаичных (выделение минных) событий модель.

Порядок событий в Windows Forms

Control.HandleCreated

Control.BindingContextChanged

Form.Load

Control.VisibleChanged

Form.Activated

Form.Shown

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