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.