1 // -------------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation. All rights reserved.
3 // -------------------------------------------------------------------
4 //From \\authoring\Sparkle\Source\1.0.1083.0\Common\Source\Framework\ValueEditors
5 namespace System.Activities.Presentation.Internal.PropertyEditing.FromExpression.Framework.ValueEditors
9 using System.Collections;
10 using System.ComponentModel;
11 using System.Diagnostics;
12 using System.Diagnostics.CodeAnalysis;
14 using System.Windows.Controls;
15 using System.Windows.Data;
16 using System.Windows.Input;
17 using System.Windows.Media;
18 using System.Windows.Controls.Primitives;
19 using System.Activities.Presentation.Internal.PropertyEditing.FromExpression.Framework.Data;
23 // Determines the view type of the choice editor.
24 // Combo - a combo box
25 // Buttons - a set of radio buttons with icons
26 // ToggleButtons - a set of radio buttons with icons that use the ToggleIcon style, which allows separate active and inactive icons.
27 // Toggle - a single toggle, good for values that have only two choices, like Boolean
29 internal enum ChoiceEditorViewType
37 // A ChoiceEditor selects a single item from a list of choices. Think combobox or set of radio buttons.
39 internal class ChoiceEditor : Control, INotifyPropertyChanged, IIconProvider
41 public static readonly DependencyProperty ValueProperty = DependencyProperty.Register("Value", typeof(object), typeof(ChoiceEditor), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, new PropertyChangedCallback(ChoiceEditor.ValueChanged), null, false, UpdateSourceTrigger.Explicit));
42 public static readonly DependencyProperty ValueIndexProperty = DependencyProperty.Register("ValueIndex", typeof(int), typeof(ChoiceEditor), new FrameworkPropertyMetadata(-1, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, new PropertyChangedCallback(ChoiceEditor.ValueIndexChanged)));
43 public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.Register("ItemsSource", typeof(IEnumerable), typeof(ChoiceEditor), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.None, new PropertyChangedCallback(ChoiceEditor.ItemsSourceChanged)));
44 public static readonly DependencyProperty ConverterProperty = DependencyProperty.Register("Converter", typeof(TypeConverter), typeof(ChoiceEditor), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.None));
46 public static readonly DependencyProperty ViewTypeProperty = DependencyProperty.Register("ViewType", typeof(ChoiceEditorViewType), typeof(ChoiceEditor), new FrameworkPropertyMetadata(ChoiceEditorViewType.Combo, FrameworkPropertyMetadataOptions.AffectsRender));
47 public static readonly DependencyProperty IsEditableProperty = DependencyProperty.Register("IsEditable", typeof(bool), typeof(ChoiceEditor), new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.None, new PropertyChangedCallback(ChoiceEditor.IsEditableChanged)));
48 public static readonly DependencyProperty IconResourcePrefixProperty = DependencyProperty.Register("IconResourcePrefix", typeof(string), typeof(ChoiceEditor), new FrameworkPropertyMetadata(null));
49 public static readonly DependencyProperty IconResourceSuffixProperty = DependencyProperty.Register("IconResourceSuffix", typeof(string), typeof(ChoiceEditor), new FrameworkPropertyMetadata("Icon"));
50 public static readonly DependencyProperty IsNinchedProperty = DependencyProperty.Register("IsNinched", typeof(bool), typeof(ChoiceEditor), new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.None, new PropertyChangedCallback(ChoiceEditor.IsNinchedChanged)));
51 public static readonly DependencyProperty ShowFullControlProperty = DependencyProperty.Register("ShowFullControl", typeof(bool), typeof(ChoiceEditor), new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.AffectsRender, null, new CoerceValueCallback(ChoiceEditor.CoerceShowFullControl)));
52 public static readonly DependencyProperty ItemTemplateProperty = DependencyProperty.Register("ItemTemplate", typeof(DataTemplate), typeof(ChoiceEditor), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsArrange | FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsRender, new PropertyChangedCallback(ChoiceEditor.ItemTemplateChanged)));
53 public static readonly DependencyProperty ItemTemplateSelectorProperty = DependencyProperty.Register("ItemTemplateSelector", typeof(DataTemplateSelector), typeof(ChoiceEditor), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsArrange | FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsRender, new PropertyChangedCallback(ChoiceEditor.ItemTemplateSelectorChanged)));
54 public static readonly DependencyProperty UseItemTemplateForSelectionProperty = DependencyProperty.Register("UseItemTemplateForSelection", typeof(Nullable<bool>), typeof(ChoiceEditor), new FrameworkPropertyMetadata(null, null, new CoerceValueCallback(ChoiceEditor.CoerceUseItemTemplateForSelection)));
56 public static readonly DependencyProperty BorderCornerRadiusProperty = DependencyProperty.Register("BorderCornerRadius", typeof(double), typeof(ChoiceEditor), new FrameworkPropertyMetadata(0d, FrameworkPropertyMetadataOptions.AffectsRender));
57 public static readonly DependencyProperty DropButtonInsetProperty = DependencyProperty.Register("DropButtonInset", typeof(Thickness), typeof(ChoiceEditor), new FrameworkPropertyMetadata(new Thickness(), FrameworkPropertyMetadataOptions.AffectsRender));
58 public static readonly DependencyProperty TextAreaInsetProperty = DependencyProperty.Register("TextAreaInset", typeof(Thickness), typeof(ChoiceEditor), new FrameworkPropertyMetadata(new Thickness(), FrameworkPropertyMetadataOptions.AffectsRender));
59 public static readonly DependencyProperty DropButtonBrushProperty = DependencyProperty.Register("DropButtonBrush", typeof(Brush), typeof(ChoiceEditor), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsRender));
60 public static readonly DependencyProperty InnerCornerRadiusProperty = DependencyProperty.Register("InnerCornerRadius", typeof(double), typeof(ChoiceEditor), new FrameworkPropertyMetadata(0d, FrameworkPropertyMetadataOptions.AffectsRender));
61 public static readonly DependencyProperty ButtonIconProperty = DependencyProperty.Register("ButtonIcon", typeof(ImageSource), typeof(ChoiceEditor), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsRender));
62 public static readonly DependencyProperty IconWidthProperty = DependencyProperty.Register("IconWidth", typeof(double), typeof(ChoiceEditor), new FrameworkPropertyMetadata(0d, FrameworkPropertyMetadataOptions.AffectsRender));
63 public static readonly DependencyProperty IconHeightProperty = DependencyProperty.Register("IconHeight", typeof(double), typeof(ChoiceEditor), new FrameworkPropertyMetadata(0d, FrameworkPropertyMetadataOptions.AffectsRender));
65 public static readonly DependencyProperty BeginCommandProperty = DependencyProperty.Register("BeginCommand", typeof(ICommand), typeof(ChoiceEditor), new PropertyMetadata(null));
66 public static readonly DependencyProperty UpdateCommandProperty = DependencyProperty.Register("UpdateCommand", typeof(ICommand), typeof(ChoiceEditor), new PropertyMetadata(null));
67 public static readonly DependencyProperty CancelCommandProperty = DependencyProperty.Register("CancelCommand", typeof(ICommand), typeof(ChoiceEditor), new PropertyMetadata(null));
68 public static readonly DependencyProperty CommitCommandProperty = DependencyProperty.Register("CommitCommand", typeof(ICommand), typeof(ChoiceEditor), new PropertyMetadata(null));
69 public static readonly DependencyProperty FinishEditingCommandProperty = DependencyProperty.Register("FinishEditingCommand", typeof(ICommand), typeof(ChoiceEditor), new PropertyMetadata(null));
71 public static readonly DependencyProperty ComboBoxLoadingCursorProperty = DependencyProperty.Register("ComboBoxLoadingCursor", typeof(Cursor), typeof(ChoiceEditor), new PropertyMetadata(null));
73 // WORKAROUND this property is used in combination with a trigger to kick the combobox when it clears its bindings Avalon bug: 1756023
74 public static readonly DependencyProperty ForceBindingProperty = DependencyProperty.Register("ForceBinding", typeof(bool), typeof(ChoiceEditor), new FrameworkPropertyMetadata(false));
77 // True if the user is editing the text in an editable combo
78 private bool isTextEditing = false;
79 // True if the user is selecting a value (e.g. in a combo when the dropdown is open)
80 private bool isSelectingValue = false;
82 // ###################################################
83 // CIDER-SPECIFIC CHANGE IN NEED OF PORTING - BEGIN
84 // ###################################################
86 // True if the full editor is being shown
87 private bool isShowingFullEditor = false;
89 // ###################################################
90 // CIDER-SPECIFIC CHANGE IN NEED OF PORTING - END
91 // ###################################################
93 // Used to lock out internal changes. Do not change this directly. Use Begin/EndIgnoreInternalChangeBlock
94 private int internalChangeLockCount = 0;
95 // Used to not set commit action = lost focus when cancelling an edit
96 private int internalStringValueChangeLockCount = 0;
97 // True if changes to value should be ignored.
98 private bool ignoreValueChanges = false;
100 // Action to take when this control looses focus
101 private LostFocusAction lostFocusAction = LostFocusAction.None;
102 // The selected item of the internal selector. Used to determine Value
103 private object internalValue = null;
104 // The string value of the internal selector if it allows text editing
105 private string internalStringValue = String.Empty;
107 // A Collection view over the ItemsSource that we have been passed
108 private CollectionView collectionView = null;
110 [SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
111 public ChoiceEditor()
113 this.CoerceValue(ChoiceEditor.UseItemTemplateForSelectionProperty);
115 public event PropertyChangedEventHandler PropertyChanged;
118 // The currently selected choice
122 get { return this.GetValue(ChoiceEditor.ValueProperty); }
123 set { this.SetValue(ChoiceEditor.ValueProperty, value); }
127 // The index of the selected choice. -1 if Value is not in the list.
129 public int ValueIndex
131 get { return (int)this.GetValue(ChoiceEditor.ValueIndexProperty); }
132 set { this.SetValue(ChoiceEditor.ValueIndexProperty, value); }
136 // The items source used to populate the choice
138 public IEnumerable ItemsSource
140 get { return (IEnumerable)this.GetValue(ChoiceEditor.ItemsSourceProperty); }
141 set { this.SetValue(ChoiceEditor.ItemsSourceProperty, value); }
145 // Converter used to convert text values to object values.
147 public TypeConverter Converter
149 get { return (TypeConverter)this.GetValue(ChoiceEditor.ConverterProperty); }
150 set { this.SetValue(ChoiceEditor.ConverterProperty, value); }
154 // Sets the view type of this choice. This will change the behavior of the choice.
156 public ChoiceEditorViewType ViewType
158 get { return (ChoiceEditorViewType)this.GetValue(ChoiceEditor.ViewTypeProperty); }
159 set { this.SetValue(ChoiceEditor.ViewTypeProperty, value); }
163 // Set to true if this choice editor should be editable. Currently only works for Combo
165 public bool IsEditable
167 get { return (bool)this.GetValue(ChoiceEditor.IsEditableProperty); }
168 set { this.SetValue(ChoiceEditor.IsEditableProperty, value); }
172 // The prefix put on all icon resource references before they are looked up
174 public string IconResourcePrefix
176 get { return (string)this.GetValue(ChoiceEditor.IconResourcePrefixProperty); }
177 set { this.SetValue(ChoiceEditor.IconResourcePrefixProperty, value); }
181 // The prefix put on all icon resource references before they are looked up
183 public string IconResourceSuffix
185 get { return (string)this.GetValue(ChoiceEditor.IconResourceSuffixProperty); }
186 set { this.SetValue(ChoiceEditor.IconResourceSuffixProperty, value); }
190 // True if this value editor is ninched
192 public bool IsNinched
194 get { return (bool)this.GetValue(ChoiceEditor.IsNinchedProperty); }
195 set { this.SetValue(ChoiceEditor.IsNinchedProperty, value); }
199 // True to show the full control instead of the optimized drawing of the control
201 public bool ShowFullControl
203 get { return (bool)this.GetValue(ChoiceEditor.ShowFullControlProperty); }
204 set { this.SetValue(ChoiceEditor.ShowFullControlProperty, value); }
207 public DataTemplate ItemTemplate
209 get { return (DataTemplate)this.GetValue(ChoiceEditor.ItemTemplateProperty); }
210 set { this.SetValue(ChoiceEditor.ItemTemplateProperty, value); }
213 public DataTemplateSelector ItemTemplateSelector
215 get { return (DataTemplateSelector)this.GetValue(ChoiceEditor.ItemTemplateSelectorProperty); }
216 set { this.SetValue(ChoiceEditor.ItemTemplateSelectorProperty, value); }
219 public bool UseItemTemplateForSelection
221 get { return (bool)this.GetValue(ChoiceEditor.UseItemTemplateForSelectionProperty); }
222 set { this.SetValue(ChoiceEditor.UseItemTemplateForSelectionProperty, value); }
225 public double BorderCornerRadius
227 get { return (double)this.GetValue(ChoiceEditor.BorderCornerRadiusProperty); }
228 set { this.SetValue(ChoiceEditor.BorderCornerRadiusProperty, value); }
231 public Thickness DropButtonInset
233 get { return (Thickness)this.GetValue(ChoiceEditor.DropButtonInsetProperty); }
234 set { this.SetValue(ChoiceEditor.DropButtonInsetProperty, value); }
237 public Thickness TextAreaInset
239 get { return (Thickness)this.GetValue(ChoiceEditor.TextAreaInsetProperty); }
240 set { this.SetValue(ChoiceEditor.TextAreaInsetProperty, value); }
243 public Brush DropButtonBrush
245 get { return (Brush)this.GetValue(ChoiceEditor.DropButtonBrushProperty); }
246 set { this.SetValue(ChoiceEditor.DropButtonBrushProperty, value); }
249 public double InnerCornerRadius
251 get { return (double)this.GetValue(ChoiceEditor.InnerCornerRadiusProperty); }
252 set { this.SetValue(ChoiceEditor.InnerCornerRadiusProperty, value); }
255 public ImageSource ButtonIcon
257 get { return (ImageSource)this.GetValue(ChoiceEditor.ButtonIconProperty); }
258 set { this.SetValue(ChoiceEditor.ButtonIconProperty, value); }
261 public double IconWidth
263 get { return (double)this.GetValue(ChoiceEditor.IconWidthProperty); }
264 set { this.SetValue(ChoiceEditor.IconWidthProperty, value); }
267 public double IconHeight
269 get { return (double)this.GetValue(ChoiceEditor.IconHeightProperty); }
270 set { this.SetValue(ChoiceEditor.IconHeightProperty, value); }
274 // Command fired when editing begins
276 public ICommand BeginCommand
278 get { return (ICommand)this.GetValue(ChoiceEditor.BeginCommandProperty); }
279 set { this.SetValue(ChoiceEditor.BeginCommandProperty, value); }
283 // Command fired when an edit value is updated. This command will fire after the
284 // binding has been updated.
286 public ICommand UpdateCommand
288 get { return (ICommand)this.GetValue(ChoiceEditor.UpdateCommandProperty); }
289 set { this.SetValue(ChoiceEditor.UpdateCommandProperty, value); }
293 // Command fired when an edit is canceled
295 public ICommand CancelCommand
297 get { return (ICommand)this.GetValue(ChoiceEditor.CancelCommandProperty); }
298 set { this.SetValue(ChoiceEditor.CancelCommandProperty, value); }
302 // Command fired when an edit is commited
304 public ICommand CommitCommand
306 get { return (ICommand)this.GetValue(ChoiceEditor.CommitCommandProperty); }
307 set { this.SetValue(ChoiceEditor.CommitCommandProperty, value); }
311 // Command fired when the editor is done editing. At this point the host
312 // may decide to move on to the next property in the list, return focus
313 // to the design surface, or perform any other action it pleases to do.
315 public ICommand FinishEditingCommand
317 get { return (ICommand)this.GetValue(ChoiceEditor.FinishEditingCommandProperty); }
318 set { this.SetValue(ChoiceEditor.FinishEditingCommandProperty, value); }
322 // True if cursor should be an hourglass when loading a ComboBox popup
324 public Cursor ComboBoxLoadingCursor
326 get { return (Cursor)this.GetValue(ChoiceEditor.ComboBoxLoadingCursorProperty); }
327 set { this.SetValue(ChoiceEditor.ComboBoxLoadingCursorProperty, value); }
331 // Command to rotate the selection of the ChoiceEditor to the next possible item
332 // or cycle to the first if at the end of the current view.
334 public ICommand NextValueCommand
338 return new DelegateCommand(new DelegateCommand.SimpleEventHandler(SelectNextValue));
343 // Command to rotate the selection of the ChoiceEditor the previously selected
344 // item in the view, or to the last item in the view if at the first item in the view.
346 public ICommand PreviousValueCommand
350 return new DelegateCommand(new DelegateCommand.SimpleEventHandler(SelectPreviousValue));
354 public bool ForceBinding
356 get { return (bool)this.GetValue(ChoiceEditor.ForceBindingProperty); }
357 set { this.SetValue(ChoiceEditor.ForceBindingProperty, value); }
361 // This is the value internal to this control. Controls in the
362 // template of this control should bind to this.
364 public object InternalValue
368 return this.internalValue;
372 if (this.internalValue != value)
374 this.internalValue = value;
375 if (this.ShouldCommitInternalValueChanges)
377 if (!this.isTextEditing)
383 this.lostFocusAction = LostFocusAction.Commit;
386 this.SendPropertyChanged("InternalValue");
391 public string InternalStringValue
393 get { return this.internalStringValue; }
396 if (!String.Equals(value, this.internalStringValue))
398 if (this.ShouldCommitInternalStringValueChanges && this.isTextEditing)
400 this.InternalValue = null;
401 this.lostFocusAction = LostFocusAction.Commit;
403 this.internalStringValue = value;
404 this.SendPropertyChanged("InternalStringValue");
409 public bool InternalIsSelectingValue
411 get { return this.isSelectingValue; }
414 if (this.isSelectingValue != value)
416 this.isSelectingValue = value;
418 if (this.isTextEditing && !this.isSelectingValue)
421 // * FinishedEditingCommand in Cider does not
422 // * move focus off of the control (as we do in Sparkle) The fix is to add code to
423 // * InternalIsSelectingValue when the value goes to false (when the popup is closing)
424 // * that checks to see if there is a lostFocusAction of Commit and if so calls CommitChange()//
425 LostFocusAction oldAction = this.lostFocusAction;
426 this.lostFocusAction = LostFocusAction.None;
427 if (oldAction == LostFocusAction.Commit)
431 this.OnFinishEditing();
433 if (this.isSelectingValue && this.CollectionView != null)
435 this.BeginIgnoreExternalValueChangeBlock();
438 this.ValueIndex = this.CollectionView.IndexOf(this.Value);
442 this.EndIgnoreExternalValueChangeBlock();
445 Cursor loadingCursor = this.ComboBoxLoadingCursor;
446 if (value && this.ViewType == ChoiceEditorViewType.Combo && loadingCursor != null)
448 bool foundPopup = false;
449 Mouse.OverrideCursor = loadingCursor;
450 ComboBox comboBox = this.Template.FindName("PART_Combo", this) as ComboBox;
451 if (comboBox != null)
453 Popup popup = comboBox.Template.FindName("PART_Popup", comboBox) as Popup;
457 popup.Opened += new EventHandler(OnPopupLoaded);
462 // if we couldn't set up the event handler, just return
463 // the cursor now so they aren't stuck with an hourglass
464 Mouse.OverrideCursor = null;
471 // ###################################################
472 // CIDER-SPECIFIC CHANGE IN NEED OF PORTING - BEGIN
473 // ###################################################
475 private CollectionView CollectionView
478 if (collectionView == null)
480 IEnumerable newItems = this.ItemsSource;
481 if (newItems != null)
483 this.collectionView = new CollectionView(this.ItemsSource);
487 this.collectionView = null;
490 return collectionView;
494 // ###################################################
495 // CIDER-SPECIFIC CHANGE IN NEED OF PORTING - END
496 // ###################################################
498 private bool ShouldCommitInternalValueChanges
500 get { return this.internalChangeLockCount == 0; }
503 private bool ShouldCommitInternalStringValueChanges
505 get { return this.internalStringValueChangeLockCount == 0; }
508 private bool ShouldIgnoreExternalValueChanges
510 get { return this.ignoreValueChanges; }
514 public void SelectNextValue()
516 int currentIndex = this.ValueIndex;
518 Fx.Assert(this.CollectionView != null, "ChoiceEditor CollectionView cannot be null.");
519 if (currentIndex < 0 || currentIndex >= this.CollectionView.Count - 1)
525 currentIndex = currentIndex + 1;
527 this.ValueIndex = currentIndex;
530 public void SelectPreviousValue()
532 int currentIndex = this.ValueIndex;
534 Fx.Assert(this.CollectionView != null, "ChoiceEditor CollectionView cannot be null.");
535 if (currentIndex <= 0 || currentIndex > this.CollectionView.Count - 1)
537 currentIndex = this.CollectionView.Count - 1;
541 currentIndex = currentIndex - 1;
543 this.ValueIndex = currentIndex;
546 public ImageSource GetIconAsImageSource(object key, object parameter)
548 // This is the place to cache icons if these lookups are costing us too much.
551 StringBuilder sb = new StringBuilder();
552 string prefix = this.IconResourcePrefix;
553 string suffix = this.IconResourceSuffix;
554 string parameterString = parameter as string;
560 sb.Append(key.ToString());
565 if (parameterString != null)
567 sb.Append(parameterString);
570 object resource = this.FindResource(sb.ToString());
571 ImageSource resourceImageSource = resource as ImageSource;
572 return resourceImageSource;
574 catch (ResourceReferenceKeyNotFoundException)
580 private void OnPopupLoaded(object sender, EventArgs e)
582 Popup popup = sender as Popup;
584 Mouse.OverrideCursor = null;
587 popup.Opened -= new EventHandler(OnPopupLoaded);
591 private static void ValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
593 // Make sure the value actually changed
594 if (!object.Equals(e.OldValue, e.NewValue))
596 ChoiceEditor choice = d as ChoiceEditor;
597 if (choice != null && !choice.ShouldIgnoreExternalValueChanges)
599 choice.UpdateInternalValuesFromValue();
604 private static void ValueIndexChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
606 ChoiceEditor choice = d as ChoiceEditor;
607 if (choice != null && !choice.ShouldIgnoreExternalValueChanges)
609 choice.UpdateInternalValuesFromValueIndex();
610 choice.UpdateValueFromInternalValues();
614 private static void ItemsSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
616 ChoiceEditor choice = d as ChoiceEditor;
619 choice.ItemsSourceChanged();
623 private static void IsEditableChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
625 // Make sure that the internal values are up to date now that we know we are editable or not.
626 ChoiceEditor choice = d as ChoiceEditor;
627 if (choice != null && !choice.ShouldIgnoreExternalValueChanges)
629 choice.UpdateInternalValuesFromValue();
633 private static void IsNinchedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
635 ChoiceEditor choice = d as ChoiceEditor;
636 if (choice != null && !choice.ShouldIgnoreExternalValueChanges)
638 choice.UpdateInternalValuesFromValue();
642 private static object CoerceShowFullControl(DependencyObject target, object value)
644 ChoiceEditor choice = target as ChoiceEditor;
645 if (choice != null && value is bool)
647 bool boolValue = (bool)value;
649 // ###################################################
650 // CIDER-SPECIFIC CHANGE IN NEED OF PORTING - BEGIN
651 // ###################################################
653 choice.isShowingFullEditor = boolValue;
654 choice.CheckUpdateValueIndex(false);
656 // ###################################################
657 // CIDER-SPECIFIC CHANGE IN NEED OF PORTING - END
658 // ###################################################
660 // If we are text editing force the full control to stay showing.
663 return choice.isTextEditing;
670 private static void ItemTemplateChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
672 ChoiceEditor choice = d as ChoiceEditor;
675 choice.CoerceValue(ChoiceEditor.UseItemTemplateForSelectionProperty);
679 private static void ItemTemplateSelectorChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
681 ChoiceEditor choice = d as ChoiceEditor;
684 choice.CoerceValue(ChoiceEditor.UseItemTemplateForSelectionProperty);
688 private static object CoerceUseItemTemplateForSelection(DependencyObject target, object value)
690 ChoiceEditor choice = target as ChoiceEditor;
695 return choice.ItemTemplate != null || choice.ItemTemplateSelector != null;
701 private void SendPropertyChanged(string propertyName)
703 if (this.PropertyChanged != null)
705 this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
709 protected override void OnPreviewGotKeyboardFocus(KeyboardFocusChangedEventArgs e)
711 base.OnPreviewGotKeyboardFocus(e);
712 FrameworkElement element = e.NewFocus as FrameworkElement;
713 if (element != null && element.Name.Equals("PART_EditableTextBox", StringComparison.Ordinal))
715 this.isTextEditing = true;
719 // ###################################################
720 // CIDER-SPECIFIC CHANGE IN NEED OF PORTING - BEGIN
721 // ###################################################
722 // CIDER needs to override the Keydown behavior :
724 // * The problem here is just a difference in expectation for control behavior
725 // * between the two products (CIDER AND BLEND). The fix is entirely in OnPreviewKeyDown
726 // * a. Remove the condition that we handle navigation keys only when the popup is open
727 // * b. Add logic that forces a commit when a navigation happens (you could optionally only do the commit when the popup is closed, but I�m not sure what the desired behavior is here). You may or may not want to limit this behavior to editable ChoiceEditors as well.//
729 protected override void OnPreviewKeyDown(KeyEventArgs e)
731 bool commitChange = false;
732 bool finishEditing = false;
734 bool markHandled = ValueEditorUtils.GetHandlesCommitKeys(this);
736 if (e.Key == Key.Return || e.Key == Key.Enter)
738 e.Handled |= markHandled;
742 finishEditing = (e.KeyboardDevice.Modifiers & ModifierKeys.Shift) == 0;
743 //Hide dropdown on excape key
746 else if (e.Key == Key.Escape)
748 e.Handled |= markHandled;
750 LostFocusAction savedAction = this.lostFocusAction;
751 this.lostFocusAction = LostFocusAction.None;
752 if (savedAction != LostFocusAction.None)
757 //Hide dropdown on excape key
759 this.OnFinishEditing();
761 // Only pay attention to navigation keys if we are selecting a value from the popup
762 if (this.CollectionView != null && !this.CollectionView.IsEmpty)
764 bool navigated = false;
765 if (e.Key == Key.Up || (!this.IsEditable && e.Key == Key.Left))
767 this.SelectPreviousValue();
770 else if (e.Key == Key.Down || (!this.IsEditable && e.Key == Key.Right))
772 this.SelectNextValue();
775 else if (!this.IsEditable && e.Key == Key.Home)
780 else if (!this.IsEditable && e.Key == Key.End)
782 this.ValueIndex = this.CollectionView.Count - 1;
788 this.lostFocusAction = LostFocusAction.Commit;
790 ComboBox comboBox = this.Template.FindName("_comboChoiceEditor", this) as ComboBox;
791 if (!comboBox.IsDropDownOpen)
800 LostFocusAction savedAction = this.lostFocusAction;
801 this.lostFocusAction = LostFocusAction.None;
802 if (savedAction == LostFocusAction.Commit)
809 this.OnFinishEditing();
811 base.OnPreviewKeyDown(e);
814 // ###################################################
815 // CIDER-SPECIFIC CHANGE IN NEED OF PORTING - END
816 // ###################################################
818 // ###################################################
819 // CIDER-SPECIFIC CHANGE IN NEED OF PORTING - BEGIN
820 // ###################################################
821 private void HideDropDown()
823 ComboBox comboBox = this.Template.FindName("_comboChoiceEditor", this) as ComboBox;
824 if (comboBox != null && comboBox.IsDropDownOpen)
826 comboBox.IsDropDownOpen = false;
830 // When the focus is lost by clicking ouside the choice-editor
831 // we dont get a preview message.
832 // The string edtior has OnLostKeyboardFocus and hence we imitate the
834 protected override void OnLostKeyboardFocus(KeyboardFocusChangedEventArgs e)
836 base.OnLostKeyboardFocus(e);
838 FrameworkElement element = e.OldFocus as FrameworkElement;
839 if (element != null && element.Name.Equals("PART_EditableTextBox", StringComparison.Ordinal))
841 this.HandleLostFocus();
844 // #################################################
845 // CIDER-SPECIFIC CHANGE IN NEED OF PORTING - END
846 // #################################################
849 protected override void OnPreviewLostKeyboardFocus(KeyboardFocusChangedEventArgs e)
851 base.OnPreviewLostKeyboardFocus(e);
852 FrameworkElement element = e.OldFocus as FrameworkElement;
853 if (element != null && element.Name.Equals("PART_EditableTextBox", StringComparison.Ordinal))
855 this.HandleLostFocus();
859 private void HandleLostFocus()
861 if (this.isTextEditing)
863 LostFocusAction oldLostFocusAction = this.lostFocusAction;
864 this.lostFocusAction = LostFocusAction.None;
865 this.isTextEditing = false;
868 if (oldLostFocusAction == LostFocusAction.Commit)
872 else if (oldLostFocusAction == LostFocusAction.Cancel)
877 this.CoerceValue(ChoiceEditor.ShowFullControlProperty);
881 protected override void OnRender(DrawingContext drawingContext)
883 base.OnRender(drawingContext);
884 if (this.ViewType == ChoiceEditorViewType.Combo && !this.ShowFullControl)
886 double borderCornerRadius = this.BorderCornerRadius;
887 Thickness dropButtonInset = this.DropButtonInset;
888 Thickness textAreaInset = this.TextAreaInset;
889 Brush dropButtonBrush = this.DropButtonBrush;
890 double innerCornerRadius = this.InnerCornerRadius;
891 ImageSource buttonIcon = this.ButtonIcon;
892 double iconWidth = this.IconWidth;
893 double iconHeight = this.IconHeight;
895 // Draw something that looks like an Expression combo
896 Rect fullRect = new Rect(0d, 0d, this.ActualWidth, this.ActualHeight);
898 if (RenderUtils.DrawInscribedRoundedRect(drawingContext, this.BorderBrush, null, fullRect, borderCornerRadius))
900 Rect innerRect = RenderUtils.CalculateInnerRect(fullRect, 0);
901 double dropButtonLeft = (innerRect.Right > textAreaInset.Right ? innerRect.Right - textAreaInset.Right : 0d) + dropButtonInset.Left;
902 double dropButtonTop = innerRect.Top + dropButtonInset.Top;
903 double dropButtonRight = innerRect.Right - dropButtonInset.Right;
904 double dropButtonBottom = innerRect.Bottom - dropButtonInset.Bottom;
906 RenderUtils.DrawInscribedRoundedRect(drawingContext, dropButtonBrush, null, new Rect(new Point(dropButtonLeft, dropButtonTop), new Point(dropButtonRight, dropButtonBottom)), innerCornerRadius);
907 if (buttonIcon != null)
909 double buttonCenterX = dropButtonLeft + (dropButtonRight - dropButtonLeft) / 2d;
910 double buttonCenterY = dropButtonTop + (dropButtonBottom - dropButtonTop) / 2d;
911 drawingContext.DrawImage(buttonIcon, new Rect(new Point(buttonCenterX - iconWidth / 2d, buttonCenterY - iconHeight / 2d), new Point(buttonCenterX + iconWidth / 2d, buttonCenterY + iconHeight / 2d)));
914 double textAreaLeft = innerRect.Left + textAreaInset.Left;
915 double textAreaTop = innerRect.Top + textAreaInset.Top;
916 double textAreaRight = innerRect.Right > textAreaInset.Right ? innerRect.Right - textAreaInset.Right : textAreaLeft;
917 double textAreaBottom = innerRect.Bottom - textAreaInset.Bottom;
919 RenderUtils.DrawInscribedRoundedRect(drawingContext, this.Background, null, new Rect(new Point(textAreaLeft, textAreaTop), new Point(textAreaRight, textAreaBottom)), innerCornerRadius);
924 protected override void OnTemplateChanged(ControlTemplate oldTemplate, ControlTemplate newTemplate)
926 this.BeginNoCommitInternalValueChangeBlock();
927 // WORKAROUND Turn off bindings on the internal combo while the template is udpating. This works around Avalon bug: 1756023
928 this.ForceBinding = false;
929 base.OnTemplateChanged(oldTemplate, newTemplate);
932 public override void OnApplyTemplate()
934 base.OnApplyTemplate();
935 // WORKAROUND Force the bindings on our internal combo (if there is one) to update. This works around Avalon bug: 1756023
936 this.ForceBinding = true;
938 this.EndNoCommitInternalValueChangeBlock();
940 this.HandleLostFocus();
943 private void CommitChange()
945 ValueEditorUtils.ExecuteCommand(this.BeginCommand, this, null);
947 if (this.UpdateValueFromInternalValues())
949 // We need to update both the binding on the value property and the valueindex property
950 // so that the value does not bounce around when we commit.
951 ValueEditorUtils.UpdateBinding(this, ChoiceEditor.ValueProperty, UpdateBindingType.Source);
952 ValueEditorUtils.UpdateBinding(this, ChoiceEditor.ValueIndexProperty, UpdateBindingType.Source);
953 ValueEditorUtils.ExecuteCommand(this.CommitCommand, this, null);
954 ValueEditorUtils.UpdateBinding(this, ChoiceEditor.ValueProperty, UpdateBindingType.Target);
955 ValueEditorUtils.UpdateBinding(this, ChoiceEditor.ValueIndexProperty, UpdateBindingType.Target);
959 this.CancelStartedChange();
962 this.lostFocusAction = LostFocusAction.None;
965 private void CancelChange()
967 ValueEditorUtils.ExecuteCommand(this.BeginCommand, this, null);
968 this.CancelStartedChange();
971 private void CancelStartedChange()
973 // Revert External values
974 ValueEditorUtils.UpdateBinding(this, ChoiceEditor.ValueProperty, UpdateBindingType.Target);
975 ValueEditorUtils.UpdateBinding(this, ChoiceEditor.ValueIndexProperty, UpdateBindingType.Target);
976 this.UpdateInternalValuesFromValue();
977 ValueEditorUtils.ExecuteCommand(this.CancelCommand, this, null);
980 private void OnFinishEditing()
982 ICommand finishedEditingCommand = this.FinishEditingCommand;
983 if (finishedEditingCommand != null)
985 ValueEditorUtils.ExecuteCommand(finishedEditingCommand, this, null);
989 Keyboard.Focus(null);
993 private void ItemsSourceChanged()
995 // The collection just changed, so we need to make sure that things are in [....]
997 // ###################################################
998 // CIDER-SPECIFIC CHANGE IN NEED OF PORTING - BEGIN
999 // ###################################################
1000 this.collectionView = null;
1001 // ###################################################
1002 // CIDER-SPECIFIC CHANGE IN NEED OF PORTING - end
1003 // ###################################################
1005 this.UpdateInternalValuesFromValue();
1006 this.UpdateValueFromInternalValues();
1009 private int IndexOf(object item)
1011 if (this.CollectionView != null)
1013 return this.CollectionView.IndexOf(item);
1021 private object GetItemAt(int index)
1023 if (this.CollectionView != null)
1025 return this.CollectionView.GetItemAt(index);
1033 protected void UpdateInternalValuesFromValue()
1035 this.BeginNoCommitInternalValueChangeBlock();
1036 this.BeginNoCommitInternalStringValueChangeBlock();
1039 if (!this.IsNinched)
1041 this.InternalValue = this.Value;
1045 this.InternalValue = null;
1048 if (this.IsEditable)
1050 string newStringValue = String.Empty;
1051 if (this.InternalValue != null && !this.IsNinched)
1053 TypeConverter converter = this.Converter;
1054 if (converter != null && converter.CanConvertFrom(this.InternalValue.GetType()))
1056 newStringValue = converter.ConvertToString(this.InternalValue);
1060 newStringValue = this.InternalValue.ToString();
1064 this.InternalStringValue = newStringValue;
1069 this.EndNoCommitInternalStringValueChangeBlock();
1070 this.EndNoCommitInternalValueChangeBlock();
1074 public void UpdateInternalValuesFromValueIndex()
1076 this.BeginNoCommitInternalValueChangeBlock();
1077 this.BeginNoCommitInternalStringValueChangeBlock();
1080 this.InternalValue = this.GetItemAt(this.ValueIndex);
1084 this.EndNoCommitInternalStringValueChangeBlock();
1085 this.EndNoCommitInternalValueChangeBlock();
1089 [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Propagating the error might cause VS to crash")]
1090 [SuppressMessage("Reliability", "Reliability108", Justification = "Propagating the error might cause VS to crash")]
1091 protected bool UpdateValueFromInternalValues()
1093 this.BeginIgnoreExternalValueChangeBlock();
1096 if (!this.IsEditable)
1098 this.Value = this.InternalValue;
1102 if (this.InternalValue != null)
1104 this.Value = this.InternalValue;
1108 string stringValue = this.InternalStringValue;
1109 // Try to parse a new value from the string value
1110 if (stringValue != null)
1112 TypeConverter converter = this.Converter;
1113 object newValue = null;
1114 if (converter != null)
1118 newValue = converter.ConvertFromString(stringValue);
1122 // Have to catch Exception here because Various of these converters (e.g. DoubleConverter) will throw it.
1123 // Do nothing since newValue should still be null;
1129 newValue = stringValue;
1132 this.Value = newValue;
1134 // At this point it is possible that the value that we just set is out of [....] with the internal value
1135 if (newValue != this.InternalValue)
1137 this.UpdateInternalValuesFromValue();
1146 this.EndIgnoreExternalValueChangeBlock();
1149 CheckUpdateValueIndex(true);
1153 protected void BeginNoCommitInternalValueChangeBlock()
1155 this.internalChangeLockCount++;
1158 // ###################################################
1159 // CIDER-SPECIFIC CHANGE IN NEED OF PORTING - BEGIN
1160 // ###################################################
1163 // Updates the value index for the current value, but
1164 // only if necessary.
1166 private void CheckUpdateValueIndex(bool sourceChanged)
1169 BeginIgnoreExternalValueChangeBlock();
1172 // Check if we need to update the value index.
1173 // We don't need to if we're a combo box and
1174 // we're not showing the drop-down.
1176 if (ViewType != ChoiceEditorViewType.Combo || this.isShowingFullEditor)
1178 if (sourceChanged || ReadLocalValue(ValueIndexProperty) == DependencyProperty.UnsetValue)
1180 ValueIndex = IndexOf(Value);
1185 ClearValue(ValueIndexProperty);
1190 EndIgnoreExternalValueChangeBlock();
1194 // ###################################################
1195 // CIDER-SPECIFIC CHANGE IN NEED OF PORTING - END
1196 // ###################################################
1198 protected void EndNoCommitInternalValueChangeBlock()
1200 this.internalChangeLockCount--;
1201 Fx.Assert(this.internalChangeLockCount >= 0, "internalChangeLockCount should be positive");
1204 protected void BeginNoCommitInternalStringValueChangeBlock()
1206 this.internalStringValueChangeLockCount++;
1209 protected void EndNoCommitInternalStringValueChangeBlock()
1211 this.internalStringValueChangeLockCount--;
1212 Fx.Assert(this.internalStringValueChangeLockCount >= 0, "internalStringValueChangeLockCount should be positive");
1215 protected void BeginIgnoreExternalValueChangeBlock()
1217 Fx.Assert(this.ignoreValueChanges == false, "ignoreValueChanges should be false");
1218 this.ignoreValueChanges = true;
1221 protected void EndIgnoreExternalValueChangeBlock()
1223 Fx.Assert(this.ignoreValueChanges == true, "ignoreValueChanges should be false");
1224 this.ignoreValueChanges = false;
1228 enum LostFocusAction