[runtime] Fix corlib out of date error with disabled COM
[mono.git] / mcs / class / referencesource / System.Activities.Presentation / System.Activities.Presentation / System / Activities / Presentation / DesignObjectWrapper.cs
1 //-----------------------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation.  All rights reserved.
3 //-----------------------------------------------------------------------------
4
5 namespace System.Activities.Presentation
6 {
7     using System;
8     using System.Activities.Presentation.Model;
9     using System.Activities.Presentation.Converters;
10     using System.Activities.Presentation.PropertyEditing;
11     using System.Activities.Presentation.View;
12     using System.Collections.Generic;
13     using System.ComponentModel;
14     using System.Diagnostics.CodeAnalysis;
15     using System.Globalization;
16     using System.Linq;
17     using System.Reflection;
18     using System.Runtime;
19     using System.Text;
20     using System.Windows;
21     using System.Windows.Controls;
22     using System.Windows.Data;
23     using System.Windows.Threading;
24
25     /// <summary>
26     /// DesignObjectWrapper. this class is used to enable more detailed control over edting model objects. especially, if underlying object 
27     /// requires some more complex logic when setting property values - i.e. value of a real property is splitted in the ui to different design properties
28     /// (like in ArgumentDesigner - actual argument's property depends on two factors: direction (in, out, ...) and actual CLR type.
29     /// the DesignObjectWrapper contains that logic and is able to interact with underlying real object, but from ui perspective offeres different set of properties.
30     /// 
31     /// the model can be presented as follows:
32     /// 
33     ///      UI                |           interaction logic             |           actual model
34     /// -----------------------+-----------------------------------------+-------------------------------
35     ///                         
36     ///    FakeModelItem  <---------------- DesignObjectWrapper ---------------------> ModelItem
37     ///                                             ^
38     ///                                             |
39     ///                            DesignObjectWrapper implementation
40     ///                            
41     ///  Where:
42     ///  - FakeModelItem - is a class which exposes any properties which are required to edit actual model. those properties do not have
43     ///                    to exist on the real object, you are responsible to provide getters (required),  setters (optional) and validation (optional)
44     ///                    code for them. In UI, you can access that property using Content property.
45     ///                    
46     /// - DesignObjectWrapper - implementing that class you have to provide a set of property descriptors (get, set, validate, name, type) methods for each of your property
47     ///                    It is required that you provide static implementation for following method:
48     ///                         PropertyDescriptorData[] InitializeTypeProperties()
49     ///                    After you are done with editing of this object, call Dispose, so it unhooks from property change notificatons
50     ///                         
51     /// - ModelItem      - actual model you bind to. DesignObjectWrapper implmentation registers for PropertyChanged notifications from that object, and will notify you via
52     ///                 OnReflectedObjectPropertyChanged. This object can be accessed using ReflectedObject property
53     /// 
54     /// </summary>
55     abstract class DesignObjectWrapper : ICustomTypeDescriptor, INotifyPropertyChanged, IDisposable
56     {
57         protected static readonly string HasErrorsProperty = "HasErrors";
58         protected static readonly string ContentProperty = "Content";
59         protected static readonly string ValidationErrorSuffix = "ValidationError";
60         protected static readonly string AutomationIdProperty = "AutomationId";
61         protected internal static readonly string TimestampProperty = "Timestamp";
62         readonly static string[] DefaultProperties = new string[] { HasErrorsProperty, AutomationIdProperty, TimestampProperty };
63         static IDictionary<Type, PropertyDescriptorCollection> TypePropertyCollection = new Dictionary<Type, PropertyDescriptorCollection>();
64
65         IDictionary<string, string> validationErrors = null;
66         IDictionary<string, PropertyValueEditor> customValueEditors = null;
67         FakeModelItemImpl content;
68         bool isDisposed = false;
69         DateTime timestamp;
70         HashSet<string> changingProperties;
71
72         protected DesignObjectWrapper()
73         {
74             throw FxTrace.Exception.AsError(new NotSupportedException(SR.InvalidConstructorCall));
75         }
76
77         [SuppressMessage(FxCop.Category.Usage, FxCop.Rule.DoNotCallOverridableMethodsInConstructors,
78             Justification = "This class is internal with limited usage inside framework assemblies only. The code written should be safe enough to allow such usage.")]
79         protected DesignObjectWrapper(ModelItem reflectedObject)
80         {
81             this.changingProperties = new HashSet<string>();
82             this.Initialize(reflectedObject);
83         }
84
85         internal void Initialize(ModelItem reflectedObject)
86         {
87             this.isDisposed = false;
88             this.changingProperties.Clear();
89             this.ReflectedObject = reflectedObject;
90             this.Context = ((IModelTreeItem)reflectedObject).ModelTreeManager.Context;
91             this.ModelTreeManager = ((IModelTreeItem)reflectedObject).ModelTreeManager;
92             this.ReflectedObject.PropertyChanged += OnReflectedObjectPropertyChanged;
93             this.RaisePropertyChangedEvent("ReflectedObject");
94             //update timestamp if we do reinitialize wrapper
95             this.UpdateTimestamp();
96             this.Content.PropertyChanged += this.OnFakeModelPropertyChanged;
97         }
98
99         void OnFakeModelPropertyChanged(object sender, PropertyChangedEventArgs e)
100         {
101             if (!this.changingProperties.Contains(e.PropertyName))
102             {
103                 this.changingProperties.Add(e.PropertyName);
104                 this.RaisePropertyChangedEvent(e.PropertyName);
105                 this.changingProperties.Remove(e.PropertyName);
106             }
107         }
108
109         public ModelItem ReflectedObject
110         {
111             get;
112             private set;
113         }
114
115         public EditingContext Context
116         {
117             get;
118             private set;
119         }
120
121         protected ModelTreeManager ModelTreeManager
122         {
123             get;
124             private set;
125         }
126
127         public ModelItem Content
128         {
129             get
130             {
131                 if (null == this.content)
132                 {
133                     ModelTreeManager manager = this.Context.Services.GetService<ModelTreeManager>();
134                     this.content = new FakeModelItemImpl(manager, this.GetType(), this, null);
135                 }
136                 return this.content;
137             }
138             private set
139             {
140                 this.content = (FakeModelItemImpl)value;
141             }
142         }
143
144         IDictionary<string, string> ValidationErrors
145         {
146             get
147             {
148                 if (null == this.validationErrors)
149                 {
150                     this.validationErrors = new Dictionary<string, string>();
151                 }
152                 return this.validationErrors;
153             }
154         }
155
156         protected IDictionary<string, PropertyValueEditor> CustomValueEditors
157         {
158             get
159             {
160                 if (null == this.customValueEditors)
161                 {
162                     this.customValueEditors = new Dictionary<string, PropertyValueEditor>();
163                 }
164                 return this.customValueEditors;
165             }
166         }
167
168         public bool HasErrors
169         {
170             get
171             {
172                 return null != this.validationErrors && this.validationErrors.Count != 0;
173             }
174         }
175
176         protected abstract string AutomationId { get; }
177
178         #region ICustomTypeDescriptor Members
179
180         public AttributeCollection GetAttributes()
181         {
182             return new AttributeCollection(this.GetType().GetCustomAttributes(false).OfType<Attribute>().ToArray());
183         }
184
185         public string GetClassName()
186         {
187             return this.GetType().FullName;
188         }
189
190         public string GetComponentName()
191         {
192             return this.GetType().FullName;
193         }
194
195         public TypeConverter GetConverter()
196         {
197             object[] attributes = this.GetType().GetCustomAttributes(typeof(TypeConverterAttribute), false);
198             if (attributes.Length != 0)
199             {
200                 TypeConverterAttribute attribute = (TypeConverterAttribute)attributes[0];
201                 return (TypeConverter)Activator.CreateInstance(Type.GetType(attribute.ConverterTypeName));
202             }
203             return null;
204         }
205
206         public EventDescriptor GetDefaultEvent()
207         {
208             return null;
209         }
210
211         public PropertyDescriptor GetDefaultProperty()
212         {
213             return null;
214         }
215
216         public string GetValidationErrors(IList<string> invalidProperties)
217         {
218             var result = string.Empty;
219             if (this.HasErrors)
220             {
221                 var content = new StringBuilder();
222                 bool newRowRequired = false;
223                 foreach (var entry in this.validationErrors)
224                 {
225                     if (newRowRequired)
226                     {
227                         content.AppendLine();
228                     }
229                     content.Append(entry.Key);
230                     content.AppendLine(":");
231                     content.Append(entry.Value);
232                     newRowRequired = true;
233                     if (null != invalidProperties)
234                     {
235                         invalidProperties.Add(entry.Key);
236                     }
237                 }
238                 result = content.ToString();
239             }
240             return result;
241         }
242
243         public string GetValidationErrors()
244         {
245             return this.GetValidationErrors(null);
246         }
247
248         public void ClearValidationErrors()
249         {
250             this.ClearValidationErrors(null);
251         }
252
253         public void ClearValidationErrors(IEnumerable<string> properties)
254         {
255             if (null != this.validationErrors)
256             {
257                 if (null != properties)
258                 {
259                     foreach (var propertyName in properties)
260                     {
261                         if (this.validationErrors.ContainsKey(propertyName))
262                         {
263                             this.validationErrors.Remove(propertyName);
264                         }
265                     }
266                 }
267                 else
268                 {
269                     this.validationErrors.Clear();
270                 }
271             }
272         }
273
274         public object GetEditor(Type editorBaseType)
275         {
276             object[] attributes = this.GetType().GetCustomAttributes(typeof(EditorAttribute), false);
277             if (attributes.Length != 0)
278             {
279                 EditorAttribute attribute = (EditorAttribute)attributes[0];
280                 return Activator.CreateInstance(Type.GetType(attribute.EditorTypeName));
281             }
282             return null;
283         }
284
285         public EventDescriptorCollection GetEvents(Attribute[] attributes)
286         {
287             return null;
288         }
289
290         public EventDescriptorCollection GetEvents()
291         {
292             return null;
293         }
294
295         public PropertyDescriptorCollection GetProperties(Attribute[] attributes)
296         {
297             return ((ICustomTypeDescriptor)this).GetProperties();
298         }
299
300         public PropertyDescriptorCollection GetProperties()
301         {
302             Type type = this.GetType();
303             if (!DesignObjectWrapper.TypePropertyCollection.ContainsKey(type))
304             {
305                 MethodInfo initMethod = type.GetMethod("InitializeTypeProperties", BindingFlags.Static | BindingFlags.Public);
306                 PropertyDescriptorData[] properties = (PropertyDescriptorData[])initMethod.Invoke(null, null);
307                 List<DesignObjectPropertyDescriptor> descriptors = new List<DesignObjectPropertyDescriptor>(properties.Length);
308                 for (int i = 0; i < properties.Length; ++i)
309                 {
310                     properties[i].OwnerType = type;
311                     DesignObjectPropertyDescriptor descriptor = new DesignObjectPropertyDescriptor(properties[i]);
312                     if (null != properties[i].PropertyValidator)
313                     {
314                         string localPropertyName = properties[i].PropertyName;
315                         PropertyDescriptorData data = new PropertyDescriptorData()
316                         {
317                             OwnerType = type,
318                             PropertyAttributes = new Attribute[] { BrowsableAttribute.No },
319                             PropertyValidator = null,
320                             PropertySetter = null,
321                             PropertyType = typeof(string),
322                             PropertyName = string.Format(CultureInfo.InvariantCulture, "{0}{1}", localPropertyName, ValidationErrorSuffix),
323                             PropertyGetter = (instance) => (!instance.IsPropertyValid(localPropertyName) ? instance.validationErrors[localPropertyName] : string.Empty),
324                         };
325                         descriptors.Add(new DesignObjectPropertyDescriptor(data));
326                     }
327                     descriptors.Add(descriptor);
328                 }
329                 for (int i = 0; i < DesignObjectWrapper.DefaultProperties.Length; ++i)
330                 {
331                     descriptors.Add(this.ConstructDefaultPropertyPropertyDescriptor(DesignObjectWrapper.DefaultProperties[i]));
332                 }
333                 DesignObjectWrapper.TypePropertyCollection[type] = new PropertyDescriptorCollection(descriptors.ToArray(), true);
334             }
335
336             return DesignObjectWrapper.TypePropertyCollection[type];
337         }
338
339         public object GetPropertyOwner(PropertyDescriptor pd)
340         {
341             return this;
342         }
343
344         #endregion
345
346         #region INotifyPropertyChanged Members
347
348         public event PropertyChangedEventHandler PropertyChanged;
349
350         #endregion
351
352         public bool IsPropertyValid(string propertyName)
353         {
354             return this.validationErrors == null || !this.validationErrors.ContainsKey(propertyName);
355         }
356
357         public virtual void Dispose()
358         {
359             if (null != this.ReflectedObject && !this.isDisposed)
360             {
361                 this.isDisposed = true;
362                 this.ReflectedObject.PropertyChanged -= this.OnReflectedObjectPropertyChanged;
363                 this.Content.PropertyChanged -= this.OnFakeModelPropertyChanged;
364                 if (null != this.customValueEditors)
365                 {
366                     this.customValueEditors.Clear();
367                 }
368                 this.RaisePropertyChangedEvent("ReflectedObject");
369             }
370         }
371
372         //GetDynamicPropertyValueEditor - if user marks one of the properties with DesignObjectWrapperDynamicPropertyEditor attribte,
373         //it is expected that valid property value editor for each instance of design object wrapper will be provided
374         internal PropertyValueEditor GetDynamicPropertyValueEditor(string propertyName)
375         {
376             PropertyValueEditor result = null;
377             //look in the value editor cache - perhaps there is one available for given object
378             if (this.CustomValueEditors.ContainsKey(propertyName))
379             {
380                 result = this.CustomValueEditors[propertyName];
381             }
382             else
383             {
384                 //no, get type of the editor
385                 Type editorType = this.GetDynamicPropertyValueEditorType(propertyName);
386                 if (null == editorType)
387                 {
388                     throw FxTrace.Exception.AsError(new ArgumentException("GetDynamicPropertyValueEditorType() returned null for propertyName."));
389                 }
390                 //create one
391                 result = (PropertyValueEditor)Activator.CreateInstance(editorType);
392                 //store it in cache
393                 this.CustomValueEditors[propertyName] = result;
394             }
395             return result;
396         }
397
398         internal Type GetDynamicPropertyValueEditorType(string propertyName)
399         {
400             //get editor type for dynamic property
401             var editorType = this.OnGetDynamicPropertyValueEditorType(propertyName);
402             if (null == editorType)
403             {
404                 //there should be always be one...
405                 Fx.Assert(false, "PropertyValueEditor not defined for property '" + propertyName + "'");
406             }
407             //and it should be assignable from PropertyValueEditor
408             else if (!typeof(PropertyValueEditor).IsAssignableFrom(editorType))
409             {
410                 Fx.Assert(false, "Type '" + editorType.FullName + "' is not assignable from PropertyValueEditor");
411                 editorType = null;
412             }
413             return editorType;
414         }
415
416         //virtual OnGetDynamicProperyValueEditorType - if user marks property with DesignObjectWrapperDynamicPropertyEditor, 
417         //this method has to be overriden
418         protected virtual Type OnGetDynamicPropertyValueEditorType(string propertyName)
419         {
420             throw FxTrace.Exception.AsError(new NotImplementedException());
421         }
422
423         //bool GetPropertyChangeTriggerState(string propertyName)
424         //{
425         //    if (this.propertyChangeTriggerState.ContainsKey(propertyName))
426         //    {
427         //        return this.propertyChangeTriggerState[propertyName];
428         //    }
429         //    else
430         //    {
431         //        return false;
432         //    }
433         //}
434
435         //void SetPropertyChangeTriggerState(string propertyName, bool state)
436         //{
437         //    if (!this.propertyChangeTriggerState.ContainsKey(propertyName))
438         //    {
439         //        this.propertyChangeTriggerState.Add(propertyName, state);
440         //    }
441         //    else
442         //    {
443         //        this.propertyChangeTriggerState[propertyName] = state;
444         //    }
445         //}
446
447         internal void NotifyPropertyChanged(string propertyName)
448         {
449             if (!this.isDisposed)
450             {
451                 (this.Content as IModelTreeItem).OnPropertyChanged(propertyName);
452             }
453         }
454
455         [SuppressMessage(FxCop.Category.Design, FxCop.Rule.UseEventsWhereAppropriate, Justification = "Procedure to raise the event")]
456         protected void RaisePropertyChangedEvent(string propertyName)
457         {
458             //don't raise property changed events if object is disposed 
459             //- the underlying ModelItem might not be valid, doesn't make sense to do anything on it.
460             if (!this.isDisposed)
461             {
462                 //let the implementation react on property change
463                 this.OnPropertyChanged(propertyName);
464
465                 if (null != this.PropertyChanged)
466                 {
467                     this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
468                 }
469                 if (!this.changingProperties.Contains(propertyName))
470                 {
471                     if (this.Content.Properties[propertyName] != null)
472                     {
473                         this.changingProperties.Add(propertyName);
474                         (this.Content as IModelTreeItem).OnPropertyChanged(propertyName);
475                         this.changingProperties.Remove(propertyName);
476                     }
477                 }
478             }
479         }
480
481         protected virtual void OnPropertyChanged(string propertyName)
482         {
483         }
484
485         DesignObjectPropertyDescriptor ConstructDefaultPropertyPropertyDescriptor(string propertyName)
486         {
487             DesignObjectPropertyDescriptor result = null;
488             if (string.Equals(propertyName, HasErrorsProperty))
489             {
490                 PropertyDescriptorData data = new PropertyDescriptorData()
491                 {
492                     OwnerType = this.GetType(),
493                     PropertyName = propertyName,
494                     PropertyAttributes = new Attribute[] { BrowsableAttribute.No },
495                     PropertySetter = null,
496                     PropertyType = typeof(bool),
497                     PropertyValidator = null,
498                     PropertyGetter = (instance) => (instance.HasErrors)
499                 };
500                 result = new DesignObjectPropertyDescriptor(data);
501             }
502             else if (string.Equals(propertyName, ContentProperty))
503             {
504                 PropertyDescriptorData data = new PropertyDescriptorData()
505                 {
506                     OwnerType = this.GetType(),
507                     PropertyName = propertyName,
508                     PropertyAttributes = new Attribute[] { BrowsableAttribute.No },
509                     PropertySetter = (instance, value) => { instance.Content = (ModelItem)value; },
510                     PropertyType = typeof(ModelItem),
511                     PropertyValidator = null,
512                     PropertyGetter = (instance) => (instance.content)
513                 };
514                 result = new DesignObjectPropertyDescriptor(data);
515             }
516             else if (string.Equals(propertyName, AutomationIdProperty))
517             {
518                 PropertyDescriptorData data = new PropertyDescriptorData()
519                 {
520                     OwnerType = this.GetType(),
521                     PropertyName = propertyName,
522                     PropertyAttributes = new Attribute[] { BrowsableAttribute.No },
523                     PropertySetter = null,
524                     PropertyType = typeof(string),
525                     PropertyValidator = null,
526                     PropertyGetter = (instance) => (instance.AutomationId)
527                 };
528                 result = new DesignObjectPropertyDescriptor(data);
529             }
530             else if (string.Equals(propertyName, TimestampProperty))
531             {
532                 PropertyDescriptorData data = new PropertyDescriptorData()
533                 {
534                     OwnerType = this.GetType(),
535                     PropertyName = propertyName,
536                     PropertyType = typeof(DateTime),
537                     PropertyValidator = null,
538                     PropertyAttributes = new Attribute[] { BrowsableAttribute.No },
539                     PropertySetter = (instance, value) => { instance.UpdateTimestamp(); },
540                     PropertyGetter = (instance) => (instance.GetTimestamp())
541                 };
542                 result = new DesignObjectPropertyDescriptor(data);
543             }
544             return result;
545         }
546
547         protected bool IsUndoRedoInProgress
548         {
549             get
550             {
551                 return null != this.Context && this.Context.Services.GetService<UndoEngine>().IsUndoRedoInProgress;
552             }
553         }
554
555         void UpdateTimestamp()
556         {
557             this.timestamp = DateTime.Now;
558             this.RaisePropertyChangedEvent(TimestampProperty);
559         }
560
561         protected DateTime GetTimestamp()
562         {
563             return this.timestamp;
564         }
565
566         void OnReflectedObjectPropertyChanged(object s, PropertyChangedEventArgs e)
567         {
568             if (this.IsUndoRedoInProgress)
569             {
570                 this.OnReflectedObjectPropertyChanged(e.PropertyName);
571                 Type type = this.GetType();
572                 if (DesignObjectWrapper.TypePropertyCollection.ContainsKey(type))
573                 {
574                     PropertyDescriptorCollection properties = DesignObjectWrapper.TypePropertyCollection[type];
575                     for (int i = 0; i < properties.Count; ++i)
576                     {
577                         if (string.Equals(properties[i].Name, e.PropertyName))
578                         {
579                             this.RaisePropertyChangedEvent(e.PropertyName);
580                             break;
581                         }
582                     }
583                 }
584             }
585
586             //whenever data within reflected object changes, we do update timestamp property on wrapped object - 
587             //this allows to create triggers and bindings which do need to be reevaluated whenever overal state of the object changes
588             this.UpdateTimestamp();
589         }
590
591         protected virtual void OnReflectedObjectPropertyChanged(string propertyName)
592         {
593         }
594
595         sealed class DesignObjectPropertyDescriptor : PropertyDescriptor
596         {
597             PropertyDescriptorData descriptorData;
598             string validationErrorPropertyName;
599
600             public DesignObjectPropertyDescriptor(PropertyDescriptorData descriptorData)
601                 : base(descriptorData.PropertyName, descriptorData.PropertyAttributes)
602             {
603                 this.descriptorData = descriptorData;
604                 this.validationErrorPropertyName = (null != this.descriptorData.PropertyValidator ?
605                     string.Format(CultureInfo.InvariantCulture, "{0}{1}", this.descriptorData.PropertyName, DesignObjectWrapper.ValidationErrorSuffix) :
606                     null);
607             }
608
609             public override bool CanResetValue(object component)
610             {
611                 return null != this.descriptorData.PropertySetter;
612             }
613
614             public override Type ComponentType
615             {
616                 get { return this.descriptorData.OwnerType; }
617             }
618
619             public override object GetValue(object component)
620             {
621                 DesignObjectWrapper instance = (DesignObjectWrapper)component;
622                 return !instance.isDisposed ? this.descriptorData.PropertyGetter(instance) : null;
623             }
624
625             public override bool IsReadOnly
626             {
627                 get { return null == this.descriptorData.PropertySetter; }
628             }
629
630             public override Type PropertyType
631             {
632                 get { return this.descriptorData.PropertyType; }
633             }
634
635             public override void ResetValue(object component)
636             {
637                 DesignObjectWrapper instance = (DesignObjectWrapper)component;
638                 this.descriptorData.PropertySetter(instance, null);
639             }
640
641             [SuppressMessage("Reliability", "Reliability108",
642                 Justification = "Exception not eaten away. If its a fatal exception we rethrow, else we wrap in another exception and throw.")]
643             public override void SetValue(object component, object value)
644             {
645                 DesignObjectWrapper instance = (DesignObjectWrapper)component;
646                 if (!instance.IsUndoRedoInProgress)
647                 {
648                     if (null != this.descriptorData.PropertyValidator)
649                     {
650                         string error = null;
651                         ValidationException exception = null;
652                         try
653                         {
654                             List<string> errors = new List<string>();
655                             if (!this.descriptorData.PropertyValidator(instance, value, errors))
656                             {
657                                 StringBuilder sb = new StringBuilder();
658                                 errors.ForEach((errMessage) =>
659                                     {
660                                         sb.AppendLine(errMessage);
661                                     });
662                                 error = sb.ToString();
663                                 exception = new ValidationException(error);
664                             }
665                         }
666                         catch (Exception err)
667                         {
668                             if (Fx.IsFatal(err))
669                             {
670                                 throw FxTrace.Exception.AsError(err);
671                             }
672                             else
673                             {
674                                 exception = new ValidationException(err.Message, err);
675                             }
676                         }
677
678                         if (null != exception)
679                         {
680                             instance.ValidationErrors[this.Name] = exception.Message;
681                             instance.RaisePropertyChangedEvent(this.validationErrorPropertyName);
682                             instance.RaisePropertyChangedEvent(DesignObjectWrapper.HasErrorsProperty);
683                             throw FxTrace.Exception.AsError(exception);
684                         }
685                         else if (null != instance.validationErrors && instance.validationErrors.ContainsKey(this.Name))
686                         {
687                             instance.validationErrors.Remove(this.Name);
688                             if (0 == instance.validationErrors.Count)
689                             {
690                                 instance.validationErrors = null;
691                             }
692                             instance.RaisePropertyChangedEvent(this.validationErrorPropertyName);
693                             instance.RaisePropertyChangedEvent(DesignObjectWrapper.HasErrorsProperty);
694                         }
695                     }
696
697                     this.descriptorData.PropertySetter(instance, value);
698
699                     (instance.Content as IModelTreeItem).ModelTreeManager.AddToCurrentEditingScope(new FakeModelNotifyPropertyChange(instance.Content as IModelTreeItem, this.Name));
700                 }
701             }
702
703             public override bool ShouldSerializeValue(object component)
704             {
705                 return false;
706             }
707         }
708     }
709
710     sealed class PropertyDescriptorData
711     {
712         public Type OwnerType { get; set; }
713         public string PropertyName { get; set; }
714         public Type PropertyType { get; set; }
715         public Func<DesignObjectWrapper, object> PropertyGetter { get; set; }
716         public Action<DesignObjectWrapper, object> PropertySetter { get; set; }
717         public Func<DesignObjectWrapper, object, List<string>, bool> PropertyValidator { get; set; }
718         [SuppressMessage(FxCop.Category.Performance, "CA1819:PropertiesShouldNotReturnArrays",
719             Justification = "Array type property does not clone the array in the getter. It references the same array instance.")]
720         public Attribute[] PropertyAttributes { get; set; }
721
722     }
723
724     //DesignObjectWrapperDynamicPropertyEditor - this class is used to allow defining different value editors for given set of DesignObjectWrappers.
725     //i.e. for generic Variable<T>, user may want to provide different editors for variable's value, depending on generic type placeholder -
726     // Variable<string> - would use default editor, but Variable<CustomType> can provide different editing expirience
727     sealed class DesignObjectWrapperDynamicPropertyEditor : DialogPropertyValueEditor
728     {
729         static DataTemplate dynamicExpressionTemplate;
730
731         //DynamicExpressionTemplate - this template defines a content presenter, which will be filled with default or custom type editor
732         static DataTemplate DynamicExpressionTemplate
733         {
734             get
735             {
736                 if (null == dynamicExpressionTemplate)
737                 {
738                     dynamicExpressionTemplate = new DataTemplate();
739                     var contentPresenterFactory = new FrameworkElementFactory(typeof(ContentPresenter));
740                     contentPresenterFactory.SetBinding(ContentPresenter.ContentProperty, new Binding());
741                     contentPresenterFactory.SetBinding(ContentPresenter.TagProperty, new Binding() { Converter = ModelPropertyEntryToOwnerActivityConverter, ConverterParameter = false, Path = new PropertyPath("ParentProperty") });
742                     MultiBinding binding = new MultiBinding() { Converter = new TemplateConverter() };
743                     binding.Bindings.Add(new Binding());
744                     binding.Bindings.Add(new Binding() { Path = new PropertyPath("Tag.Timestamp"), Mode = BindingMode.OneWay, RelativeSource = RelativeSource.Self });
745                     contentPresenterFactory.SetBinding(ContentPresenter.ContentTemplateProperty, binding);
746                     dynamicExpressionTemplate.VisualTree = contentPresenterFactory;
747                     dynamicExpressionTemplate.Seal();
748                 }
749                 //dynamicExpressionTemplate = (DataTemplate)EditorResources.GetResources()["dynamicExpressionTemplate"];
750                 return dynamicExpressionTemplate;
751             }
752         }
753
754         static IValueConverter ModelPropertyEntryToModelItemConverter
755         {
756             get
757             {
758                 return (ModelPropertyEntryToModelItemConverter)EditorResources.GetResources()["ModelPropertyEntryToContainerConverter"];
759             }
760         }
761
762         static IValueConverter ModelPropertyEntryToOwnerActivityConverter
763         {
764             get
765             {
766                 return (ModelPropertyEntryToOwnerActivityConverter)EditorResources.GetResources()["ModelPropertyEntryToOwnerActivityConverter"];
767             }
768         }
769
770         //helper method - gets property value editor for property marked with DesignObjectWrapperDynamicPropertyEditor 
771         static PropertyValueEditor GetEditor(PropertyValue propertyValue)
772         {
773             //convert property value to set of { ModelItem, Context, PropertyValue } 
774             var content = (ModelPropertyEntryToModelItemConverter.Container)
775                 DesignObjectWrapperDynamicPropertyEditor.ModelPropertyEntryToModelItemConverter.Convert(propertyValue, null, null, null);
776
777             //get current instance of design object wrapper
778             var wrapper = (DesignObjectWrapper)content.ModelItem.GetCurrentValue();
779
780             //query it for actual value editor
781             var editor = wrapper.GetDynamicPropertyValueEditor(propertyValue.ParentProperty.PropertyName);
782
783             if (null == editor)
784             {
785                 Fx.Assert(false, "PropertyValue editor not found for '" + propertyValue.ParentProperty.PropertyName + "'");
786             }
787             return editor;
788         }
789
790         public DesignObjectWrapperDynamicPropertyEditor()
791         {
792             this.InlineEditorTemplate = DesignObjectWrapperDynamicPropertyEditor.DynamicExpressionTemplate;
793         }
794
795         [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes",
796             Justification = "Propagating exceptions might lead to VS crash.")]
797         [SuppressMessage("Reliability", "Reliability108:IsFatalRule",
798             Justification = "Propagating exceptions might lead to VS crash.")]
799         public override void ShowDialog(PropertyValue propertyValue, IInputElement commandSource)
800         {
801             //get actual value editor
802             var editor = DesignObjectWrapperDynamicPropertyEditor.GetEditor(propertyValue);
803
804             Fx.Assert(editor is DialogPropertyValueEditor, "PropertyValueEditor is not assigned or is not derived from DialogPropertyValueEditor");
805
806             //if we are here, the editor must derive from DialogPropertyEditor, if it doesn't user provided wrong template
807             if (editor is DialogPropertyValueEditor)
808             {
809                 try
810                 {
811                     ((DialogPropertyValueEditor)editor).ShowDialog(propertyValue, commandSource);
812                 }
813                 catch (Exception err)
814                 {
815                     ErrorReporting.ShowErrorMessage(err.ToString());
816                 }
817             }
818         }
819
820         //helper class - allows pulling template definition for dynamic property value
821         private sealed class TemplateConverter : IMultiValueConverter
822         {
823             public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
824             {
825                 object template = Binding.DoNothing;
826                 if (null != values[0])
827                 {
828                     var editor = DesignObjectWrapperDynamicPropertyEditor.GetEditor((PropertyValue)values[0]);
829                     if (null != editor)
830                     {
831                         template = editor.InlineEditorTemplate;
832                     }
833                 }
834                 return template;
835             }
836
837             public object[] ConvertBack(object value, Type[] targetType, object parameter, CultureInfo culture)
838             {
839                 throw FxTrace.Exception.AsError(new NotSupportedException());
840             }
841         }
842     }
843
844 }