Question

Consider the following code:

<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <StackPanel>
        <Slider ValueChanged="slider_ValueChanged/>
        <TextBox x:Name="counter"/>
    </StackPanel>
</Window>

and

namespace Project1
{
    public partial class Window1 : Window
    {
        public MainWindow() { InitializeComponent(); }

        void slider_ValueChanged(object sender,
            RoutedPropertyChangedEventArgs<double> e)
        {
            counter.Text = e.NewValue.ToString();
        }
    }
}

Slider will raise its ValueChanged event during initialization while counter is still null.

This is an example of a larger problem that I've been running into using WPF, that UI events can fire at any time, and that there is no single place where I can put my initialization code so that it's guaranteed to run after all the pointers owned by the WPF system have been initialized but before any UI events have fired.

What is the most elegant way to deal with this? The fact that this specific example should use data binding is beside the point.

Was it helpful?

Solution 2

The true answer to this question is to use the MVVM pattern where window code behind files contain little to no initialization code.

In this pattern, the UI is connected to the rest of the code with data binding only. You write special view-model classes that implement INotifyPropertyChanged and take your business logic and expose it as a series of properties that UI binds to.

Naturally, you fully control how your view-models initialize.

OTHER TIPS

There are many ways to deal with this, depending on your situation

First off, you could simply recognize the fact that the object might not be initialized and check for that before processing. For example,

if (counter.Text != null)
    counter.Text = e.NewValue.ToString();

Second, you could attach your events in the Loaded event of the object so they don't fire until after the object has been initialized.

void Counter_Loaded(object sender, EventArgs e)
{
    slider.ValueChanged += Slider_ValueChanged;
}

void Counter_Unloaded(object sender, EventArgs e)
{
    slider.ValueChanged -= Slider_ValueChanged;
}

And last of all, you can use WPF's Dispatcher to run events on the UI thread at a different DispatcherPriority. The default is Normal, which runs after Loaded, Render, and DataBind operations

Dispatcher.BeginInvoke(DispatcherPriority.DataBind, 
    new Action(delegate() { counter.Text = e.NewValue.ToString(); }));
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top