[runtime] Fix corlib out of date error with disabled COM
[mono.git] / mcs / class / referencesource / System.Activities.Presentation / System.Activities.Presentation / System / Activities / Presentation / Base / Core / PropertyEditing / PropertyContainer.cs
1 namespace System.Activities.Presentation.PropertyEditing
2 {
3     using System.Windows.Controls;
4     using System.Windows;
5     using System;
6     using System.Windows.Data;
7     using System.Diagnostics;
8     using System.Diagnostics.CodeAnalysis;
9     using System.Windows.Input;
10     using System.Windows.Controls.Primitives;
11     using System.Windows.Media;
12     using System.ComponentModel;
13     using System.Collections.Generic;
14     using System.Activities.Presentation;
15     using System.Runtime;
16     using System.Activities.Presentation.Internal.PropertyEditing.Editors;
17     using System.Activities.Presentation.Internal.PropertyEditing;
18
19     /// <summary>
20     /// This control is used as a graphical container for PropertyEntry instances.  The control is
21     /// look-less.  However, it is generally styled as a horizontal row that includes the
22     /// name of the property followed by an editor for its value.  This control, however, is
23     /// intended to be restiled by 3rd-parties to suite their needs.  The style is controled
24     /// by three ControlTemplates (InlineRowTemplate, ExtendedPopupRowTemplate, and
25     /// ExtendedPinnedRowTemplate) that are chosed by the logic within this control based
26     /// on the current value of ActiveEditMode.  This control also exposes three DataTemplates
27     /// (InlineEditor, ExtendedEditor, and DialogEditor) that each of the row templates can use 
28     /// to display the appropriate value editor for the PropertyValue being edited.
29     /// </summary>
30     class PropertyContainer : Control, INotifyPropertyChanged
31     {
32
33         private static RoutedCommand _openDialogWindow;
34
35         DataTemplate flagEditorTemplate;
36
37         /// <summary>
38         /// INotifyPropertyChanged event
39         /// </summary>
40         public event PropertyChangedEventHandler PropertyChanged;
41
42         internal event DependencyPropertyChangedEventHandler DependencyPropertyChanged;
43
44         private bool _attachedToPropertyEntryEvents;
45
46         /// <summary>
47         /// Creates a PropertyContainer
48         /// </summary>
49         [SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
50         public PropertyContainer()
51         {
52
53             // Set the OwningPropertyContainer (attached, inherited DP) to self,
54             // so that all of the children of this control know which PropertyContainer
55             // they belong to
56             SetOwningPropertyContainer(this, this);
57
58             this.Loaded += new RoutedEventHandler(OnLoaded);
59             this.Unloaded += new RoutedEventHandler(OnUnloaded);
60         }
61
62         // Useful DPs
63
64         // PropertyEntry DP
65
66         public static readonly DependencyProperty IsValueEditEnabledProperty = DependencyProperty.Register(
67             "IsValueEditEnabled",
68             typeof(bool),
69             typeof(PropertyContainer),
70             new UIPropertyMetadata(true));
71
72
73         /// <summary>
74         /// Gets or sets the PropertyEntry instance on which this PropertyContainer operates. 
75         /// That is the context of the PropertyContainer.  The exposed editor templates 
76         /// (InlineEditor, ExtendedEditor, and DialogEditor) are based on the value of this property.
77         /// </summary>
78         public static readonly DependencyProperty PropertyEntryProperty =
79             DependencyProperty.Register(
80                 "PropertyEntry",
81                 typeof(PropertyEntry),
82                 typeof(PropertyContainer),
83                 new FrameworkPropertyMetadata(
84                     null,
85                     new PropertyChangedCallback(PropertyEntryPropertyChanged)));
86
87         /// <summary>
88         /// Gets or sets the PropertyEntry instance on which this PropertyContainer operates. 
89         /// That is the context of the PropertyContainer.  The exposed editor templates 
90         /// (InlineEditor, ExtendedEditor, and DialogEditor) are based on the value of this property.
91         /// </summary>
92         [Fx.Tag.KnownXamlExternalAttribute]
93         public PropertyEntry PropertyEntry
94         {
95             get { return (PropertyEntry)this.GetValue(PropertyContainer.PropertyEntryProperty); }
96             set { this.SetValue(PropertyContainer.PropertyEntryProperty, value); }
97         }
98
99         public bool IsValueEditEnabled
100         {
101             get { return (bool)GetValue(IsValueEditEnabledProperty); }
102             set { SetValue(IsValueEditEnabledProperty, value); }
103         }
104
105         private static void PropertyEntryPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
106         {
107             PropertyContainer theThis = (PropertyContainer)obj;
108
109             // Since the underlying property has changed, chances are that the inline or extended editors
110             // have changed as well, so fire events indicating that their values have changed
111             theThis.NotifyTemplatesChanged();
112             theThis.OnPropertyChanged("MatchesFilter");
113
114             // Switch back to Inline mode
115             theThis.ActiveEditMode = PropertyContainerEditMode.Inline;
116
117             // Ensure that the Template property of this control is set to the right ControlTemplate
118             UpdateControlTemplate(theThis);
119
120             // Hook into PropertyEntry's changed events
121             if (e.OldValue != null)
122                 theThis.DisassociatePropertyEventHandlers((PropertyEntry)e.OldValue);
123             if (e.NewValue != null)
124                 theThis.AssociatePropertyEventHandlers((PropertyEntry)e.NewValue);
125         }
126
127
128         // ActiveEditMode DP
129
130         // 
131
132
133
134
135         /// <summary>
136         /// Gets or sets currently displayed edit mode of this container (ie. ExtendedPopup,
137         /// ExtendedPinned, Inline or Dialog).  
138         /// </summary>
139         public static readonly DependencyProperty ActiveEditModeProperty =
140             DependencyProperty.Register(
141                 "ActiveEditMode",
142                 typeof(PropertyContainerEditMode),
143                 typeof(PropertyContainer),
144                 new FrameworkPropertyMetadata(
145                     PropertyContainerEditMode.Inline,
146                     new PropertyChangedCallback(OnActiveEditModePropertyChanged)));
147
148         /// <summary>
149         /// Gets or sets currently displayed edit mode of this container (ie. ExtendedPopup,
150         /// ExtendedPinned, Inline or Dialog).  
151         /// </summary>
152         public PropertyContainerEditMode ActiveEditMode
153         {
154             get { return (PropertyContainerEditMode)this.GetValue(PropertyContainer.ActiveEditModeProperty); }
155             set { this.SetValue(PropertyContainer.ActiveEditModeProperty, value); }
156         }
157
158         private static void OnActiveEditModePropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
159         {
160             PropertyContainer theThis = (PropertyContainer)obj;
161
162             // Ensure that the Template property of this control is set to the right ControlTemplate
163             UpdateControlTemplate(theThis);
164
165             // Invoke a dialog editor if needed
166             if (object.Equals(e.NewValue, PropertyContainerEditMode.Dialog))
167             {
168
169                 if (OpenDialogWindow.CanExecute(null, theThis))
170                 {
171                     // There is someone who handles this command, so let it deal with it
172                     // however it wants.
173                     OpenDialogWindow.Execute(null, theThis);
174                 }
175                 else
176                 {
177                     // There is no-one handling this command, so see if there is a virtual
178                     // method we can invoke
179                     DialogPropertyValueEditor editor = theThis.FindDialogPropertyValueEditor();
180                     if (editor != null)
181                     {
182                         // If the DialogCommandSource is not explicitly set, use this control as the
183                         // command source
184                         IInputElement dialogCommandSource = theThis.DialogCommandSource ?? theThis;
185                         editor.ShowDialog(theThis.PropertyEntry.PropertyValue, dialogCommandSource);
186                     }
187                 }
188
189                 // And revert back to old edit mode once done
190                 theThis.ActiveEditMode = (PropertyContainerEditMode)e.OldValue;
191             }
192         }
193
194
195         // DialogCommandSource DP
196
197         /// <summary>
198         /// Gets or sets the IInputElement to pass into the ShowDialog() method as the command source.
199         /// If null (default), _this_ will be passed in.
200         /// Note: ShowDialog() method is called on a DialogPropertyValueEditor instance if a dialog editor
201         /// is invoked and the editor does not specify any dialog DataTemplate.
202         /// </summary>
203         public static readonly DependencyProperty DialogCommandSourceProperty =
204             DependencyProperty.Register(
205                 "DialogCommandSource",
206                 typeof(IInputElement),
207                 typeof(PropertyContainer),
208                 new PropertyMetadata((IInputElement)null));
209
210         /// <summary>
211         /// Gets or sets the IInputElement to pass into the ShowDialog() method as the command source.
212         /// If null (default), _this_ will be passed in.
213         /// Note: ShowDialog() method is called on a DialogPropertyValueEditor instance if a dialog editor
214         /// is invoked and the editor does not specify any dialog DataTemplate.
215         /// </summary>
216         [Fx.Tag.KnownXamlExternalAttribute]
217         public IInputElement DialogCommandSource
218         {
219             get { return (IInputElement)this.GetValue(DialogCommandSourceProperty); }
220             set { this.SetValue(DialogCommandSourceProperty, value); }
221         }
222
223
224         // OwningPropertyContainer Attached, Inherited DP
225
226         /// <summary>
227         /// Attached, inherited DP that can be used by UI elements of PropertyValueEditors
228         /// to gain access to their parent PropertyContainer.
229         /// </summary>
230         public static readonly DependencyProperty OwningPropertyContainerProperty =
231             DependencyProperty.RegisterAttached(
232                 "OwningPropertyContainer",
233                 typeof(PropertyContainer),
234                 typeof(PropertyContainer),
235                 new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.Inherits));
236
237         /// <summary>
238         /// Setter for attached, inherited DP that can be used by UI elements of PropertyValueEditors
239         /// to gain access to their parent PropertyContainer.
240         /// </summary>
241         /// <param name="dependencyObject">The DO to set the property on</param>
242         /// <param name="value">The Owning PropertyContainer</param>
243         public static void SetOwningPropertyContainer(DependencyObject dependencyObject, PropertyContainer value)
244         {
245             if (dependencyObject == null)
246                 throw FxTrace.Exception.ArgumentNull("dependencyObject");
247
248             dependencyObject.SetValue(PropertyContainer.OwningPropertyContainerProperty, value);
249         }
250
251         /// <summary>
252         /// Getter for attached, inherited DP that can be used by UI elements of PropertyValueEditors
253         /// to gain access to their parent PropertyContainer.
254         /// </summary>
255         /// <param name="dependencyObject">The DO to get the property from</param>
256         /// <returns>The owning PropertyContainer</returns>
257         public static PropertyContainer GetOwningPropertyContainer(DependencyObject dependencyObject)
258         {
259             if (dependencyObject == null)
260                 throw FxTrace.Exception.ArgumentNull("dependencyObject");
261
262             return (PropertyContainer)dependencyObject.GetValue(PropertyContainer.OwningPropertyContainerProperty);
263         }
264
265
266         // ControlTemplates for PropertyContainer to define the UI for the different edit modes
267
268         // InlineRowTemplate DP
269
270         /// <summary>
271         /// This DP is used to get/set the InlineRowTemplate for the PropertyContainer.  The 
272         /// InlineRowTemplate defines how the PropertyContainer renders itself when 
273         /// ActiveEditMode = Inline.
274         /// </summary>
275         public static readonly DependencyProperty InlineRowTemplateProperty =
276             DependencyProperty.Register(
277                 "InlineRowTemplate",
278                 typeof(ControlTemplate),
279                 typeof(PropertyContainer),
280                 new FrameworkPropertyMetadata(
281                     null,
282                     FrameworkPropertyMetadataOptions.None,
283                     new PropertyChangedCallback(RowTemplateChanged)));
284
285         /// <summary>
286         /// Gets or sets the InlineRowTemplate for the PropertyContainer.  The 
287         /// InlineRowTemplate defines how the PropertyContainer renders itself when 
288         /// ActiveEditMode = Inline.
289         /// </summary>
290         [Fx.Tag.KnownXamlExternalAttribute]
291         public ControlTemplate InlineRowTemplate
292         {
293             get { return (ControlTemplate)this.GetValue(PropertyContainer.InlineRowTemplateProperty); }
294             set { this.SetValue(PropertyContainer.InlineRowTemplateProperty, value); }
295         }
296
297         // Called when any of the row templates (InlineRowTemplate, ExtendedPopupRowTemplate, ExtendedPinnedRowTemplate)
298         // change
299         private static void RowTemplateChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
300         {
301             PropertyContainer theThis = (PropertyContainer)obj;
302             bool updateControlTemplate = false;
303
304             // Check InlineRowTemplate
305             updateControlTemplate = updateControlTemplate |
306                 (e.Property == PropertyContainer.InlineRowTemplateProperty &&
307                 theThis.ActiveEditMode == PropertyContainerEditMode.Inline);
308
309             // Check ExtendedPopup
310             updateControlTemplate = updateControlTemplate |
311                 (e.Property == PropertyContainer.ExtendedPopupRowTemplateProperty &&
312                 theThis.ActiveEditMode == PropertyContainerEditMode.ExtendedPopup);
313
314             // Check ExtendedPinned
315             updateControlTemplate = updateControlTemplate |
316                 (e.Property == PropertyContainer.ExtendedPinnedRowTemplateProperty &&
317                 theThis.ActiveEditMode == PropertyContainerEditMode.ExtendedPinned);
318
319             if (updateControlTemplate)
320                 UpdateControlTemplate(theThis);
321         }
322
323
324         // ExtendedPopupRowTemplate DP
325
326         /// <summary>
327         /// This DP is used to get/set the ExtendedPopupRowTemplate for this PropertyContainer.
328         /// The ExtendedPopupRowTemplate defines how the PropertyContainer renders itself when 
329         /// ActiveEditMode = ExtendedPopup.  Generally, host implementations will define this
330         /// template to automatically include the InlineRowTemplate as well.
331         /// </summary>
332         public static readonly DependencyProperty ExtendedPopupRowTemplateProperty =
333             DependencyProperty.Register(
334                 "ExtendedPopupRowTemplate",
335                 typeof(ControlTemplate),
336                 typeof(PropertyContainer),
337                 new FrameworkPropertyMetadata(
338                     null,
339                     FrameworkPropertyMetadataOptions.None,
340                     new PropertyChangedCallback(RowTemplateChanged)));
341
342         /// <summary>
343         /// Gets or sets the ExtendedPopupRowTemplate for this PropertyContainer.  
344         /// The ExtendedPopupRowTemplate defines how the PropertyContainer renders itself when 
345         /// ActiveEditMode = ExtendedPopup.  Generally, host implementations will define this
346         /// template to automatically include the InlineRowTemplate as well.
347         /// </summary>
348         [Fx.Tag.KnownXamlExternalAttribute]
349         public ControlTemplate ExtendedPopupRowTemplate
350         {
351             get { return (ControlTemplate)this.GetValue(PropertyContainer.ExtendedPopupRowTemplateProperty); }
352             set { this.SetValue(PropertyContainer.ExtendedPopupRowTemplateProperty, value); }
353         }
354
355
356         // ExtendedPinnedRowTemplate DP
357
358         /// <summary>
359         /// This DP is used to get/set the ExtendedPinnedRowTemplate for this PropertyContainer.  
360         /// The ExtendedPinnedRowTemplate defines how the PropertyContainer renders itself when 
361         /// ActiveEditMode = ExtendedPinned.  Generally, host implementations will define this
362         /// template to automatically include the InlineRowTemplate as well.
363         /// </summary>
364         public static readonly DependencyProperty ExtendedPinnedRowTemplateProperty =
365             DependencyProperty.Register(
366                 "ExtendedPinnedRowTemplate",
367                 typeof(ControlTemplate),
368                 typeof(PropertyContainer),
369                 new FrameworkPropertyMetadata(
370                     null,
371                     FrameworkPropertyMetadataOptions.None,
372                     new PropertyChangedCallback(RowTemplateChanged)));
373
374         /// <summary>
375         /// Get/set the ExtendedPinnedRowTemplate for this PropertyContainer.  
376         /// The ExtendedPinnedRowTemplate defines how the PropertyContainer renders itself when 
377         /// ActiveEditMode = ExtendedPinned.  Generally, host implementations will define this
378         /// template to automatically include the InlineRowTemplate as well.
379         /// </summary>
380         [Fx.Tag.KnownXamlExternalAttribute]
381         public ControlTemplate ExtendedPinnedRowTemplate
382         {
383             get { return (ControlTemplate)this.GetValue(PropertyContainer.ExtendedPinnedRowTemplateProperty); }
384             set { this.SetValue(PropertyContainer.ExtendedPinnedRowTemplateProperty, value); }
385         }
386
387
388         // Default PropertyValueEditors to use when a given property doesn't specify its own
389
390         // DefaultStandardValuesPropertyValueEditor DP
391
392         /// <summary>
393         /// DP to get or set the default standard-values editor which is used when a PropertyEntry supports
394         /// StandardValues (enum or through a TypeConverter) and there isn't a PropertyValueEditor
395         /// defined for the PropertyEntry or Type explicitely.
396         /// </summary>
397         public static readonly DependencyProperty DefaultStandardValuesPropertyValueEditorProperty =
398             DependencyProperty.Register(
399                 "DefaultStandardValuesPropertyValueEditor",
400                 typeof(PropertyValueEditor),
401                 typeof(PropertyContainer),
402                 new FrameworkPropertyMetadata(
403                     null,
404                     new PropertyChangedCallback(DefaultPropertyValueEditorChanged)));
405
406         /// <summary>
407         /// Gets or set the default standard-values editor which is used when a PropertyEntry supports
408         /// StandardValues (enum or through a TypeConverter) and there isn't a PropertyValueEditor
409         /// defined for the PropertyEntry or Type explicitely.
410         /// </summary>
411         [Fx.Tag.KnownXamlExternalAttribute]
412         public PropertyValueEditor DefaultStandardValuesPropertyValueEditor
413         {
414             get { return (PropertyValueEditor)this.GetValue(PropertyContainer.DefaultStandardValuesPropertyValueEditorProperty); }
415             set { this.SetValue(PropertyContainer.DefaultStandardValuesPropertyValueEditorProperty, value); }
416         }
417
418         private static void DefaultPropertyValueEditorChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
419         {
420             PropertyContainer theThis = (PropertyContainer)obj;
421
422             // Since one of the default PVE's has changed, chances are that the Inline or the Extended
423             // editor template has changed as well.
424             theThis.NotifyTemplatesChanged();
425         }
426
427
428         // DefaultPropertyValueEditor DP
429
430         /// <summary>
431         /// DP to get or set the default PropertyValueEditor which is the editor used when the
432         /// PropertyEntry or Type does not explicitely define its own PropertyValueEditor and does not
433         /// support StandardValues.
434         /// </summary>
435         public static readonly DependencyProperty DefaultPropertyValueEditorProperty =
436             DependencyProperty.Register(
437                 "DefaultPropertyValueEditor",
438                 typeof(PropertyValueEditor),
439                 typeof(PropertyContainer),
440                 new FrameworkPropertyMetadata(
441                     null,
442                     new PropertyChangedCallback(DefaultPropertyValueEditorChanged)));
443
444         /// <summary>
445         /// Gets or sets the default PropertyValueEditor which is the editor used when the
446         /// PropertyEntry or Type does not explicitely define its own PropertyValueEditor and does not
447         /// support StandardValues.
448         /// </summary>
449         [Fx.Tag.KnownXamlExternalAttribute]
450         public PropertyValueEditor DefaultPropertyValueEditor
451         {
452             get { return (PropertyValueEditor)this.GetValue(PropertyContainer.DefaultPropertyValueEditorProperty); }
453             set { this.SetValue(PropertyContainer.DefaultPropertyValueEditorProperty, value); }
454         }
455
456
457         // Regular properties (read-only values for DataBinding)
458
459         // InlineEditorTemplate read-only CLR property
460
461         /// <summary>
462         /// Gets the most appropriate InlineEditorTemplate for the current PropertyEntry.
463         /// A row template may decide to use this value to render the editor on the appropriate place.
464         /// </summary>
465         [Fx.Tag.KnownXamlExternalAttribute]
466         public DataTemplate InlineEditorTemplate
467         {
468             get
469             {
470                 return FindPropertyValueEditorTemplate(PropertyContainerEditMode.Inline);
471             }
472         }
473
474
475         // ExtendedEditorTemplate read-only CLR property
476
477         /// <summary>
478         /// Gets the most appropriate ExtendedEditorTemplate for the current PropertyEntry.
479         /// A row template may decide to use this value to render the editor on the appropriate place.
480         /// </summary>
481         [Fx.Tag.KnownXamlExternalAttribute]
482         public DataTemplate ExtendedEditorTemplate
483         {
484             get
485             {
486                 return FindPropertyValueEditorTemplate(PropertyContainerEditMode.ExtendedPinned);
487             }
488         }
489
490
491         // DialogEditorTemplate read-only CLR property
492
493         /// <summary>
494         /// Gets the most appropriate DialogEditorTemplate for the current PropertyEntry.
495         /// A row template or a Dialog may decide to use this value to render the editor on the
496         /// appropriate place.
497         /// </summary>
498         [Fx.Tag.KnownXamlExternalAttribute]
499         public DataTemplate DialogEditorTemplate
500         {
501             get
502             {
503                 return FindPropertyValueEditorTemplate(PropertyContainerEditMode.Dialog);
504             }
505         }
506
507
508         // MatchesFilter read-only CLR property
509
510         /// <summary>
511         /// Gets the value for MatchesFilter stored in the contained PropertyEntry.  If the PropertyEntry
512         /// is null, the value returned is false.
513         /// This property can be used to trigger UI changes to the PropertyContainer based on
514         /// whether the current PropertyEntry matches the current filter or not.
515         /// </summary>
516         public bool MatchesFilter
517         {
518             get
519             {
520                 PropertyEntry property = this.PropertyEntry;
521                 return property != null && property.MatchesFilter;
522             }
523         }
524
525
526         // OpenDialogWindow static, read-only command property
527
528         /// <summary>
529         /// Gets the command that is fired when someone changes the ActiveEditMode property to "Dialog".
530         /// The host may choose to handle this command and, display the DialogEditorTemplate
531         /// (if one exists) in a host-specific dialog container.  If the host does not handle
532         /// this command (OpenDialogWindow.CanExecute is false), the PropertyContainer itself
533         /// defaults to calling into the virtual DialogPropertyValueEditor.ShowDialog()
534         /// method, but only if DialogPropertyValueEditor is found.
535         /// </summary>
536         public static RoutedCommand OpenDialogWindow
537         {
538             get
539             {
540                 if (_openDialogWindow == null)
541                     _openDialogWindow = new RoutedCommand("OpenDialogWindow", typeof(PropertyContainer));
542
543                 return _openDialogWindow;
544             }
545         }
546
547
548         internal bool SupportsEditMode(PropertyContainerEditMode mode)
549         {
550             // special handling for dialog editor
551             if (mode == PropertyContainerEditMode.Dialog)
552                 return FindDialogPropertyValueEditor() != null;
553
554             // for everything else
555             return FindPropertyValueEditorTemplate(mode) != null;
556         }
557
558         // When the control gets unloaded, unhook any remaining event handlers
559         // so that it can be garbage collected
560         private void OnUnloaded(object sender, RoutedEventArgs e)
561         {
562             PropertyEntry entry = this.PropertyEntry;
563             if (entry != null)
564                 DisassociatePropertyEventHandlers(entry);
565         }
566
567         // When the control gets re-loaded, re-hook any previous event handlers
568         // that may have been disassociated during Unload
569         private void OnLoaded(object sender, RoutedEventArgs e)
570         {
571             PropertyEntry entry = this.PropertyEntry;
572             if (entry != null)
573                 AssociatePropertyEventHandlers(entry);
574         }
575
576         // Helper that hooks into PropertyChanged events on the given Property
577         private void AssociatePropertyEventHandlers(PropertyEntry property)
578         {
579             if (!_attachedToPropertyEntryEvents)
580             {
581                 property.PropertyChanged += new PropertyChangedEventHandler(OnPropertyPropertyChanged);
582                 _attachedToPropertyEntryEvents = true;
583             }
584         }
585
586         // Helper that unhooks from PropertyChanged events on the given Property
587         private void DisassociatePropertyEventHandlers(PropertyEntry property)
588         {
589             if (_attachedToPropertyEntryEvents)
590             {
591                 property.PropertyChanged -= new PropertyChangedEventHandler(OnPropertyPropertyChanged);
592                 _attachedToPropertyEntryEvents = false;
593             }
594         }
595
596         // Called when the properties of the Property object change
597         private void OnPropertyPropertyChanged(object sender, PropertyChangedEventArgs e)
598         {
599             // Propagate MatchesFilter change notifications outwards so that
600             // people can set up triggers against it
601             if ("MatchesFilter".Equals(e.PropertyName))
602                 this.OnPropertyChanged("MatchesFilter");
603             else if ("PropertyValueEditor".Equals(e.PropertyName))
604                 this.NotifyTemplatesChanged();
605         }
606
607         /// <summary>
608         /// Helper class that attempts to find the DataTemplate for the requested PropertyContainerEditMode
609         /// given the currently displayed PropertyEntry and the currently set default PropertyValueEditors.
610         /// 
611         /// If the requested DataTemplate is found on the PropertyValueEditor associated with the displayed
612         /// control, is it returned.
613         /// 
614         /// Otherwise if the requested DataTemplate is found on DefaultStandardValuesPropertyValueEditor,
615         /// it is returned.
616         /// 
617         /// Otherwise if the requested DataTemplate is found on DefaultPropertyValueEditor,
618         /// it is returned.
619         /// 
620         /// Otherwise null is returned.
621         /// </summary>
622         /// <param name="editMode">The editMode for which the DataTemplate should be retrieved</param>
623         /// <returns>Most relevant DataTemplate for the specified edit mode based on the currently
624         /// displayed PropertyEntry and the current set of default PropertyValueEditors, or null if not
625         /// found.</returns>
626         private DataTemplate FindPropertyValueEditorTemplate(PropertyContainerEditMode editMode)
627         {
628             PropertyEntry property = this.PropertyEntry;
629             PropertyValueEditor editor = null;
630             DataTemplate requestedTemplate = null;
631
632             // Look at property
633             if (property != null)
634             {
635                 editor = property.PropertyValueEditor;
636                 if (editor != null)
637                 {
638                     requestedTemplate = editor.GetPropertyValueEditor(editMode);
639                 }
640             }
641
642             if (requestedTemplate != null)
643                 return requestedTemplate;
644
645             // Is the property of type enum and used as flags?
646             if (IsFlagsProperty(property))
647             {
648                 requestedTemplate = this.GetFlagEditorTemplate(editMode);
649             }
650
651             if (requestedTemplate != null)
652             {
653                 return requestedTemplate;
654             }
655
656             // Does the property have standard values?
657             if (property != null && property.HasStandardValuesInternal)
658             {
659                 editor = this.DefaultStandardValuesPropertyValueEditor;
660                 if (editor != null)
661                 {
662                     requestedTemplate = editor.GetPropertyValueEditor(editMode);
663                 }
664             }
665
666             if (requestedTemplate != null)
667                 return requestedTemplate;
668
669             // Use the default
670             editor = this.DefaultPropertyValueEditor;
671             if (editor != null)
672             {
673                 requestedTemplate = editor.GetPropertyValueEditor(editMode);
674             }
675
676             return requestedTemplate;
677         }
678
679         bool IsFlagsProperty(PropertyEntry property)
680         {
681             return property != null && property.PropertyType != null && property.PropertyType.IsEnum &&
682                    ExtensibilityAccessor.GetAttribute<FlagsAttribute>(property.PropertyType) != null;
683         }
684
685         DataTemplate GetFlagEditorTemplate(PropertyContainerEditMode editMode)
686         {
687             Type propertyType = this.PropertyEntry.PropertyType;
688             if (editMode == PropertyContainerEditMode.Inline)
689             {
690                 if (this.flagEditorTemplate == null)
691                 {
692                     this.flagEditorTemplate = new DataTemplate();
693                     this.flagEditorTemplate.VisualTree = new FrameworkElementFactory(typeof(FlagEditor));
694                     this.flagEditorTemplate.VisualTree.SetValue(FlagEditor.FlagTypeProperty, propertyType);
695                     Binding binding = new Binding("Value");
696                     binding.Converter = new FlagStringConverter();
697                     binding.ConverterParameter = propertyType;
698                     binding.UpdateSourceTrigger = UpdateSourceTrigger.Explicit;
699                     this.flagEditorTemplate.VisualTree.SetBinding(FlagEditor.TextProperty, binding);
700                 }
701                 return this.flagEditorTemplate;
702             }
703             else
704             {
705                 return null;
706             }
707         }
708
709         // Helper that tries to find the first applicable DialogPropertyValueEditor
710         private DialogPropertyValueEditor FindDialogPropertyValueEditor()
711         {
712             PropertyEntry property = this.PropertyEntry;
713             DialogPropertyValueEditor editor = null;
714
715             // Look at property
716             if (property != null)
717             {
718                 editor = property.PropertyValueEditor as DialogPropertyValueEditor;
719             }
720
721             if (editor != null)
722                 return editor;
723
724             // Does the property have standard values?
725             if (property != null && property.HasStandardValuesInternal)
726             {
727                 editor = this.DefaultStandardValuesPropertyValueEditor as DialogPropertyValueEditor;
728             }
729
730             if (editor != null)
731                 return editor;
732
733             // Use the default
734             editor = this.DefaultPropertyValueEditor as DialogPropertyValueEditor;
735
736             return editor;
737         }
738
739         // Updates the ControlTemplate of this control based on the currently ActiveEditMode
740         private static void UpdateControlTemplate(PropertyContainer container)
741         {
742
743             PropertyContainerEditMode editMode = container.ActiveEditMode;
744             ControlTemplate newTemplate = null;
745
746             switch (editMode)
747             {
748                 case PropertyContainerEditMode.Inline:
749                     newTemplate = container.InlineRowTemplate;
750                     break;
751                 case PropertyContainerEditMode.ExtendedPopup:
752                     newTemplate = container.ExtendedPopupRowTemplate;
753                     break;
754                 case PropertyContainerEditMode.ExtendedPinned:
755                     newTemplate = container.ExtendedPinnedRowTemplate;
756                     break;
757                 case PropertyContainerEditMode.Dialog:
758                     // In dialog case, just keep the same value
759                     return;
760                 default:
761                     Debug.Fail(
762                         string.Format(
763                             System.Globalization.CultureInfo.CurrentCulture,
764                             "PropertyContainerEditMode does not yet support PropertyContainerEditMode '{0}'.",
765                             editMode.ToString()));
766                     newTemplate = container.Template;
767                     break;
768             }
769
770             if (newTemplate != container.Template)
771                 container.Template = newTemplate;
772         }
773
774         private void NotifyTemplatesChanged()
775         {
776             OnPropertyChanged("InlineEditorTemplate");
777             OnPropertyChanged("ExtendedEditorTemplate");
778             OnPropertyChanged("DialogEditorTemplate");
779         }
780
781         /// <summary>
782         /// Called when a property changes
783         /// </summary>
784         /// <param name="propertyName">Name of the property</param>
785         protected virtual void OnPropertyChanged(string propertyName)
786         {
787             if (PropertyChanged != null)
788                 PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
789         }
790
791         /// <summary>
792         /// Called when a property changes
793         /// </summary>
794         /// <param name="e">Name of the property</param>
795         protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
796         {
797             if (DependencyPropertyChanged != null)
798                 DependencyPropertyChanged(this, e);
799
800             base.OnPropertyChanged(e);
801         }
802     }
803 }
804