tags:

views:

247

answers:

6

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.

+1  A: 

You can use ManagedSpy to figure this out on your own.

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

Austin Salonen
+1 for cool tool I didn't know about!
Dave Swersky
+2  A: 

I just did kind of the same thing behaviorly in a systray WPF app.

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.

Joseph
+1  A: 

Setting the DataContext will likely fire the SelectionChanged event, and you can't rely on when exactly it's fired. 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.

Will Eddins
+3  A: 

Order of Events in Windows Forms

Control.HandleCreated

Control.BindingContextChanged

Form.Load

Control.VisibleChanged

Form.Activated

Form.Shown

Philip Wallace
Not relevant to the question, since he is asking about WPF and you are talking about WinForms.
Ray Burns
+1  A: 

Not to throw a whole lot of stuff at you that you may or may not be familiar with, but if this is a relatively new codebase, you may want to consider using the MVVM pattern and use Commands instead of the archaic (emphasis mine) eventing model.

dustyburwell
+3  A: 

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.

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.

This is how to do it:

Dispatcher.BeginInovke(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.

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:

  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

  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]

  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

  4. The window and all non-collapsed Visuals on it are Arranged

  5. The window and its descendants (both logical and visual) receive a Loaded event

  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. The other steps generally don't happen until a Window is shown, but they can happen earlier if triggered manually.

Ray Burns