1 namespace System.Activities.Presentation.PropertyEditing {
4 using System.Windows.Input;
5 using System.Windows.Controls;
6 using System.Diagnostics.CodeAnalysis;
7 using System.Diagnostics;
10 /// Convenience button that allows the user to switch between the different PropertyContainer modes.
11 /// This button is styled to follow the look and feel specific to the host application. It can operate
12 /// in two modes - either it always executes a specified mode-switching command, or it adapts to
13 /// the current mode of the containing PropertyContainer and "does the right thing". If set manually,
14 /// SyncModeToOwningContainer must be set to false and the mode-switching command needs to be specified
15 /// using the TargetEditMode property. To set the mode automatically, SyncModeToOwningContainer must
16 /// be set to true in which case the TargetEditMode property is ignored.
18 [SuppressMessage("Microsoft.Maintainability", "CA1501:AvoidExcessiveInheritance")]
19 public class EditModeSwitchButton : Button {
21 private PropertyContainer _owningContainer;
22 private bool _attachedToContainerEvents;
27 [SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
28 public EditModeSwitchButton() {
29 this.Loaded += new RoutedEventHandler(OnLoaded);
30 this.Unloaded += new RoutedEventHandler(OnUnloaded);
31 this.FontSize = SystemFonts.IconFontSize;
32 this.FontFamily = SystemFonts.IconFontFamily;
33 this.FontWeight = SystemFonts.IconFontWeight;
39 /// The mode to switch to when this control is clicked. Only used when
40 /// SyncModeToOwningContainer is set to false. Defaults to Inline.
42 public static readonly DependencyProperty TargetEditModeProperty = DependencyProperty.Register(
44 typeof(PropertyContainerEditMode),
45 typeof(EditModeSwitchButton),
46 new FrameworkPropertyMetadata(
47 PropertyContainerEditMode.Inline,
48 null, // PropertyChangedCallback
49 new CoerceValueCallback(OnCoerceEditModeProperty)));
52 /// The mode to switch to when this control is clicked. Only used when
53 /// SyncModeToOwningContainer is set to false. Defaults to Inline.
55 public PropertyContainerEditMode TargetEditMode {
56 get { return (PropertyContainerEditMode) this.GetValue(TargetEditModeProperty); }
57 set { this.SetValue(TargetEditModeProperty, value); }
60 private static object OnCoerceEditModeProperty(DependencyObject obj, object value) {
62 EditModeSwitchButton theThis = (EditModeSwitchButton) obj;
64 // [....] to the owning PropertyContainer only if requested to do so
65 if (!theThis.SyncModeToOwningContainer)
68 // Do we have an owning PropertyContainer?
69 if (theThis._owningContainer == null)
72 PropertyContainerEditMode newMode;
73 PropertyContainer owningContainer = theThis._owningContainer;
75 switch (owningContainer.ActiveEditMode) {
76 case PropertyContainerEditMode.Inline:
77 // when clicked, have this button switch to extended popup mode
78 // or dialog mode (dialog takes precedence)
79 if (owningContainer.SupportsEditMode(PropertyContainerEditMode.Dialog))
80 newMode = PropertyContainerEditMode.Dialog;
81 else if (owningContainer.SupportsEditMode(PropertyContainerEditMode.ExtendedPopup))
82 newMode = PropertyContainerEditMode.ExtendedPopup;
84 newMode = PropertyContainerEditMode.Inline;
88 case PropertyContainerEditMode.ExtendedPopup:
89 // when clicked, have this button switch to extended pinned mode
90 newMode = PropertyContainerEditMode.ExtendedPinned;
93 case PropertyContainerEditMode.ExtendedPinned:
94 // when clicked, have this button switch to inline mode
95 newMode = PropertyContainerEditMode.Inline;
98 case PropertyContainerEditMode.Dialog:
100 newMode = theThis.TargetEditMode;
104 Debug.Fail(string.Format(
105 System.Globalization.CultureInfo.CurrentCulture,
106 "ModeSwitchControl does not yet support PropertyContainerEditMode '{0}'.",
107 owningContainer.ActiveEditMode.ToString()));
108 newMode = (PropertyContainerEditMode) value;
116 // SyncModeToOwningContainer DP
119 /// When set to true, the TargetEditMode will be calculated automatically to match the ActiveEditMode
120 /// of the owning PropertyContainer. Otherwise, the mode to switch to will be based on the
121 /// TargetEditMode property.
123 public static readonly DependencyProperty SyncModeToOwningContainerProperty = DependencyProperty.Register(
124 "SyncModeToOwningContainer",
126 typeof(EditModeSwitchButton),
127 new FrameworkPropertyMetadata(true, new PropertyChangedCallback(OnSyncModeToOwningContainerChanged)));
130 /// When set to true, the TargetEditMode will be calculated automatically to match the ActiveEditMode
131 /// of the owning PropertyContainer. Otherwise, the mode to switch to will be based on the
132 /// TargetEditMode property.
134 public bool SyncModeToOwningContainer {
135 get { return (bool)this.GetValue(SyncModeToOwningContainerProperty); }
136 set { this.SetValue(SyncModeToOwningContainerProperty, value); }
139 // When the SyncModeToOwningContainer changes, we may need to update the current
141 private static void OnSyncModeToOwningContainerChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e) {
142 EditModeSwitchButton theThis = (EditModeSwitchButton)obj;
143 theThis.CoerceValue(TargetEditModeProperty);
148 /// Called when any DependencyProperties of this Control change. If you override
149 /// this method, call the base implementation first to preserve the desired functionality.
151 /// <param name="e">Event args</param>
152 protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e) {
153 // Check to see if this control has changed (or gained or lost) its owning PropertyContainer.
154 // If so, it may need to update its state / appearance accordingly
155 if (e.Property == PropertyContainer.OwningPropertyContainerProperty) {
156 PropertyContainer oldContainer = (PropertyContainer)e.OldValue;
157 PropertyContainer newContainer = (PropertyContainer)e.NewValue;
158 _owningContainer = newContainer;
160 if (oldContainer != null)
161 DisassociateContainerEventHandlers(oldContainer);
163 if (newContainer != null)
164 AssociateContainerEventHandlers(newContainer);
166 this.CoerceValue(TargetEditModeProperty);
169 base.OnPropertyChanged(e);
172 private void OnPropertyContainerDependencyPropertyChanged(object sender, DependencyPropertyChangedEventArgs e) {
173 // All of these properties changing have the potential of affecting the appearance
175 if (e.Property == PropertyContainer.ActiveEditModeProperty ||
176 e.Property == PropertyContainer.PropertyEntryProperty ||
177 e.Property == PropertyContainer.DefaultStandardValuesPropertyValueEditorProperty ||
178 e.Property == PropertyContainer.DefaultPropertyValueEditorProperty) {
180 this.CoerceValue(TargetEditModeProperty);
184 private void AssociateContainerEventHandlers(PropertyContainer container) {
185 if (!_attachedToContainerEvents) {
186 container.DependencyPropertyChanged += new DependencyPropertyChangedEventHandler(OnPropertyContainerDependencyPropertyChanged);
187 _attachedToContainerEvents = true;
191 private void DisassociateContainerEventHandlers(PropertyContainer container) {
192 if (_attachedToContainerEvents) {
193 container.DependencyPropertyChanged -= new DependencyPropertyChangedEventHandler(OnPropertyContainerDependencyPropertyChanged);
194 _attachedToContainerEvents = false;
199 // When the control gets unloaded, unhook any remaining event handlers
200 // so that it can be garbage collected
201 private void OnUnloaded(object sender, RoutedEventArgs e) {
202 if (_owningContainer != null)
203 DisassociateContainerEventHandlers(_owningContainer);
206 // When the control gets re-loaded somewhere else in the tree, re-hook the
207 // event handlers back up
208 private void OnLoaded(object sender, RoutedEventArgs e) {
209 if (_owningContainer != null)
210 AssociateContainerEventHandlers(_owningContainer);
214 /// Because of the nature of the popup capturing mouse and disappearing when the
215 /// user clicks outside of it, to pin an ExtendedEditor we listen to MouseDown event
216 /// rather than the Click event.
218 /// <param name="e">Event args</param>
219 protected override void OnMouseDown(MouseButtonEventArgs e) {
221 if (e.LeftButton == MouseButtonState.Pressed) {
223 // Invoke the appropriate command
224 switch (this.TargetEditMode) {
225 case PropertyContainerEditMode.Inline:
226 PropertyValueEditorCommands.ShowInlineEditor.Execute(null, this);
228 case PropertyContainerEditMode.ExtendedPopup:
229 PropertyValueEditorCommands.ShowExtendedPopupEditor.Execute(null, this);
231 case PropertyContainerEditMode.ExtendedPinned:
232 PropertyValueEditorCommands.ShowExtendedPinnedEditor.Execute(null, this);
234 case PropertyContainerEditMode.Dialog:
235 PropertyValueEditorCommands.ShowDialogEditor.Execute(null, this);
238 Debug.Fail(string.Format(
239 System.Globalization.CultureInfo.CurrentCulture,
240 "ModeSwitchControl does not yet support PropertyContainerEditMode '{0}'.",
241 this.TargetEditMode.ToString()));