Monday, March 1, 2010

Silverlight, Binding, Notifications and Threads

It's always good practice to not do anything on the UI from anything but the thread it was created on, but with Silverlight it is law. This extends all the way to objects which implement INotifyPropertyChanged. If they are to raise the PropertyChanged event, they must raise it on the UI thread - or you have broken the law and go to jail without passing Go and collecting $200.

So, my solution is to make sure all my view models have a Dispatcher through which to send PropertyChanged events. This has to be bound when they are created, which makes the way I was resolving them from Unity a bit ugly - I must now register a Dispatcher instance in the unity container for it to be all good, or bind it later, when the view model is resolved.


Dispatcher dispatcher;

public Dispatcher Dispatcher
{
get { return dispatcher; }
set { dispatcher = value; }
}

protected virtual void NotifyPropertyChanged(string propName)
{
if (PropertyChanged != null)
{
if (dispatcher != null)
{
dispatcher.BeginInvoke(new CommandExecuteHandler(SendProperty), propName);
}
}
}

void SendProperty(object prop)
{
string propName = prop as string;
PropertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propName));
OnPropertyChanged(propName);

}


Another rub is the lack of command binding in Silverlight, when it comes to reusing the view models I had already implemented for silverlight/gtkInstigator. I had stored my commands in a dictionary, and they were resolved through a value converter which would pull them out of the command dictionary by name. Well, for Silverlight, I am putting the command name into the Tag, and use the following code in the click handler:

private void Button_Click(object sender, RoutedEventArgs e)
{
var button = sender as Button;
var dc = DataContext as IViewModel;
if (button != null && dc != null)
{
if (dc.Commands.ContainsKey(button.Tag as string))
{
ICommandWrapper command = dc.Commands[button.Tag as string];
if (command.CanExecute(null))
{
command.Execute(null);
}
}
}
}


Problems still to solve: refreshing the enabled/disabled statuses of my buttons, by watching for a PropertyChanged event with "Commands" as a parameter, then iterating through the commands dictionary and available buttons, and setting enabled/disabled to the return of CanExecute.

Passing parameters to commands is another problem which I've yet to have need to solve - all of my commands are simple parameterless actions, so far.

At least ICommand is there, and command binding will likely show up, so using the commanding pattern still makes a lot of sense, even if you have to roll up your sleeves.

No comments: