1 //-----------------------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation. All rights reserved.
3 //-----------------------------------------------------------------------------
5 namespace System.Activities.Presentation
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;
17 using System.Reflection;
21 using System.Windows.Controls;
22 using System.Windows.Data;
23 using System.Windows.Threading;
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.
31 /// the model can be presented as follows:
33 /// UI | interaction logic | actual model
34 /// -----------------------+-----------------------------------------+-------------------------------
36 /// FakeModelItem <---------------- DesignObjectWrapper ---------------------> ModelItem
39 /// DesignObjectWrapper implementation
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.
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
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
55 abstract class DesignObjectWrapper : ICustomTypeDescriptor, INotifyPropertyChanged, IDisposable
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>();
65 IDictionary<string, string> validationErrors = null;
66 IDictionary<string, PropertyValueEditor> customValueEditors = null;
67 FakeModelItemImpl content;
68 bool isDisposed = false;
70 HashSet<string> changingProperties;
72 protected DesignObjectWrapper()
74 throw FxTrace.Exception.AsError(new NotSupportedException(SR.InvalidConstructorCall));
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)
81 this.changingProperties = new HashSet<string>();
82 this.Initialize(reflectedObject);
85 internal void Initialize(ModelItem reflectedObject)
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;
99 void OnFakeModelPropertyChanged(object sender, PropertyChangedEventArgs e)
101 if (!this.changingProperties.Contains(e.PropertyName))
103 this.changingProperties.Add(e.PropertyName);
104 this.RaisePropertyChangedEvent(e.PropertyName);
105 this.changingProperties.Remove(e.PropertyName);
109 public ModelItem ReflectedObject
115 public EditingContext Context
121 protected ModelTreeManager ModelTreeManager
127 public ModelItem Content
131 if (null == this.content)
133 ModelTreeManager manager = this.Context.Services.GetService<ModelTreeManager>();
134 this.content = new FakeModelItemImpl(manager, this.GetType(), this, null);
140 this.content = (FakeModelItemImpl)value;
144 IDictionary<string, string> ValidationErrors
148 if (null == this.validationErrors)
150 this.validationErrors = new Dictionary<string, string>();
152 return this.validationErrors;
156 protected IDictionary<string, PropertyValueEditor> CustomValueEditors
160 if (null == this.customValueEditors)
162 this.customValueEditors = new Dictionary<string, PropertyValueEditor>();
164 return this.customValueEditors;
168 public bool HasErrors
172 return null != this.validationErrors && this.validationErrors.Count != 0;
176 protected abstract string AutomationId { get; }
178 #region ICustomTypeDescriptor Members
180 public AttributeCollection GetAttributes()
182 return new AttributeCollection(this.GetType().GetCustomAttributes(false).OfType<Attribute>().ToArray());
185 public string GetClassName()
187 return this.GetType().FullName;
190 public string GetComponentName()
192 return this.GetType().FullName;
195 public TypeConverter GetConverter()
197 object[] attributes = this.GetType().GetCustomAttributes(typeof(TypeConverterAttribute), false);
198 if (attributes.Length != 0)
200 TypeConverterAttribute attribute = (TypeConverterAttribute)attributes[0];
201 return (TypeConverter)Activator.CreateInstance(Type.GetType(attribute.ConverterTypeName));
206 public EventDescriptor GetDefaultEvent()
211 public PropertyDescriptor GetDefaultProperty()
216 public string GetValidationErrors(IList<string> invalidProperties)
218 var result = string.Empty;
221 var content = new StringBuilder();
222 bool newRowRequired = false;
223 foreach (var entry in this.validationErrors)
227 content.AppendLine();
229 content.Append(entry.Key);
230 content.AppendLine(":");
231 content.Append(entry.Value);
232 newRowRequired = true;
233 if (null != invalidProperties)
235 invalidProperties.Add(entry.Key);
238 result = content.ToString();
243 public string GetValidationErrors()
245 return this.GetValidationErrors(null);
248 public void ClearValidationErrors()
250 this.ClearValidationErrors(null);
253 public void ClearValidationErrors(IEnumerable<string> properties)
255 if (null != this.validationErrors)
257 if (null != properties)
259 foreach (var propertyName in properties)
261 if (this.validationErrors.ContainsKey(propertyName))
263 this.validationErrors.Remove(propertyName);
269 this.validationErrors.Clear();
274 public object GetEditor(Type editorBaseType)
276 object[] attributes = this.GetType().GetCustomAttributes(typeof(EditorAttribute), false);
277 if (attributes.Length != 0)
279 EditorAttribute attribute = (EditorAttribute)attributes[0];
280 return Activator.CreateInstance(Type.GetType(attribute.EditorTypeName));
285 public EventDescriptorCollection GetEvents(Attribute[] attributes)
290 public EventDescriptorCollection GetEvents()
295 public PropertyDescriptorCollection GetProperties(Attribute[] attributes)
297 return ((ICustomTypeDescriptor)this).GetProperties();
300 public PropertyDescriptorCollection GetProperties()
302 Type type = this.GetType();
303 if (!DesignObjectWrapper.TypePropertyCollection.ContainsKey(type))
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)
310 properties[i].OwnerType = type;
311 DesignObjectPropertyDescriptor descriptor = new DesignObjectPropertyDescriptor(properties[i]);
312 if (null != properties[i].PropertyValidator)
314 string localPropertyName = properties[i].PropertyName;
315 PropertyDescriptorData data = new PropertyDescriptorData()
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),
325 descriptors.Add(new DesignObjectPropertyDescriptor(data));
327 descriptors.Add(descriptor);
329 for (int i = 0; i < DesignObjectWrapper.DefaultProperties.Length; ++i)
331 descriptors.Add(this.ConstructDefaultPropertyPropertyDescriptor(DesignObjectWrapper.DefaultProperties[i]));
333 DesignObjectWrapper.TypePropertyCollection[type] = new PropertyDescriptorCollection(descriptors.ToArray(), true);
336 return DesignObjectWrapper.TypePropertyCollection[type];
339 public object GetPropertyOwner(PropertyDescriptor pd)
346 #region INotifyPropertyChanged Members
348 public event PropertyChangedEventHandler PropertyChanged;
352 public bool IsPropertyValid(string propertyName)
354 return this.validationErrors == null || !this.validationErrors.ContainsKey(propertyName);
357 public virtual void Dispose()
359 if (null != this.ReflectedObject && !this.isDisposed)
361 this.isDisposed = true;
362 this.ReflectedObject.PropertyChanged -= this.OnReflectedObjectPropertyChanged;
363 this.Content.PropertyChanged -= this.OnFakeModelPropertyChanged;
364 if (null != this.customValueEditors)
366 this.customValueEditors.Clear();
368 this.RaisePropertyChangedEvent("ReflectedObject");
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)
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))
380 result = this.CustomValueEditors[propertyName];
384 //no, get type of the editor
385 Type editorType = this.GetDynamicPropertyValueEditorType(propertyName);
386 if (null == editorType)
388 throw FxTrace.Exception.AsError(new ArgumentException("GetDynamicPropertyValueEditorType() returned null for propertyName."));
391 result = (PropertyValueEditor)Activator.CreateInstance(editorType);
393 this.CustomValueEditors[propertyName] = result;
398 internal Type GetDynamicPropertyValueEditorType(string propertyName)
400 //get editor type for dynamic property
401 var editorType = this.OnGetDynamicPropertyValueEditorType(propertyName);
402 if (null == editorType)
404 //there should be always be one...
405 Fx.Assert(false, "PropertyValueEditor not defined for property '" + propertyName + "'");
407 //and it should be assignable from PropertyValueEditor
408 else if (!typeof(PropertyValueEditor).IsAssignableFrom(editorType))
410 Fx.Assert(false, "Type '" + editorType.FullName + "' is not assignable from PropertyValueEditor");
416 //virtual OnGetDynamicProperyValueEditorType - if user marks property with DesignObjectWrapperDynamicPropertyEditor,
417 //this method has to be overriden
418 protected virtual Type OnGetDynamicPropertyValueEditorType(string propertyName)
420 throw FxTrace.Exception.AsError(new NotImplementedException());
423 //bool GetPropertyChangeTriggerState(string propertyName)
425 // if (this.propertyChangeTriggerState.ContainsKey(propertyName))
427 // return this.propertyChangeTriggerState[propertyName];
435 //void SetPropertyChangeTriggerState(string propertyName, bool state)
437 // if (!this.propertyChangeTriggerState.ContainsKey(propertyName))
439 // this.propertyChangeTriggerState.Add(propertyName, state);
443 // this.propertyChangeTriggerState[propertyName] = state;
447 internal void NotifyPropertyChanged(string propertyName)
449 if (!this.isDisposed)
451 (this.Content as IModelTreeItem).OnPropertyChanged(propertyName);
455 [SuppressMessage(FxCop.Category.Design, FxCop.Rule.UseEventsWhereAppropriate, Justification = "Procedure to raise the event")]
456 protected void RaisePropertyChangedEvent(string propertyName)
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)
462 //let the implementation react on property change
463 this.OnPropertyChanged(propertyName);
465 if (null != this.PropertyChanged)
467 this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
469 if (!this.changingProperties.Contains(propertyName))
471 if (this.Content.Properties[propertyName] != null)
473 this.changingProperties.Add(propertyName);
474 (this.Content as IModelTreeItem).OnPropertyChanged(propertyName);
475 this.changingProperties.Remove(propertyName);
481 protected virtual void OnPropertyChanged(string propertyName)
485 DesignObjectPropertyDescriptor ConstructDefaultPropertyPropertyDescriptor(string propertyName)
487 DesignObjectPropertyDescriptor result = null;
488 if (string.Equals(propertyName, HasErrorsProperty))
490 PropertyDescriptorData data = new PropertyDescriptorData()
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)
500 result = new DesignObjectPropertyDescriptor(data);
502 else if (string.Equals(propertyName, ContentProperty))
504 PropertyDescriptorData data = new PropertyDescriptorData()
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)
514 result = new DesignObjectPropertyDescriptor(data);
516 else if (string.Equals(propertyName, AutomationIdProperty))
518 PropertyDescriptorData data = new PropertyDescriptorData()
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)
528 result = new DesignObjectPropertyDescriptor(data);
530 else if (string.Equals(propertyName, TimestampProperty))
532 PropertyDescriptorData data = new PropertyDescriptorData()
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())
542 result = new DesignObjectPropertyDescriptor(data);
547 protected bool IsUndoRedoInProgress
551 return null != this.Context && this.Context.Services.GetService<UndoEngine>().IsUndoRedoInProgress;
555 void UpdateTimestamp()
557 this.timestamp = DateTime.Now;
558 this.RaisePropertyChangedEvent(TimestampProperty);
561 protected DateTime GetTimestamp()
563 return this.timestamp;
566 void OnReflectedObjectPropertyChanged(object s, PropertyChangedEventArgs e)
568 if (this.IsUndoRedoInProgress)
570 this.OnReflectedObjectPropertyChanged(e.PropertyName);
571 Type type = this.GetType();
572 if (DesignObjectWrapper.TypePropertyCollection.ContainsKey(type))
574 PropertyDescriptorCollection properties = DesignObjectWrapper.TypePropertyCollection[type];
575 for (int i = 0; i < properties.Count; ++i)
577 if (string.Equals(properties[i].Name, e.PropertyName))
579 this.RaisePropertyChangedEvent(e.PropertyName);
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();
591 protected virtual void OnReflectedObjectPropertyChanged(string propertyName)
595 sealed class DesignObjectPropertyDescriptor : PropertyDescriptor
597 PropertyDescriptorData descriptorData;
598 string validationErrorPropertyName;
600 public DesignObjectPropertyDescriptor(PropertyDescriptorData descriptorData)
601 : base(descriptorData.PropertyName, descriptorData.PropertyAttributes)
603 this.descriptorData = descriptorData;
604 this.validationErrorPropertyName = (null != this.descriptorData.PropertyValidator ?
605 string.Format(CultureInfo.InvariantCulture, "{0}{1}", this.descriptorData.PropertyName, DesignObjectWrapper.ValidationErrorSuffix) :
609 public override bool CanResetValue(object component)
611 return null != this.descriptorData.PropertySetter;
614 public override Type ComponentType
616 get { return this.descriptorData.OwnerType; }
619 public override object GetValue(object component)
621 DesignObjectWrapper instance = (DesignObjectWrapper)component;
622 return !instance.isDisposed ? this.descriptorData.PropertyGetter(instance) : null;
625 public override bool IsReadOnly
627 get { return null == this.descriptorData.PropertySetter; }
630 public override Type PropertyType
632 get { return this.descriptorData.PropertyType; }
635 public override void ResetValue(object component)
637 DesignObjectWrapper instance = (DesignObjectWrapper)component;
638 this.descriptorData.PropertySetter(instance, null);
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)
645 DesignObjectWrapper instance = (DesignObjectWrapper)component;
646 if (!instance.IsUndoRedoInProgress)
648 if (null != this.descriptorData.PropertyValidator)
651 ValidationException exception = null;
654 List<string> errors = new List<string>();
655 if (!this.descriptorData.PropertyValidator(instance, value, errors))
657 StringBuilder sb = new StringBuilder();
658 errors.ForEach((errMessage) =>
660 sb.AppendLine(errMessage);
662 error = sb.ToString();
663 exception = new ValidationException(error);
666 catch (Exception err)
670 throw FxTrace.Exception.AsError(err);
674 exception = new ValidationException(err.Message, err);
678 if (null != exception)
680 instance.ValidationErrors[this.Name] = exception.Message;
681 instance.RaisePropertyChangedEvent(this.validationErrorPropertyName);
682 instance.RaisePropertyChangedEvent(DesignObjectWrapper.HasErrorsProperty);
683 throw FxTrace.Exception.AsError(exception);
685 else if (null != instance.validationErrors && instance.validationErrors.ContainsKey(this.Name))
687 instance.validationErrors.Remove(this.Name);
688 if (0 == instance.validationErrors.Count)
690 instance.validationErrors = null;
692 instance.RaisePropertyChangedEvent(this.validationErrorPropertyName);
693 instance.RaisePropertyChangedEvent(DesignObjectWrapper.HasErrorsProperty);
697 this.descriptorData.PropertySetter(instance, value);
699 (instance.Content as IModelTreeItem).ModelTreeManager.AddToCurrentEditingScope(new FakeModelNotifyPropertyChange(instance.Content as IModelTreeItem, this.Name));
703 public override bool ShouldSerializeValue(object component)
710 sealed class PropertyDescriptorData
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; }
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
729 static DataTemplate dynamicExpressionTemplate;
731 //DynamicExpressionTemplate - this template defines a content presenter, which will be filled with default or custom type editor
732 static DataTemplate DynamicExpressionTemplate
736 if (null == dynamicExpressionTemplate)
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();
749 //dynamicExpressionTemplate = (DataTemplate)EditorResources.GetResources()["dynamicExpressionTemplate"];
750 return dynamicExpressionTemplate;
754 static IValueConverter ModelPropertyEntryToModelItemConverter
758 return (ModelPropertyEntryToModelItemConverter)EditorResources.GetResources()["ModelPropertyEntryToContainerConverter"];
762 static IValueConverter ModelPropertyEntryToOwnerActivityConverter
766 return (ModelPropertyEntryToOwnerActivityConverter)EditorResources.GetResources()["ModelPropertyEntryToOwnerActivityConverter"];
770 //helper method - gets property value editor for property marked with DesignObjectWrapperDynamicPropertyEditor
771 static PropertyValueEditor GetEditor(PropertyValue propertyValue)
773 //convert property value to set of { ModelItem, Context, PropertyValue }
774 var content = (ModelPropertyEntryToModelItemConverter.Container)
775 DesignObjectWrapperDynamicPropertyEditor.ModelPropertyEntryToModelItemConverter.Convert(propertyValue, null, null, null);
777 //get current instance of design object wrapper
778 var wrapper = (DesignObjectWrapper)content.ModelItem.GetCurrentValue();
780 //query it for actual value editor
781 var editor = wrapper.GetDynamicPropertyValueEditor(propertyValue.ParentProperty.PropertyName);
785 Fx.Assert(false, "PropertyValue editor not found for '" + propertyValue.ParentProperty.PropertyName + "'");
790 public DesignObjectWrapperDynamicPropertyEditor()
792 this.InlineEditorTemplate = DesignObjectWrapperDynamicPropertyEditor.DynamicExpressionTemplate;
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)
801 //get actual value editor
802 var editor = DesignObjectWrapperDynamicPropertyEditor.GetEditor(propertyValue);
804 Fx.Assert(editor is DialogPropertyValueEditor, "PropertyValueEditor is not assigned or is not derived from DialogPropertyValueEditor");
806 //if we are here, the editor must derive from DialogPropertyEditor, if it doesn't user provided wrong template
807 if (editor is DialogPropertyValueEditor)
811 ((DialogPropertyValueEditor)editor).ShowDialog(propertyValue, commandSource);
813 catch (Exception err)
815 ErrorReporting.ShowErrorMessage(err.ToString());
820 //helper class - allows pulling template definition for dynamic property value
821 private sealed class TemplateConverter : IMultiValueConverter
823 public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
825 object template = Binding.DoNothing;
826 if (null != values[0])
828 var editor = DesignObjectWrapperDynamicPropertyEditor.GetEditor((PropertyValue)values[0]);
831 template = editor.InlineEditorTemplate;
837 public object[] ConvertBack(object value, Type[] targetType, object parameter, CultureInfo culture)
839 throw FxTrace.Exception.AsError(new NotSupportedException());