context: //---------------------------------------------------------------------------
  //
  // Copyright (C) Microsoft Corporation.  All rights reserved.
  //
  //---------------------------------------------------------------------------
  
  using System;
  using System.Collections;
  using System.ComponentModel;
  using System.Diagnostics;
  using System.Windows;
  using System.Windows.Controls;
  using System.Windows.Data;
  using System.Windows.Input;
  
  namespace Microsoft.Windows.Controls
  {
      /// <summary>
      ///     A column that displays a drop-down list while in edit mode.
      /// </summary>
      public class DataGridComboBoxColumn : DataGridColumn
      {
          #region Constructors
  
          static DataGridComboBoxColumn()
          {
              SortMemberPathProperty.OverrideMetadata(typeof(DataGridComboBoxColumn), new FrameworkPropertyMetadata(null, OnCoerceSortMemberPath));
          }
  
          #endregion
  
          #region Helper Type for Styling
  
          internal class TextBlockComboBox : ComboBox
          {
              static TextBlockComboBox()
              {
                  DefaultStyleKeyProperty.OverrideMetadata(typeof(TextBlockComboBox), new FrameworkPropertyMetadata(typeof(TextBlockComboBox)));
                  KeyboardNavigation.IsTabStopProperty.OverrideMetadata(typeof(TextBlockComboBox), new FrameworkPropertyMetadata(false));
                  DataContextProperty.OverrideMetadata(typeof(TextBlockComboBox), new FrameworkPropertyMetadata(OnDataContextPropertyChanged));
              }
  
              /// <summary>
              ///     Property changed callback for DataContext property
              /// </summary>
              private static void OnDataContextPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
              {
                  // Selector has a bug regarding DataContext change and SelectedItem property,
                  // where if the SelectedItem due to old DataContext is a valid item in ItemsSource
                  // but the SelectedItem due to new DataContext is not a valid item in ItemsSource,
                  // the SelectedIndex remains that of old context instead of changing to -1.
                  // This method is a workaround to that problem, since it is of high impact to DataGrid.
                  TextBlockComboBox combo = (TextBlockComboBox)d;
                  bool isLocalValue = (DependencyPropertyHelper.GetValueSource(combo, SelectedItemProperty).BaseValueSource == BaseValueSource.Local);
  
                  if (isLocalValue)
                  {
                      // Clear the selection and re-apply the binding.
                      BindingBase binding = BindingOperations.GetBindingBase(combo, SelectedItemProperty);
                      if (binding != null)
                      {
                          combo.ClearValue(SelectedItemProperty);
                          DataGridComboBoxColumn.ApplyBinding(binding, combo, SelectedItemProperty);
                      }
                  }
                  else
                  {
                      // Clear the selection by setting the local value
                      // and re-evaluate the property by clearing the local value.
                      combo.SelectedItem = null;
                      combo.ClearValue(SelectedItemProperty);
                  }
              }
          }
  
          #endregion
  
          #region Binding
  
          private static object OnCoerceSortMemberPath(DependencyObject d, object baseValue)
          {
              var column = (DataGridComboBoxColumn)d;
              var sortMemberPath = (string)baseValue;
  
              if (string.IsNullOrEmpty(sortMemberPath))
              {
                  sortMemberPath = DataGridHelper.GetPathFromBinding(column.EffectiveBinding as Binding);
              }
  
              return sortMemberPath;
          }
  
          /// <summary>
          ///     Chooses either SelectedItemBinding, TextBinding, SelectedValueBinding or  based which are set.
          /// </summary>
          private BindingBase EffectiveBinding
          {
              get
              {
                  if (SelectedItemBinding != null)
                  {
                      return SelectedItemBinding;
                  }
                  else if (SelectedValueBinding != null)
                  {
                      return SelectedValueBinding;
                  }
                  else
                  {
                      return TextBinding;
                  }
              }
          }
  
          /// <summary>
          ///     The binding that will be applied to the SelectedValue property of the ComboBox.  This works in conjunction with SelectedValuePath  
          /// </summary>
          /// <remarks>
          ///     This isn't a DP because if it were getting the value would evaluate the binding.
          /// </remarks>
          public virtual BindingBase SelectedValueBinding
          {
              get
              {
                  if (!_selectedValueBindingEnsured)
                  {
                      if (!IsReadOnly)
                      {
                          DataGridHelper.EnsureTwoWayIfNotOneWay(_selectedValueBinding);
                      }
  
                      _selectedValueBindingEnsured = true;
                  }
  
                  return _selectedValueBinding;
              }
  
              set
              {
                  if (_selectedValueBinding != value)
                  {
                      BindingBase oldBinding = _selectedValueBinding;
                      _selectedValueBinding = value;
                      CoerceValue(IsReadOnlyProperty);
                      CoerceValue(SortMemberPathProperty);
                      _selectedValueBindingEnsured = false;
                      OnSelectedValueBindingChanged(oldBinding, _selectedValueBinding);
                  }
              }
          }
  
          protected override bool OnCoerceIsReadOnly(bool baseValue)
          {
              if (DataGridHelper.IsOneWay(EffectiveBinding))
              {
                  return true;
              }
  
              // only call the base if we dont want to force IsReadOnly true
              return base.OnCoerceIsReadOnly(baseValue);
          }
  
  
          /// <summary>
          ///     The binding that will be applied to the SelectedItem property of the ComboBoxValue.
          /// </summary>
          /// <remarks>
          ///     This isn't a DP because if it were getting the value would evaluate the binding.
          /// </remarks>
          public virtual BindingBase SelectedItemBinding
          {
              get
              {
                  if (!_selectedItemBindingEnsured)
                  {
                      if (!IsReadOnly)
                      {
                          DataGridHelper.EnsureTwoWayIfNotOneWay(_selectedItemBinding);
                      }
  
                      _selectedItemBindingEnsured = true;
                  }
  
                  return _selectedItemBinding;
              }
  
              set
              {
                  if (_selectedItemBinding != value)
                  {
                      BindingBase oldBinding = _selectedItemBinding;
                      _selectedItemBinding = value;
                      CoerceValue(IsReadOnlyProperty);
                      CoerceValue(SortMemberPathProperty);
                      _selectedItemBindingEnsured = false;
                      OnSelectedItemBindingChanged(oldBinding, _selectedItemBinding);
                  }
              }
          }
  
          /// <summary>
          ///     The binding that will be applied to the Text property of the ComboBoxValue.
          /// </summary>
          /// <remarks>
          ///     This isn't a DP because if it were getting the value would evaluate the binding.
          /// </remarks>
          public virtual BindingBase TextBinding
          {
              get
              {
                  if (!_textBindingEnsured)
                  {
                      if (!IsReadOnly)
                      {
                          DataGridHelper.EnsureTwoWayIfNotOneWay(_textBinding);
                      }
  
                      _textBindingEnsured = true;
                  }
  
                  return _textBinding;
              }
  
              set
              {
                  if (_textBinding != value)
                  {
                      BindingBase oldBinding = _textBinding;
                      _textBinding = value;
                      CoerceValue(IsReadOnlyProperty);
                      CoerceValue(SortMemberPathProperty);
                      _textBindingEnsured = false;
                      OnTextBindingChanged(oldBinding, _textBinding);
                  }
              }
          }
  
          /// <summary>
          ///     Called when SelectedValueBinding changes.
          /// </summary>
          /// <param name="oldBinding">The old binding.</param>
          /// <param name="newBinding">The new binding.</param>
          protected virtual void OnSelectedValueBindingChanged(BindingBase oldBinding, BindingBase newBinding)
          {
              NotifyPropertyChanged("SelectedValueBinding");
          }
  
          /// <summary>
          ///     Called when SelectedItemBinding changes.
          /// </summary>
          /// <param name="oldBinding">The old binding.</param>
          /// <param name="newBinding">The new binding.</param>
          protected virtual void OnSelectedItemBindingChanged(BindingBase oldBinding, BindingBase newBinding)
          {
              NotifyPropertyChanged("SelectedItemBinding");
          }
  
          /// <summary>
          ///     Called when TextBinding changes.
          /// </summary>
          /// <param name="oldBinding">The old binding.</param>
          /// <param name="newBinding">The new binding.</param>
          protected virtual void OnTextBindingChanged(BindingBase oldBinding, BindingBase newBinding)
          {
              NotifyPropertyChanged("TextBinding");
          }
  
          #endregion
  
          #region Styling
  
          /// <summary>
          ///     The default value of the ElementStyle property.
          ///     This value can be used as the BasedOn for new styles.
          /// </summary>
          public static Style DefaultElementStyle
          {
              get
              {
                  if (_defaultElementStyle == null)
                  {
                      Style style = new Style(typeof(ComboBox));
  
                      // Set IsSynchronizedWithCurrentItem to false by default
                      style.Setters.Add(new Setter(ComboBox.IsSynchronizedWithCurrentItemProperty, false));
  
                      style.Seal();
                      _defaultElementStyle = style;
                  }
  
                  return _defaultElementStyle;
              }
          }
  
          /// <summary>
          ///     The default value of the EditingElementStyle property.
          ///     This value can be used as the BasedOn for new styles.
          /// </summary>
          public static Style DefaultEditingElementStyle
          {
              get
              {
                  // return the same as that of DefaultElementStyle
                  return DefaultElementStyle;
              }
          }
  
          /// <summary>
          ///     A style that is applied to the generated element when not editing.
          ///     The TargetType of the style depends on the derived column class.
          /// </summary>
          public Style ElementStyle
          {
              get { return (Style)GetValue(ElementStyleProperty); }
              set { SetValue(ElementStyleProperty, value); }
          }
  
          /// <summary>
          ///     The DependencyProperty for the ElementStyle property.
          /// </summary>
          public static readonly DependencyProperty ElementStyleProperty =
              DataGridBoundColumn.ElementStyleProperty.AddOwner(typeof(DataGridComboBoxColumn), new FrameworkPropertyMetadata(DefaultElementStyle));
  
          /// <summary>
          ///     A style that is applied to the generated element when editing.
          ///     The TargetType of the style depends on the derived column class.
          /// </summary>
          public Style EditingElementStyle
          {
              get { return (Style)GetValue(EditingElementStyleProperty); }
              set { SetValue(EditingElementStyleProperty, value); }
          }
  
          /// <summary>
          ///     The DependencyProperty for the EditingElementStyle property.
          /// </summary>
          public static readonly DependencyProperty EditingElementStyleProperty =
              DataGridBoundColumn.EditingElementStyleProperty.AddOwner(typeof(DataGridComboBoxColumn), new FrameworkPropertyMetadata(DefaultEditingElementStyle));
  
          /// <summary>
          ///     Assigns the ElementStyle to the desired property on the given element.
          /// </summary>
          private void ApplyStyle(bool isEditing, bool defaultToElementStyle, FrameworkElement element)
          {
              Style style = PickStyle(isEditing, defaultToElementStyle);
              if (style != null)
              {
                  element.Style = style;
              }
          }
  
          /// <summary>
          ///     Assigns the ElementStyle to the desired property on the given element.
          /// </summary>
          internal void ApplyStyle(bool isEditing, bool defaultToElementStyle, FrameworkContentElement element)
          {
              Style style = PickStyle(isEditing, defaultToElementStyle);
              if (style != null)
              {
                  element.Style = style;
              }
          }
  
          private Style PickStyle(bool isEditing, bool defaultToElementStyle)
          {
              Style style = isEditing ? EditingElementStyle : ElementStyle;
              if (isEditing && defaultToElementStyle && (style == null))
              {
                  style = ElementStyle;
              }
  
              return style;
          }
  
          /// <summary>
          ///     Assigns the Binding to the desired property on the target object.
          /// </summary>
          private static void ApplyBinding(BindingBase binding, DependencyObject target, DependencyProperty property)
          {
              if (binding != null)
              {
                  BindingOperations.SetBinding(target, property, binding);
              }
              else
              {
                  BindingOperations.ClearBinding(target, property);
              }
          }
  
          #endregion
  
          #region Clipboard Copy/Paste
  
          /// <summary>
          /// If base ClipboardContentBinding is not set we use Binding.
          /// </summary>
          public override BindingBase ClipboardContentBinding
          {
              get
              {
                  return base.ClipboardContentBinding ?? EffectiveBinding;
              }
  
              set
              {
                  base.ClipboardContentBinding = value;
              }
          }
  
          #endregion
  
          #region ComboBox Column Properties
  
          /// <summary>
          ///     The ComboBox will attach to this ItemsSource.
          /// </summary>
          public IEnumerable ItemsSource
          {
              get { return (IEnumerable)GetValue(ItemsSourceProperty); }
              set { SetValue(ItemsSourceProperty, value); }
          }
  
          /// <summary>
          ///     The DependencyProperty for ItemsSource.
          /// </summary>
          public static readonly DependencyProperty ItemsSourceProperty =
              ComboBox.ItemsSourceProperty.AddOwner(typeof(DataGridComboBoxColumn), new FrameworkPropertyMetadata(null, DataGridColumn.NotifyPropertyChangeForRefreshContent));
  
          /// <summary>
          ///     DisplayMemberPath is a simple way to define a default template
          ///     that describes how to convert Items into UI elements by using
          ///     the specified path.
          /// </summary>
          public string DisplayMemberPath
          {
  
gt:             get { return (string)GetValue(DisplayMemberPathProperty); }