2

A little background

I'm building an application which uses a plug-in architecture to access specific devices. These devices have fields, which I named Attributes, that can be read, written or both. This is done using a WPF front-end running in it's own thread and a MVVM like structure in the back end which wraps specific plug-in to expose it's attributes in a generic way.

Some details about the threading structure

I have two "host" objects, one initiates the plug-in structures eventually exposes two views: a view of available plug ins and (if a plug-in was selected) a view of all attributes known by a single plug-in. The other eventually starts a STA-thread and runs the a main window in a WPF application. The second host uses BackgroundWorkers to do select, initialization, update and submit tasks. The DoWork events/delegates(I believe they are called delegates) are defined in a ViewController class which is in the MVVM structure and provides a functional interface for updating etc. and implements the INotifyPropertyChanged interface.

Some more details

Upon creation of the mainwindow it's context is set to the view controller object. The GUI then binds two list boxes to both views.

The problem

When I call the selectPlugin method from within the UI thread it freezes until the connect operation is complete and every single attribute is loaded into the list contained by the ViewModel. However it does work and afterwards the ListBox itemssource binding is updated and all attributes are shown.

However I don't want the UI to freeze up on every operation, so I implemented backgroundworkers. It all works fine, the objects get updated and the binding source is replaced by a new View instance. But the binding itself doesn't update.

I tried allot off different solutions using the Dispatcher.Invoke or the DoWorkComplete event in the UI thread. I found that the PropertyChanged event itself stays null using the following property setter:

    public IAttributesViewModel AttributesView
    {
        get 
        {
                StaticLogger.WriteDebugLog(log,
                                           String.Format("Executing {0}",
                                                         System.Reflection.MethodBase.GetCurrentMethod()));
                return _currentAttributes;
        }
        set 
        {
            _currentAttributes = value;
            OnPropertyChanged("AttributesView");
        }
    }

The implementation of INotifyPropertyChanged looks like this:

    #region INotifyPropertyChange implementation
    /// <summary>
    /// Occurs when a property value changes.
    /// </summary>
    public event PropertyChangedEventHandler PropertyChanged;
    /// <summary>
    /// Called when [property changed].
    /// </summary>
    /// <param name="name">The name.</param>
    protected void OnPropertyChanged(string name)
    {
        StaticLogger.WriteDebugLog(log, String.Format("Executing {0}", System.Reflection.MethodBase.GetCurrentMethod()));
        var handler = PropertyChanged;
        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(name));
        }
    }
    #endregion

The PropertyChanged event as declared above is always null. This could or should have something to do with the actual binding in XAML, however i did set de datacontext of the main window in the UI thread like this:

    private static void Host(object viewControler)
    {
        var controler = viewControler as IViewController;
        _instance._main = new MainWindow(controler) { DataContext = controler };
        var gui = new App();
        if (_instance._main == null) throw new ArgumentNullException("viewControler");
        gui.Run(_instance._main);
    }

I use a Singleton instance off the UI host object, in case that makes a difference.

In XAML I use a custom dependency property on a UserControll containing the AttributesListbox and some styling code. The actual ListBox binds to this property for it's own ItemSource property. Don't think this should be different from using the list box directly, but just in case this causes the issue with the PropertyChanged event being null, and is implemented in the following way:

C# /// /// Interaction logic for AttributesListBox.xaml /// public partial class AttributesListBox : UserControl { private static readonly UIPropertyMetadata _sourceMetadata = new UIPropertyMetadata(String.Empty, SourceChangedCallback);

    public AttributesListBox()
    {
        InitializeComponent();
    }

    /// <summary>
    /// The dependency property that gets or sets the source of the ListBox to render.
    /// </summary>
    public static DependencyProperty sourceProperty = DependencyProperty.Register(
        "Source", typeof(IEnumerable), typeof(AttributesListBox),_sourceMetadata);
    /// <summary>
    /// Gets or sets the nsource of the image to render.
    /// </summary>
    public IEnumerable Source
    {
        get { return (IEnumerable)GetValue(sourceProperty); }
        set { SetValue(sourceProperty, value); }
    }

    private static void SourceChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var box = (AttributesListBox) d;
        box.List.ItemsSource = e.NewValue as IEnumerable;
    }
}

XAML

<ListBox Name="List" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" ItemsSource="{Binding Source}" BorderBrush="{x:Null}" />

The first question(s)

What is wrong with the way I use the ViewController object in conjunction with the UI? And thus why is the PropertyChanged event always null? This should mean I messed up the binding process somewhere doesn't it?

The second question(s)

How do I get the OnPropertyChanged event to notify the UI thread, specificity the Binding in the UserControll(AttributesListbox)?

What route does the event follow?

for instance: I use a DoWork delegate and this method changes the property directly from within the instance, which is not in the UI thread. Does this cause the event never to reach the UI thread because is it raised in the other worker thread?

I can't get my head around the event bubbling/tunnelling, is a event restricted to the thread which created the instance or the thread which called the specific method(using a dispatcher or whatever)?

Hypothesis

I suspect there are two issues in my case: 1. The PropertyChanged handler stays null because the object is either not bound or because it is not created in the correct thread. 2. The event, if it where actually fired, never reaches the correct thread because it gets stuck in either the BackgroundWorker thread or the back end "host" thread.

This is the first question I have asked here, so if your missing some pieces off the puzzle please do inform me.

Thanks for taking the time to read about my little problem situation, I hope we can come to a solution.

Sander Aernouts
  • 959
  • 4
  • 16

2 Answers2

0

I think you may have messed up the binding of your listbox, namely (shortened for simplicity):

<ListBox ItemsSource="{Binding Source}" />

this binding means "look for a property called Source in the my current DataContext", which I suspect is not what you intend. If you want to bind to the property Source of the AttributesListBox (which I suppose hosts the above listbox), you should do it this way:

<ListBox ItemsSource="{Binding Source, RelativeSource={RelativeSource AncestorType={x:Type AttributesListBox}}}" />

which means - "look for a property called Source in the first object up the tree that is of type AttributesListBox". Of cource you still have to bind that Source property to the correct thing of your controller, but I suppose you have done that.

The deal with the property changed event is that it must be raised from the UI (Dispatcher) thread, if you raise it from a background thread wpf will not automatically marshal it to the UI thread. So make sure that when the background work has completed, you set the propoperties which need to be updated on the UI thread.

xenry
  • 456
  • 3
  • 8
  • Thanks for your response. The code seems to work with just {Binding Source} but this might be a coincidence, I can see how your binding expression is a safer bet. And the PropertyChanged event seems to somehow, maybe coincidental, make it's way to the UI thread. I don't use the Dispatcher or the DoWorkComplete event to make sure the property is updated using the UI thread. Is this a safe bet or does this deserve a good look? However you did clarify my second question(s). – Sander Aernouts Dec 22 '10 at 12:55
  • Well I am surprised that the {Binding Source} works, what is the DataContext of the ListBox at runtime (you can check it with the "snoop" tool found on codeplex)? About the INotifyPropertyChanged, I was wrong, and I learned something new today =) Check this http://stackoverflow.com/questions/2553333/wpf-databinding-thread-safety – xenry Dec 22 '10 at 13:55
0

The great thing about asking a question is that is forces you to really think on de cause off your problem, thus taking a little break you might finally, after two days, find the utterly simple solution.

The problem was a ParameterizedThreadStart which request a void method(object arg) so the compiler does no type checking what so ever as long as you pass a object. An I passed the wrong object because I edited the method. The safe typecasting using the "as" operator seems to be a pain to, it seems to swallow an CastingException thus not informing you about the small mistake.

So simple solution is:

private static void Host(object viewControler)
{
    var controler = viewControler as IViewController;
    _instance._main = new MainWindow(controler) { DataContext = controler };
    var gui = new App();
    if (_instance._main == null) throw new ArgumentNullException("viewControler");
    gui.Run(controler);
}

I didn't expect my BackgroundWorker to work a 100% "out of the box" but it seems to do so. Whether my code is "best practice" is another separate question.

The key to making me think in that direction was: Short example: bad and good practice for databinding and object initialization

When working with DataBindings in visaul studio also check out this small article: How to show WPF trace

Sander Aernouts
  • 959
  • 4
  • 16