简体   繁体   English

加载新的WPF / C#窗口时要触发的最后一个事件是什么?

[英]What is the last event to fire when loading a new WPF/C# window?

I am trying to load a preferences window for my application and I would like the apply button to initially be disabled, then when a preference is updated, the apply button gets enabled again. 我正在尝试为我的应用程序加载首选项窗口,我希望最初禁用“应用”按钮,然后在更新首选项时,再次启用“应用”按钮。 I have some controls data bound to a preferences object and what happens is that after the window loads, the combobox events get triggered. 我有一些绑定到首选项对象的控件数据,发生的是窗口加载后,组合框事件被触发。 Is there any event that is guaranteed to happen dead last after everything is stable? 在一切稳定之后,是否有任何事件可以保证在最后发生?

Here is what my code looks like (the apply button is always enabled after the window loads): 这是我的代码的样子(窗口加载后始终启用应用按钮):

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;
}

Is it also interesting to note that this only happens with textboxes and comboboxes, not checkboxes or radiobuttons. 是否有趣的是,这只发生在文本框和组合框中,而不是复选框或单选按钮。

Best solution for simple need 简单需求的最佳解决方案

Joseph's answer is the best solution by far for your simple need: Just use data binding and let the data model handle it. 对于您的简单需求,Joseph的答案是迄今为止最好的解决方案:只需使用数据绑定并让数据模型处理它。

Answer to question as posed 回答提出的问题

There are more complex scenarios when you really do need control after absolutely everything has finished loading and all events have fired. 在完全加载所有事件并且所有事件都被触发之后,当你确实需要控制时,有更复杂的场景。 There is no single event that occurs "dead last", but it is easy to effectively roll your own using the Dispatcher queue. 没有单个事件发生在“死后”,但使用Dispatcher队列很容易有效地滚动自己的事件。

This is how to do it: 这是怎么做的:

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

Everything inside the { } will be executed when WPF finishes everything at a higher priority than ContextIdle, which includes all event handlers, loaded events, input events, rendering, etc. 当WPF以比ContextIdle更高的优先级完成所有内容时,{}内的所有内容都将被执行,后者包括所有事件处理程序,加载的事件,输入事件,呈现等。

Sequence of events when a Window is created and shown 创建和显示窗口时的事件序列

As requested, here is the sequence of major events in WPF when a window is created and shown: 根据要求,这是在创建和显示窗口时WPF中的主要事件序列:

  1. Constructors and getters/setters are called as objects are created, including PropertyChangedCallback, ValidationCallback, etc on the objects being updated and any objects that inherit from them 在创建对象时调用构造函数和getter / setter,包括对正在更新的对象的PropertyChangedCallback,ValidationCallback等以及从它们继承的任何对象

  2. As each element gets added to a visual or logical tree its Intialized event is fired, which causes Styles and Triggers to be found applied in addition to any element-specific initialization you may define [note: Initialized event not fired for leaves in a logical tree if there is no PresentationSource (eg Window) at its root] 当每个元素被添加到可视或逻辑树时,其Intialized事件被触发,这导致除了您可能定义的任何特定于元素的初始化之外还应用样式和触发器[注意:逻辑树中的叶子未触发初始化事件如果根源没有PresentationSource(例如Window)

  3. The window and all non-collapsed Visuals on it are Measured, which causes an ApplyTemplate at each Control, which causes additional object tree construction including more constructors and getters/setters 窗口和所有未折叠的Visuals都是Measured,这会在每个Control上导致ApplyTemplate,这会导致其他对象树构造,包括更多构造函数和getter / setter

  4. The window and all non-collapsed Visuals on it are Arranged 窗口和所有未折叠的Visuals都是Arranged

  5. The window and its descendants (both logical and visual) receive a Loaded event 窗口及其后代(逻辑和可视)接收Loaded事件

  6. Any data bindings that failed when they were first set are retried 任何首次设置失败的数据绑定都将重试

  7. The window and its descendants are given an opportunity to render their content visually 窗口及其后代有机会在视觉上呈现其内容

Steps 1-2 are done when the Window is created, whether or not it is shown. 无论是否显示Window,都会在创建Window时完成步骤1-2。 The other steps generally don't happen until a Window is shown, but they can happen earlier if triggered manually. 其他步骤通常在窗口显示之前不会发生,但如果手动触发它们可能会更早发生。

I just did kind of the same thing behaviorly in a systray WPF app. 我只是在systray WPF应用程序中表现出同样的行为。

However, I didn't do it using event handling. 但是,我没有使用事件处理。 I simply bound the Enabled property of my button to a property in my ViewModel, and had the property updated whenever I needed the behavior. 我只是将我的按钮的Enabled属性绑定到我的ViewModel中的属性,并在需要该行为时更新属性。

Window.ContentRendered事件满足了我的要求。

You can use ManagedSpy to figure this out on your own. 您可以使用ManagedSpy自行解决这个问题。

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

Setting the DataContext will likely fire the SelectionChanged event, and you can't rely on when exactly it's fired. 设置DataContext可能会触发SelectionChanged事件,并且您无法依赖于何时触发它。 Some logic checking on what exactly is selected would be more reliable: 对确切选择的内容进行一些逻辑检查会更可靠:

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

The reason it's happening afterwards with your code as-is is because the event gets queued on the thread for the UI, so it's up to Windows if it will execute the next line of code in Load , or to handle the other events on the queue. 之后使用您的代码发生的原因是因为事件在UI的线程上排队,所以如果它将在Load执行下一行代码,或者处理队列中的其他事件,则由Windows决定。

不要向你抛出很多你可能熟悉或不熟悉的东西,但如果这是一个相对较新的代码库,你可能要考虑使用MVVM模式并使用命令而不是古老的 (强调我的)事件模型。

Order of Events in Windows Forms Windows窗体中的事件顺序

Control.HandleCreated Control.HandleCreated

Control.BindingContextChanged Control.BindingContextChanged

Form.Load Form.Load

Control.VisibleChanged Control.VisibleChanged

Form.Activated Form.Activated

Form.Shown Form.Shown

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM