This should be an easy one, but I can't figure it out. When some property of my custom activity changes (e.g. via changing it in the properties grid in WF designer), I want to update the DisplayName dynamically. I do that in the property setter code:
public sealed class TarpSpecItem : Activity, ITarpSpecItem, INotifyPropertyChanged
{
...
public DocumentationType Type
{
get { return _type; }
set { _type = value;
DisplayName = "<" + value.ToString() + ">";
OnPropertyChanged("DisplayName");
OnPropertyChanged("Type");
}
}
The activity implements INotifyPropertyChanged. Yet, the DisplayName does not change on the designer surface. What am I missing?
Will's answer is correct. I'm pasting the updated code of the designer *.xaml.cs here as a separate answer to have correct formatting (not possible as a comment). This code copmpiles and does the trick.
protected override void OnModelItemChanged(object newItem)
{
ModelItem modelItem = newItem as ModelItem;
if (modelItem != null)
modelItem.PropertyChanged += this.ModelItemPropertyChangedHandler;
base.OnModelItemChanged(newItem);
}
private void ModelItemPropertyChangedHandler(object sender, PropertyChangedEventArgs e)
{
if (!e.PropertyName.Equals("Type", StringComparison.OrdinalIgnoreCase))
return;
ModelItem.Properties["DisplayName"].SetValue("<" + ModelItem.Properties["Type"].Value +">");
}
This wrapping of the activity inside the modelitem does not make the solution obvious, does it? It's a bit hard to see what's going on, but it really works. Great job, Will!
Your Activity
is wrapped in a ModelItem
, which handles all notification services between the Activity and the design surface.
When initially wrapped within the designer, the ModelItem
does not inspect your Activity
to see if it provides some property change notification method (INPC, a custom TypeDescriptor
, etc) or other interfaces (i.e., IDataErrorInfo
, for which I have a connect here).
So, essentially, you cannot do what you are attempting to accomplish here. From within the Activity, you cannot send change notification back through the wrapping ModelItem. Which you really shouldn't be doing, anyhow.
In the designer, state change should only pass through the ModelItem
, as it needs to keep its state in sync with the wrapped Activity
. If its state and the wrapped instance's state become misaligned, you won't see the correct information in the designer.
// we're doing this in the designer
var activity = ModelItem.GetCurrentValue() as MyActivity;
activity.DisplayName = "You won't ever see this in the design surface!";
Instead, you must make code changes through the ModelItem
// again, in the designer code
ModelItem.Properties["DisplayName"].Value = "You will see this change!";
So, what's the solution? The only real solution is to ensure that your Activities
are passive when their state changes! Property value changes shouldn't trigger anything.
Instead, move code like this into the designer!
override OnModelItemChanged(object obj)
{
// live dangerously!
var mi = obj as ModelItem;
mi.PropertyChanged += OnPropertyChanged;
}
void OnPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if(!e.PropertyName.Equals("Type", StringComparison.OrdinalIgnoreCase))
return;
ModelItem.Properties["DisplayName"].Value =
"<" +
ModelItem.Properties["Type"].GetCurrentValue().ToString() +
">";
}
Of course, you can't just copypaste this. And it might have some bugs. But it gives you an idea what you have to do.